马黑黑 发表于 2023-12-31 08:50

练手:JS计算器(第一版)

本帖最后由 马黑黑 于 2023-12-31 09:29 编辑 <br /><br /><style>
#ma {
    margin: 50px auto 0;
    padding: 8px;
    width: fit-content;
    max-width: 296px;
    box-sizing: border-box;
    height: fit-content;
    border-radius: 10px;
    background: #666;
    box-shadow: 2px 4px 8px black;
}
#calcMsg, #calcRes {
    padding: 6px;
    color: #eee;
    font-size: 14px;
    word-break: break-all;
}
#calcRes {
    text-align: right;
    color: #000;
    font-size: 20px;
    background: #eee;
}
#calc {
    display: grid;
    place-items: center;
    grid-template-columns: repeat(4, 70px);
    grid-template-rows: repeat(5, 60px);
}
.btn, .clear {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: #ccc;
    box-shadow: inset 0 0 10px #000;
    font: normal 20px/40px sans-serif;
    text-align: center;
    user-select: none;
}
.btn:hover, .clear:hover {
    background: #aaa;
}
.num { color: darkred; font-weight: bold; }
</style>

<div id="ma">
    <div id="calcMsg">计算器</div>
    <div id="calcRes">0</div>
    <div id="calc">
      <div class="clear" id="clear">C</div>
      <div class="btn" data-idx="(">(</div>
      <div class="btn" data-idx=")">)</div>
      <div class="clear" id="del">←</div>
      <div class="btn num" data-idx="7">7</div>
      <div class="btn num" data-idx="8">8</div>
      <div class="btn num" data-idx="9">9</div>
      <div class="btn" data-idx="/">÷</div>
      <div class="btn num" data-idx="4">4</div>
      <div class="btn num" data-idx="5">5</div>
      <div class="btn num" data-idx="6">6</div>
      <div class="btn" data-idx="*">×</div>
      <div class="btn num" data-idx="1">1</div>
      <div class="btn num" data-idx="2">2</div>
      <div class="btn num" data-idx="3">3</div>
      <div class="btn num" data-idx="-">-</div>
      <div class="btn num" data-idx="0">0</div>
      <div class="btn" data-idx=".">.</div>
      <div class="btn" id="percent">%</div>
      <div class="btn" data-idx="+">+</div>
      <div class="clear" id="pow2">x²</div>
      <div class="clear" id="pow3">x³</div>
      <div class="clear" id="sqrt">√</div>
      <div class="clear" id="equal" data-idx="=">=</div>
    </div>
</div>

<script>

let btns = document.querySelectorAll('.btn');

let _eval = (fn) => new Function('return ' + fn)();

btns.forEach(item => {
    item.onclick = () => {
      calcRes.innerText = calcRes.innerText === '0' ? item.dataset.idx : (calcRes.innerText + item.dataset.idx);
      calcMsg.innerText = calcRes.innerText;
    }
});

clear.onclick = () => {
    calcMsg.innerText = '计算器';
    calcRes.innerText = '0';
};

equal.onclick = () => {
    if(calcMsg.innerText.lastIndexOf('=') != -1) return;
    let str = calcRes.innerText;
    let result = _eval(str);
    calcRes.innerText = num2Fixed(result);
    calcMsg.innerText += '=';
   
};

del.onclick = () => {
    calcRes.innerText = calcRes.innerText.slice(0,-1);
    calcMsg.innerText = calcMsg.innerText.slice(0,-1);
};

sqrt.onclick = () => {
    let num = calcRes.innerText;
    calcRes.innerText = Math.sqrt(num);
    calcMsg.innerText = `√(${num})=`;
}

pow2.onclick = () => {
    let num = calcRes.innerText;
    calcRes.innerText = Math.pow(num,2);
    calcMsg.innerText = `${num}\u00b2=`;
}

pow3.onclick = () => {
    let num = calcRes.innerText;
    calcRes.innerText = Math.pow(num,3);
    calcMsg.innerText = `${num}\u00b3=`;
}

percent.onclick = () => {
    let num = calcRes.innerText;
    calcRes.innerText = _eval(num/100);
    calcMsg.innerText = num + '/100=';
};

let num2Fixed = (res) => {
    let str = typeof(res) === 'number' ? res.toString() : res;
    let ar = str.split('.');
    if(ar.length <= 1) return res;
    if(ar.length >= 14) str = str.slice(0,-1);
    return parseFloat(str);
};

</script>

马黑黑 发表于 2023-12-31 08:51

本帖最后由 马黑黑 于 2023-12-31 09:30 编辑

代码
<style>
#ma {
    margin: 50px auto 0;
    padding: 8px;
    width: fit-content;
    max-width: 296px;
    box-sizing: border-box;
    height: fit-content;
    border-radius: 10px;
    background: #666;
    box-shadow: 2px 4px 8px black;
}
#calcMsg, #calcRes {
    padding: 6px;
    color: #eee;
    font-size: 14px;
    word-break: break-all;
}
#calcRes {
    text-align: right;
    color: #000;
    font-size: 20px;
    background: #eee;
}
#calc {
    display: grid;
    place-items: center;
    grid-template-columns: repeat(4, 70px);
    grid-template-rows: repeat(5, 60px);
}
.btn, .clear {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: #ccc;
    box-shadow: inset 0 0 10px #000;
    font: normal 20px/40px sans-serif;
    text-align: center;
    user-select: none;
}
.btn:hover, .clear:hover {
    background: #aaa;
}
.num { color: darkred; font-weight: bold; }
</style>

<div id="ma">
    <div id="calcMsg">计算器</div>
    <div id="calcRes">0</div>
    <div id="calc">
      <div class="clear" id="clear">C</div>
      <div class="btn" data-idx="(">(</div>
      <div class="btn" data-idx=")">)</div>
      <div class="clear" id="del">←</div>
      <div class="btn num" data-idx="7">7</div>
      <div class="btn num" data-idx="8">8</div>
      <div class="btn num" data-idx="9">9</div>
      <div class="btn" data-idx="/">÷</div>
      <div class="btn num" data-idx="4">4</div>
      <div class="btn num" data-idx="5">5</div>
      <div class="btn num" data-idx="6">6</div>
      <div class="btn" data-idx="*">×</div>
      <div class="btn num" data-idx="1">1</div>
      <div class="btn num" data-idx="2">2</div>
      <div class="btn num" data-idx="3">3</div>
      <div class="btn num" data-idx="-">-</div>
      <div class="btn num" data-idx="0">0</div>
      <div class="btn" data-idx=".">.</div>
      <div class="btn" id="percent">%</div>
      <div class="btn" data-idx="+">+</div>
      <div class="clear" id="pow2">x²</div>
      <div class="clear" id="pow3">x³</div>
      <div class="clear" id="sqrt">√</div>
      <div class="clear" id="equal" data-idx="=">=</div>
    </div>
</div>

<script>

let btns = document.querySelectorAll('.btn');

let _eval = (fn) => new Function('return ' + fn)();

btns.forEach(item => {
    item.onclick = () => {
      calcRes.innerText = calcRes.innerText === '0' ? item.dataset.idx : (calcRes.innerText + item.dataset.idx);
      calcMsg.innerText = calcRes.innerText;
    }
});

clear.onclick = () => {
    calcMsg.innerText = '计算器';
    calcRes.innerText = '0';
};

equal.onclick = () => {
    if(calcMsg.innerText.lastIndexOf('=') != -1) return;
    let str = calcRes.innerText;
    let result = _eval(str);
    calcRes.innerText = num2Fixed(result);
    calcMsg.innerText += '=';
   
};

del.onclick = () => {
    calcRes.innerText = calcRes.innerText.slice(0,-1);
    calcMsg.innerText = calcMsg.innerText.slice(0,-1);
};

sqrt.onclick = () => {
    let num = calcRes.innerText;
    calcRes.innerText = Math.sqrt(num);
    calcMsg.innerText = `√(${num})=`;
}

pow2.onclick = () => {
    let num = calcRes.innerText;
    calcRes.innerText = Math.pow(num,2);
    calcMsg.innerText = `${num}\u00b2=`;
}

pow3.onclick = () => {
    let num = calcRes.innerText;
    calcRes.innerText = Math.pow(num,3);
    calcMsg.innerText = `${num}\u00b3=`;
}

percent.onclick = () => {
    let num = calcRes.innerText;
    calcRes.innerText = _eval(num/100);
    calcMsg.innerText = num + '/100=';
};

let num2Fixed = (res) => {
    let str = typeof(res) === 'number' ? res.toString() : res;
    let ar = str.split('.');
    if(ar.length <= 1) return res;
    if(ar.length >= 14) str = str.slice(0,-1);
    return parseFloat(str);
};

</script>

马黑黑 发表于 2023-12-31 08:51

本帖最后由 马黑黑 于 2023-12-31 09:23 编辑

未解决的已知问题:

[*]浮点数运算的精准度。由于JS内部机制中基于浮点数的运算存在bug,我采用了截取方法处理这个问题,当小数点后面的数字大于等于14,去掉最后一位数,没有四舍五入,存在精度准确问题;
[*]特大整数运算未做测试,估计也可能存在问题,原因也是JS数据类型问题导致。不过JS会自动使用科学计数法,绝大多数的特大整数运算应该过得去;
[*]算式合法性检测机制。这个,比如四则运算的括号问题、除以0等问题,未做任何处理;
[*]0.xxx表达式的显示问题:小数点前面的 0 不显示。



作为练手小作品,第一版就酱吧。欢迎各位提出意见与建议,谢谢,顺祝2024新年快乐!

樵歌 发表于 2023-12-31 10:04

真好用,能下载到瘦机上吗?

樵歌 发表于 2023-12-31 10:06

能加上开平方,三角函数吗。

起个网名好难 发表于 2023-12-31 10:12

马黑黑 发表于 2023-12-31 08:51
代码

好制作!

评分后失效需刷新后恢复,或许是个例?

马黑黑 发表于 2023-12-31 10:28

起个网名好难 发表于 2023-12-31 10:12
好制作!

评分后失效需刷新后恢复,或许是个例?

不是。JS代码如果包裹在 (function() { ... })(); 可以处理这个问题

马黑黑 发表于 2023-12-31 10:28

樵歌 发表于 2023-12-31 10:06
能加上开平方,三角函数吗。

开方已经有了,其他三角函数运算是小问题

马黑黑 发表于 2023-12-31 10:30

樵歌 发表于 2023-12-31 10:04
真好用,能下载到瘦机上吗?

手机上的计算器功能要比这个好

起个网名好难 发表于 2023-12-31 10:55

马黑黑 发表于 2023-12-31 10:28
不是。JS代码如果包裹在 (function() { ... })(); 可以处理这个问题

把let替换为var或许也能解决问题。

马黑黑 发表于 2023-12-31 13:12

起个网名好难 发表于 2023-12-31 10:55
把let替换为var或许也能解决问题。

嗯,var有提升变量能力

红影 发表于 2023-12-31 13:50

这个好,不但能加加减减,还有平方和立方以及开平方和百分比,还能回到上一步。
功能还是很全面呢,用代码做出来的,太赞了{:4_199:}

红影 发表于 2023-12-31 13:51

版面设计也漂亮,给黑黑点赞{:4_199:}

起个网名好难 发表于 2023-12-31 15:40

马黑黑 发表于 2023-12-31 13:12
嗯,var有提升变量能力

let 不可以重复申明,只是为什么评分会导致重复申明。

马黑黑 发表于 2024-1-2 08:31

起个网名好难 发表于 2023-12-31 15:40
let 不可以重复申明,只是为什么评分会导致重复申明。

评分后页面的刷新机制是局部刷新,重新加载一部分内容,包括帖子的主帖内容。所以,相应代码如果用立即执行函数包裹,这个问题多数会得到解决。

起个网名好难 发表于 2024-1-2 09:49

本帖最后由 起个网名好难 于 2024-1-2 09:51 编辑

马黑黑 发表于 2024-1-2 08:31
评分后页面的刷新机制是局部刷新,重新加载一部分内容,包括帖子的主帖内容。所以,相应代码如果用立即执 ...
ypPlayer 是不是与 js日历有冲突(见红影帖回家的路)事件处理都不响应了。

马黑黑 发表于 2024-1-2 11:49

起个网名好难 发表于 2024-1-2 09:49
ypPlayer 是不是与 js日历有冲突(见红影帖回家的路)事件处理都不响应了。

这不是冲突问题,而是加载阻塞导致的 onclick 和 onchange 事件失效问题。一般可以考虑使用异步或延时来处理。
页: [1]
查看完整版本: 练手:JS计算器(第一版)