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.) :-)