2

I have this code that has an onclick handler and an IIFE.

var btn = document.getElementById("btn");
btn.onclick = function clickHandler(){console.log("Button clicked"); } 

//btn.onclick = function clickHandler(){console.log("Now clicked"); };

(function(){
    console.log("Hello");
})();

The issue is that the clickHandler gets invoked automatically after the page loads and the IIFE isn't invoked. However, on commenting out line 2, and un-commenting out line 3 (the commented out line in the above code), the code works as expected.

The only difference between the two lines is that the 2nd line has no ; in the end, but the 3rd line has.

Also, if I remove the IIFE and keeping the rest of the code unchanged, the code works fine.

Demo : https://codepen.io/anon/pen/Pezpqv

Animesh Kumar
  • 237
  • 3
  • 11
  • 1
    Add `;` at the end of the second line to fix this. – raina77ow Apr 25 '18 at 10:52
  • @raina77ow I know that putting `;` would fix it. But why is the handler getting invoked automatically? – Animesh Kumar Apr 25 '18 at 10:53
  • 1
    Because your statement is not treated the way you think about it - `(function(` is considered a part of the statement started on the second line. – raina77ow Apr 25 '18 at 10:54
  • So how does `(function(` being a part of the second line invokes the handler? And if it does, why doesn't the `console.log("Hello World")` get logged in the console? – Animesh Kumar Apr 25 '18 at 10:56
  • 2
    Added the explanation. Remember to check [this question](https://stackoverflow.com/questions/2846283/what-are-the-rules-for-javascripts-automatic-semicolon-insertion-asi) for more details on how ASI works - and how it doesn't - in JavaScript. – raina77ow Apr 25 '18 at 10:59

3 Answers3

4

This is tricky. Because semicolon is missing in the second line of your code, parser doesn't stop there - and consumes all the tokens it can. In the end, the code's parsed as...

btn.onclick = function clickHandler() {
  console.log("Button clicked");
}(
  //btn.onclick = function clickHandler(){console.log("Now clicked"); };

  function() {
    console.log("Hello");
  }
)
();

See the difference () makes - your function expression is invoked immediately. It's funny that ...

function() { console.log('Hello') }

... is treated as its parameter value - which is ignored anyway.

After that, result of clickHandler is attempted to be reused as a function - but, as it returns undefined, JS stops with (intermediate value)(...) is not a function.


Thought it's a good idea to twist your code a bit - and made the following snippet to illustrate the idea with more, well, drama in it.

const btn = document.getElementById('btn');
btn.onclick = function clickHandler(e) {
  console.log('Now clicked');
  return e;
}
  
//btn.onclick = function clickHandler(){console.log("Now clicked"); };

(function() {
  console.log('Hi!');
})();
<button type="button" id="btn">Click me!</button>

Show this to your colleagues and ask the same question (why doesn't click handler work here?) if you want to make them a bit... mad. )

raina77ow
  • 103,633
  • 15
  • 192
  • 229
1

You missed a semi column over here

btn.onclick = function clickHandler(){console.log("Button clicked"); } ;
Roopak Puthenveettil
  • 1,387
  • 2
  • 13
  • 27
  • 2
    I mentioned that in the question itself. See the 2nd last paragraph. – Animesh Kumar Apr 25 '18 at 10:58
  • 1
    The ECMAScript specification has specific rules for automatic semicolon insertion, however in this case a semicolon isn't automatically inserted because the parenthesised expression that begins on the next line can be interpreted as an argument list for a function call. https://www.ecma-international.org/ecma-262/5.1/#sec-7.9 – Roopak Puthenveettil Apr 25 '18 at 11:02
1

The problem is in starting code with ( while the previous line of code does not end with semicolon (;). This treats both line of codes to be executed together like:

}(

To solve the problem either you have to put ; at the end of the statemnet or move the IIFE code to the top of your code:

(function(){
    console.log("Hello");
})();
var btn = document.getElementById("btn");
  btn.onclick = function clickHandler(){console.log("Button clicked"); 
}
<button type="button" id="btn" >Click</button>
Mamun
  • 66,969
  • 9
  • 47
  • 59