Why does addEventListener fire before the event if at all?

Wilhelm picture Wilhelm · May 22, 2014 · Viewed 7.8k times · Source

I was experimenting [in jsfiddle] w/a function created to append a newly created TextNode to the <p> in the HTML below:

    <button onclick="addTextNode('YES! ');">YES!</button>
    <button onclick="addTextNode('NO! ');">NO!</button>
    <button onclick="addTextNode('WE CAN! ');">WE CAN!</button>
    <hr />
    <p id="p1">First line of paragraph.</p>

Here is my javascript as well:

    function addTextNode(text) {
        var newtext = document.createTextNode(text),
            p1 = document.getElementById("p1");

        p1.appendChild(newtext);
    }

This works fine. However, the crux reveals itself when I attempt to make my javascript 'unobtrusive' by removing the behavior from the markup...

    <button>YES!</button>
    <button>NO!</button>
    <button>WE CAN!</button>
    <hr />
    <p id="p1">First line of paragraph.</p>

then utilizing a loop to attach addEventListener to each <button> element, which in turn uses the child TextNode to call addTextNode:

    function addTextNode(text) {
        var newtext = document.createTextNode(text),
            p1 = document.getElementById("p1");

        p1.appendChild(newtext);
    }

    var btns = document.getElementsByTagName("button");

    for(i = 0; i < btns.length; i++){
        var button = btns[i]; 
        button.addEventListener("click", addTextNode(button.innerHTML));
    }

Two strange things happen with my code:
When [jsfiddle's] Code Wrap is set to 'no wrap - in head', nothing happens.

However, When Code Wrap is set to 'onLoad', 'onDomReady' or 'no wrap - in body' the function runs before the click and I get this.

Could someone tell me what I'm missing?

Answer

Kevin B picture Kevin B · May 22, 2014

The root of your problem is here:

button.addEventListener("click", addTextNode(button.innerHTML))

You're executing the function rather than passing it to the event listener. Instead, pass the function by reference, then get the innerHTML inside the function.

function addTextNode() {
    var newtext = document.createTextNode(this.innerHTML),
        p1 = document.getElementById("p1");

    p1.appendChild(newtext);
}

var btns = document.getElementsByTagName("button");

for(i = 0; i < btns.length; i++){
    var button = btns[i]; 
    button.addEventListener("click", addTextNode);
}

http://jsfiddle.net/bn85J/