问题
I want to add keyboard support to my calculator. When i press operations with keyboard (i.e. +,-,* or /) js sees it as number, not as operation.
For example, when I compute "10+11" by clicking, I will get "21" as a result. When I do the same with inputing through keyboard, I will get "10".
Why does it happen? And is it possible to change it?
<!DOCTYPE html>
<html>
<head>
<link href="style.css" type="text/css" rel="stylesheet">
</head>
<body>
<div class="main">
<div id="output">
<input id="display" value="0">
</div>
<button class="clear" id="ce">CE</button>
<button class="clear" id="c">C</button>
<button class="operation" id="slash">/</button>
<button class="number" id="seven">7</button>
<button class="number" id="eight">8</button>
<button class="number" id="nine">9</button>
<button class="operation" id="star">*</button>
<button class="number" id="four">4</button>
<button class="number" id="five">5</button>
<button class="number" id="six">6</button>
<button class="operation" id="minus">-</button>
<button class="number" id="one">1</button>
<button class="number" id="two">2</button>
<button class="number" id="three">3</button>
<button class="operation" id="plus">+</button>
<button class="number" id="zero">0</button>
<button class="decimal" id="dot">.</button>
<button class="operation" id="equal">=</button>
</div>
<script>
let numberBtn=document.querySelectorAll(".number"),
operationBtn=document.querySelectorAll(".operation"),
clearBtn=document.querySelectorAll(".clear"),
decimalBtn=document.querySelector(".decimal"),
currentValue=0,
isNewValue= false,
currentOperation=null,
phrase="ошибка";
display=document.getElementById('display');
for (let i=0; i<operationBtn.length; i++) {
operations=operationBtn[i];
operations.addEventListener ('click', function (e) {
operate (e.target.textContent);
});
}
for (let i=0; i<operationBtn.length; i++) {
op=operationBtn[i];
op.addEventListener ('keypress', function (e) {
operate (e.key);
});
}
for (let i=0; i<numberBtn.length; i++) {
numbers=numberBtn[i];
numbers.addEventListener ('click', function (e) {
numberPress (e.target.textContent);
});
}
for (let i=0; i<numberBtn.length; i++) {
numbers=numberBtn[i];
numbers.addEventListener ('keypress', function (e) {
numberPress (e.key)
});
}
for (let i=0; i<clearBtn.length; i++) {
clears=clearBtn[i];
clears.addEventListener ('click', function (e) {
console.log (e.srcElement.id);
clear(e.srcElement.id);
});
}
function numberPress(numbers) {
if (isNewValue) {
display.value=numbers;
isNewValue=false;
console.log ('1');
} else {
if (display.value==0 && display.value!=='0.') {
display.value=numbers;
console.log ('2');
} else {
display.value+=numbers;
console.log ('3');
}
}
}
function operate(op) {
localOperationMemory=display.value;
if (isNewValue && currentOperation!=="=") {
currentValue=display.value;
console.log ('4');
} else {
isNewValue=true;
if (currentOperation=="+") {
currentValue+=parseFloat (localOperationMemory);
console.log ('5');
} else if (currentOperation=="-") {
currentValue-=parseFloat(localOperationMemory);
console.log ('6');
} else if (currentOperation=="*") {
currentValue*=parseFloat (localOperationMemory);
console.log ('7');
} else if (currentOperation=="/") {
currentValue/=parseFloat (localOperationMemory);
console.log ('8');
} else {
currentValue=parseFloat (localOperationMemory);
console.log ('9');
}
display.value=(Math.round(currentValue*100)/100);
currentOperation=op;
divideZero ()
}
}
</script>
</body>
</html>
回答1:
Give this a shot. It's not got any functionality for delete key or arrow keys, but it will perhaps help move you forward more.
<!DOCTYPE html>
<html>
<head>
</head>
<script>
var values
var dp
function checkCalcKey(key) {
switch (key) {
case '.':
document.getElementById('dot').click();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '+':
case '-':
case '/':
case '*':
case '=':
document.getElementById(key).click();
break;
}
return false;
// TODO handle any of 'ArrowLeft', 'ArrowRight', 'Delete','Backspace'. I'll leave this as an exercise for you to complete!
}
function hardReset() {
values = [];
opcode = null;
clear = true;
reset();
}
function reset() {
newNumber = false;
if (opcode === null) {
clearDisplay()
}
}
function clearDisplay() {
dp = 0;
nc = 0;
document.getElementById('display').value = '';
}
function getValue() {
var value;
var s = '' + document.getElementById('display').value;
if (s === '') {
value = 0;
} else if (s.indexOf('.') === -1) {
value = Number(s);
} else {
value = parseFloat(s);
}
return value;
}
function performMaths() {
m = values.pop();
v = values.pop();
switch (opcode) {
case '+':
v += m;
break;
case '-':
v -= m;
break;
case '*':
v *= m;
break;
case '/':
if ( m=== 0) {
v = NaN;
} else {
v /= m;
}
break;
}
values = [v,m];
document.getElementById('display').value = v;
}
function init() {
clear = true;
hardReset();
document.querySelectorAll(".number").forEach((b) => {
b.addEventListener('click', (e) =>
{
newNumber = true;
if (clear) {
clearDisplay();
clear = false;
}
nc++;
display=document.getElementById('display');
if (dp === 0) {
var value = (10 * Number(display.value)) + Number(e.target.textContent);
display.value = value;
} else {
var f = Number(e.target.textContent) / Math.pow(10,dp);
var value = parseFloat(display.value);
var value = f + value;
var sv = '' + value
sv = sv.substring(0,nc);
display.value = sv;
dp++;
}
});
});
document.getElementById('dot').addEventListener('click', () => {
newNumber = true;
if (dp === 0) {
dp++;
nc++;
}
});
document.getElementById('c').addEventListener('click', () => {
hardReset();
});
document.getElementById('ce').addEventListener('click', () => {
reset();
});
document.querySelectorAll(".operator").forEach((b) => {
b.addEventListener('click', (e) =>
{
clear = true;
if (e.target.textContent != opcode) {
opcode = e.target.textContent;
}
if (document.getElementById('display').value != '') {
if (values.length === 2) {
values.shift();
}
values.unshift(getValue());
}
newNumber = false;
});
});
document.getElementById('=').addEventListener('click', () => {
if (values.length === 2) {
if (newNumber) {
values.pop();
values.push(getValue());
}
} else {
values.push(getValue());
}
performMaths();
newNumber = false;
});
}
</script>
<body onload="init()">
<div class="main">
<div id="output">
<input id="display" value="" onkeydown="return checkCalcKey(event.key)">
</div>
<button id="ce">CE</button>
<button id="c">C</button>
<button id="0" class="number">0</button>
<button id="1" class="number">1</button>
<button id="2" class="number">2</button>
<button id="3" class="number">3</button>
<button id="4" class="number">4</button>
<button id="5" class="number">5</button>
<button id="6" class="number">6</button>
<button id="7" class="number">7</button>
<button id="8" class="number">8</button>
<button id="9" class="number">9</button>
<button id="/" class="operator">/</button>
<button id="*" class="operator">*</button>
<button id="-" class="operator">-</button>
<button id="+" class="operator">+</button>
<button id="=">=</button>
<button id="dot">.</button>
</div>
</body>
</html>
An explanation of the code:
The getValue routine does the bulk of fixing things up, and answers the key points in your original question:
If a variable is being interpreted as of type string it can mess up the computation. So you can convert a variable you don't know the type of to string by combining an empty string with the variable itself.
There is code to check for the existence of a decimal point, then use either parseFloat or Number to cast the type of the variable (parseInt is a more verbose alternative, needing radix of 10 - for a base 10 number).
A couple of other key variables are:
- dp (decimal point). It stands for the number of digits past the decimal point, so any numbers you add after are divided by 10^dp (so 1 digit after dp is n * 1/10th, 2 digits is n * 1/100th etc).
- nc (number of characters). Sometimes you can get odd rounding errors, so things like 1.111 suddenly become 1.111000000001 or something like that, so nc is used to count digits and decimal places entered and take the left number of characters based on nc.
There is also some funky stuff going on, mimicking a normal calculator. So that when computation goes on, you can do something like enter:
- 5 * = to give 25..
- Press = again and it will give you 125.
So the last number you enter becomes like a 'sticky' constant in memory. Whenever a new value gets entered, that becomes the new 'sticky' value. So:
- 3 * 4 = (yields 12),
- = then yields 48 etc.
So: - the values array is used to store both the sticky value and the total:
- It gets re-evaluated in performMaths()
- adjusted in operator and = event handlers.
- the boolean newValue is set to true whenever a number is pressed, and false whenever an operation, such as
+
,-
,/
,*
or=
is pressed.
- the boolean newValue is set to true whenever a number is pressed, and false whenever an operation, such as
checkCalcKey() is an event handler for onkeydown upon the input HTML control's. By returning false
, it prevents the character you type from build input. Instead it defers to the click handler of the corresponding button and any character entry into the input control value property gets performed via that handler instead. (More info here).
回答2:
The problem is this:
<input
id="display"
value=""
onkeydown="return checkCalcKey(event.key)"
/>
function checkCalcKey(key) {
return (
(dp === 0 && key === ".") ||
(key >= "0" && key <= "9") ||
["ArrowLeft", "ArrowRight", "Delete", "Backspace"].filter(
e => e === key
).length === 1
);
}
When we type something in the input, you filter what you allow us to type. And, actually, you don't allow "+" so when you say:
when I compute "10+11" by clicking, I will get "21" as a result. When I do the same with inputing through keyboard, I will get "10"
It's surely about this. Though, when me I type "10+11", the input gets "1011", and when I hit "=" it returns "undefined".
And is it possible to change it?
Lucky you, yes ! What's impossible with JavaScript ? Here are my advices, but the possibilities are enormous!
- Do not listen to keydowns that way.
- When we click on a button, then only check what's inside the input.
- When checking the input you can have fun: letters ? alert the user to not use letters. Weird symbols ? Slap the user. Mathematical symbols ? Translate them so that JavaScript can understand them and compute them.
I made a quickie just for you to have an idea, you can do it I'm sure!
const btn = document.getElementById('my-button');
const checkInput = function() {
// do some magic, returns true if no letters or weird symbols for example
};
btn.addEventListener("click", function() {
const usrInput = document.getElementById('user-input');
const inputIsOk = checkInput(user-input.value);
if (!inputIsOk) return alert('Ho damn user correct yourself!'); // but be polite
// apparently you can do the following magic judging your actual level
// have a fun time with it!
};
来源:https://stackoverflow.com/questions/59504287/problems-with-adding-keyboard-support-to-js-calculator