1

Context: I'm trying to learn some JavaScript and HTML. I've got some arrays and wanted to normalize them so that their entries sum to 1. To do this, I defined a function called normalize (that took an array as its argument). Things were working fine until I tried to incorporate a call to my normalize function as part of a button click in my main .html file. Below is a working dummy example with the array stuff omitted.

<script>
    function normalize () {return 1;};
</script>
    
<input type="button" onclick="console.log(normalize()); //prints undefined when clicked">

<script>
    console.log(normalize()); // prints 1 as intended
</script>

But if I change the name of the function to something other than normalize, then both calls work just fine:

<script>
    function foo () {return 1;};
</script>
    
<input type="button" onclick="console.log(foo()); //prints 1 when clicked">

<script>
    console.log(foo()); // prints 1
</script>

My question is: why is the onclick call behaving differently when the function is named normalize? I would've expected it to throw an error or something, given that onclick seems to be calling some external normalize function on one of my arrays. I did a quick search for other normalize functions, and I did see that there's an HTML DOM normalize method, but I don't see why it's being called (if it is being called!) instead of my intended function. What's going on with the onclick normalize call?

Thanks in advance for any help you can give!

Josh Keneda
  • 111
  • 5
  • 1
    I'm off looking for the duplicate target, but basically this is one of the many reasons not to use that kind of event handler. They execute with a **bunch** of unexpected things in scope that hide your global functions. Use `addEventListener` instead. – T.J. Crowder Jun 03 '21 at 17:41
  • 1
    **All:** There's a well-established dupetarget for this, I'm just having trouble finding it... – T.J. Crowder Jun 03 '21 at 17:43
  • 1
    Does this answer your question? [disappearing google map](https://stackoverflow.com/questions/5127037/disappearing-google-map) In that case they used `write` instead of `normalize`, but same deal. – Heretic Monkey Jun 03 '21 at 17:53
  • @HereticMonkey It does, thanks! – Josh Keneda Jun 04 '21 at 06:30

1 Answers1

2

This is one of the many reasons not to use onxyz-attribute-style event handlers: They execute in a multiply-nested code that has lots of methods of objects in the scope. In this case, you're accidentally running Node.prototype.normalize, which you can see exists:

console.log("normalize" in Node.prototype);

We can even see that that's the one your button is using:

<input type="button" onclick="console.log(normalize === Node.prototype.normalize)">

The Node.prototype.normalize method (quoting the MDN link above)...

...puts the specified node and all of its sub-tree into a "normalized" form. In a normalized sub-tree, no text nodes in the sub-tree are empty and there are no adjacent text nodes.

Use addEventListener instead, so that

  1. Your function doesn't have to be a global, and

  2. Your code isn't running in a weird scope with lots and lots of things to conflict with

 function normalize () {return 1;};
 
 document.getElementById("the-button").addEventListener("click", () => {
    console.log(normlize()); // Prints 1
 });
<input type="button" id="the-button" value="Click Me">

I've used an ID with getElementById in that example, but you don't have to use IDs for this to work. You just have to be able to identify the element with any CSS selector and querySelector or querySelectorAll.

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