JavaScript addEventListener() external function triggering without the event

Viewed 1149

I'm drawing a grid of boxes, want to be able to click on each box and send notification with its ID to server using SocketIO

<body>
<div class="wrapper">
    <div id="1" class="box"></div>
    <div id="2" class="box"></div>
</div>
</body>

let boxes = document.querySelectorAll('.box');

Array.from(boxes, function(box) {
    box.addEventListener('click', function()  {
        id = box.id

        //Send socketio message to server
        socket.emit('box_event_client', {data: id});
    });
});

This works, I can click each box and event is sent to server, but apparently this does not allow the even listener to be removed, as per https://www.w3schools.com/jsref/met_element_removeeventlistener.asp

Note: To remove event handlers, the function specified with the addEventListener() method must be an external function, Anonymous functions, like "element.removeEventListener("event", function(){ myScript });" will not work.

So I changed to this

Array.from(boxes, function(box) {
    box.addEventListener('click', addEL(box.id));
});

function addEL(boxID) {
    console.log("Box clicked: " + boxID)

    //Send socketio message to server
    socket.emit('box', {data: boxID});
}

Now as soon page is loaded into browser all boxes in grid are automatically 'clicked' and events sent to server.

Can anyone help understand why this is happening?

3 Answers

Can anyone help understand why this is happening?

Array.from(boxes, function(box) {
    box.addEventListener('click', addEL(box.id));
});

addEL(box.id) is function call therefore its taking id and executing it on each box's click listener attachment

Solution:

AddEventListener expects the event name and the callback function name or the function itself(inline function)

Just give the function name in the parameter.

Array.from(boxes, function(box) {
    box.addEventListener('click', addEL);
});

Access the id of the box from event object like event.currentTarget.id

function addEL(event) {
    let boxId=event.currentTarget.id
    console.log("Box clicked: " + boxId)
    //Send socketio message to server
    socket.emit('box', {data: boxId});
}

change

box.addEventListener('click', addEL(box.id));

to

box.addEventListener('click', function() { addEL(box.id) });

or simplier (be sure you can use ES6 here)

box.addEventListener('click', () => addEL(box.id));

or

box.addEventListener('click', addEL.bind(null, box.id));

it's because you use the result of the function, not the function itself

UPD: to be able to delete handler declare the function like var adder = addEL.bind(null, box.id) and use box.addEventListener('click', adder);

also there can be an array of such functions like adders.push(addEL.bind(null, box.id)), box.addEventListener('click', adders[i]);

The code box.addEventListener('click', addEL(box.id)); calls addEL(box.id) and passes the result of that call to the second argument of addEventListener, that's why addEL is called directly without any event.

if you want to be able to remove the listener then you need to write it that way:

Array.from(boxes, function(box) {
    let clickCallback = function()  {
        id = box.id

        //Send socketio message to server
        socket.emit('box_event_client', {data: id});
    }

    box.addEventListener('click', clickCallback);

    // you can remove the listener within the scope where clickCallback is defined
    // box.removeEventListener('click', clickCallback);
});
Related