0

I am new to programming and I am trying to make a calculator by using html and js. I want the calculator to look like a real-life calculator so when I click the number button, the number will show in the block. I have made this function, but I can't clear the number shown on block by clicking "AC" button. What should I change to make the clear function work.

part of html code

<tr><td colspan="5" id="result" height="40"></td></tr>

<tr>
    <td id="seven" class="number" onclick = "output()">7</td>
    <td id="eight" class="number" onclick = "output()">8</td>
    <td id="nine" class="number" onclick = "output()">9</td>
    <td id="C" >C</td>
    <td id="AC" onclick = "clear()">AC</td>
</tr>

js code

let result = document.getElementById("result");

function output() {
    result.innerHTML =  result.innerHTML + event.target.innerHTML;
 };  

function clear() {
    result.innerHTML = "";
 };

I tried the clear function to clear the innerhtml but it didn't work. Should I change the code or there is a problem of the output function. How to use the clear fucntion to clear the output displayed.

Stack Snippet

let result = document.getElementById("result");

function output() {
    result.innerHTML =  result.innerHTML + event.target.innerHTML;
 };  

function clear() {
    result.innerHTML = "";
 };
<table>
<tr><td colspan="5" id="result" height="40"></td></tr>

<tr>
    <td id="seven" class="number" onclick = "output()">7</td>
    <td id="eight" class="number" onclick = "output()">8</td>
    <td id="nine" class="number" onclick = "output()">9</td>
    <td id="C" >C</td>
    <td id="AC" onclick = "clear()">AC</td>
</tr>
</table>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • You should use more specific HTML tags like `input` , `button` etc. And you should not rely heavily on tables for layout purposes instead you can learn CSS for styling the layout of your webpage. – ShivCK Apr 02 '22 at 11:27

4 Answers4

2

This is one of the several reasons not to use onxyz-attribute-style event handlers. They can only call global functions, and the global namespace is very crowded and prone to naming conflicts. They also run in a complicated scope that incorporates not only the global environment, but also all of the properties and methods on the element the onxyz attribute is on and all of its parent elements. That's what's happening to you: You're running into a situation where clear exists in that special scope because it exists on the element or its parent(s). (Specifically, it's on HTMLDocument; see the bottom of the answer.)

Instead, use addEventListener. It lets you use non-globals, and reliably attaches the precise function you give it. For instance, to do that with minimal changes to what you have:

let result = document.getElementById("result");
for (const id of ["seven", "eight", "nine"]) {
    document.getElementById(id).addEventListener("click", output);
}
document.getElementById("AC").addEventListener("click", clear);

function output() {
    result.innerHTML =  result.innerHTML + event.target.innerHTML;
} // <== Side note: No `;` here, this is a function *declration*

function clear() {
    result.innerHTML = "";
} // <== Same

Live Example:

let result = document.getElementById("result");
for (const id of ["seven", "eight", "nine"]) {
    document.getElementById(id).addEventListener("click", output);
}
document.getElementById("AC").addEventListener("click", clear);

function output() {
    result.innerHTML =  result.innerHTML + event.target.innerHTML;
}

function clear() {
    result.innerHTML = "";
}
<table>
<tr><td colspan="5" id="result" height="40"></td></tr>

<tr>
    <td id="seven" class="number">7</td>
    <td id="eight" class="number">8</td>
    <td id="nine" class="number">9</td>
    <td id="C" >C</td>
    <td id="AC">AC</td>
</tr>
</table>

(In modern coding, you'd probably have that in a module rather than a script, so that it isn't at global scope.)

But, ideally we wouldn't have to give each and every element an id like that and refer to them specifically. We could, for instance, use the number class you have on the digits:

for (const element of document.querySelectorAll(".number")) {
    element.addEventListener("click", output);
}
document.getElementById("AC").addEventListener("click", clear);

Live Example:

let result = document.getElementById("result");
for (const element of document.querySelectorAll(".number")) {
    element.addEventListener("click", output);
}
document.getElementById("AC").addEventListener("click", clear);

function output() {
    result.innerHTML =  result.innerHTML + event.target.innerHTML;
}

function clear() {
    result.innerHTML = "";
}
<table>
<tr><td colspan="5" id="result" height="40"></td></tr>

<tr>
    <td class="number">7</td>
    <td class="number">8</td>
    <td class="number">9</td>
    <td id="C" >C</td>
    <td id="AC">AC</td>
</tr>
</table>

So where did clear come from? It's on HTMLDocument. Your code is running in a contents that looks like this (there may be other layers as well):

with (document) {
    with (/* the `html` element*/) {
        with (/* the `body` element*/) {
            with (/* the `table` element for your table*/) {
                with (/* the [implied] `tbody` element for your table*/) {
                    with (/* the `tr` element for your table row*/) {
                        with (/* the `td` element for your AC cell*/) {
                            clear(); // <== Your code
                        }
                    }
                }
            }
        }
    }
}

So clear is found on HTMLDocument, using that instead of the clear you've declared at global scope.

The lack of clarity around which clear is used is also the primary reason why the with statement is effectively deprecated. (It's disallowed in strict mode, for instance, and all new code should be written using strict mode.) :-)

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
0

Just change the clear function name that will work

let result = document.getElementById("result");

function output() {
   result.innerHTML =  result.innerHTML + event.target.innerHTML;
}

function clearResult() {
  document.getElementById("result").innerHTML = "";
}
0

The problem is "clear()" function already used by other thing, so you cannot use it, the fastest way to solve your problem is by changing the function name only. You could check my 1st example for this.

Other way to avoid it is actually just to wrap the function inside other thing, as you said you're still learning, so might as well show you the possibility. You could check this on my 2nd example.

let result = document.getElementById("result");

function calcOutput() {
    result.innerHTML =  result.innerHTML + event.target.innerHTML;
 };  

function calcClear() {
    result.innerHTML = "";
 };
 
 // For Second Example
const calculator = {
  output : () => {
      result.innerHTML =  result.innerHTML + event.target.innerHTML;
   },
  clear : () => {
      result.innerHTML = "";
   }
};
<!-- First Example -->
<table>
  <tbody>
    <tr><td colspan="5" id="result" height="40"></td></tr>
    <tr>
        <td id="seven" class="number" onclick = "calcOutput()">7</td>
        <td id="eight" class="number" onclick = "calcOutput()">8</td>
        <td id="nine" class="number" onclick = "calcOutput()">9</td>
        <td id="C" >C</td>
        <td id="AC" onclick = "calcClear()">AC</td>
    </tr>
  </tbody>
</table>


<!-- Second Example -->
<table>
  <tbody>
    <tr><td colspan="5" id="result2nd" height="40"></td></tr>
    <tr>
        <td id="seven2nd" class="number" onclick = "calculator.output()">7</td>
        <td id="eight2nd" class="number" onclick = "calculator.output()">8</td>
        <td id="nine2nd" class="number" onclick = "calculator.output()">9</td>
        <td id="C2nd" >C</td>
        <td id="AC2nd" onclick = "calculator.clear()">AC</td>
    </tr>
  </tbody>
</table>
0

Since you use an inline event handler, your global function window.clear is shadowed by the obsolete document.clear. In an event handler as HTML attribute, the Document object is in the scope chain before the Window object. You can test it by running console.log(clear === document.clear); in your snippet.

You should try to create your app's scope with using an IIFE and then use Event Listener to address the proper callback for each event.

Nima Bastani
  • 185
  • 11