javascripthtmldom-eventsthis

When I call a function in a DOM event attribute, how to ensure it has access to the event’s `this`?


I'm trying to call a function when the click event occurs. In the function itself, I refer to this:

function move(e) {
  var myId = this.id;
  console.log(myId);
}
<button id="A1" onclick="move()">#A1</button>
<button id="A2" onclick="move()">#A2</button>
<button id="A3" onclick="move()">#A3</button>

But I run the whole thing, the console displays “undefined”. When I try alert(this); I get “[object window]”.

How do I fix this?


Solution

  • When calling a function from an event handler, its this isn't set by the handler (though you can pass this from the handler per Xdazz's answer, or set this per Kyle's answer). Another approach is to extract the sender element from the event object associated with the event:

    function move(e) {
        if (!e)
            e = window.event;
        var sender = e.srcElement || e.target;
    
        //maybe some nested element.. find the actual table cell parent.
        while (sender && sender.nodeName.toLowerCase() != "td")
            sender = sender.parentNode;
    
        var myId = sender.id;
        alert(myId);
    }
    ​
    

    You also must pass the event explicitly:

    onclick="move(event)"
    

    Note that when the table cell has nested elements they will be the "sender" thus to grab the desired element (which is the table cell) you have to traverse upwards. To avoid all this headache see below how to attach the handlers through code.

    Live test case.

    That said, better practice would be to bind the click event through code instead of inline - don't mix HTML with JavaScript. To achieve this, have such code:

    window.onload = function() {
        var arrCells = document.getElementsByTagName("td");
        for (var i = 0; i < arrCells.length; i++) {
            var oCell = arrCells[i];
            if (oCell.id && oCell.id.substr(0, 1) == "A") {
                oCell.onclick = move;
            }
        }
    }
    

    With the above code in place, you can remove the inline onclick= calls from the HTML and keep your original function - the this will point to the clicked table cell.

    Updated fiddle.