Passing Event Object and Other arguments to Callback Function

Suppose I want to pass the event object along with other parameters to the callback function in the addEventLististener as shown below, how can I do it? Needless to say the code below doesn’t work, it will complain that “e is not defined”.

div.addEventListener("mousedown", function(){
   let val1 = "test1";
   let val2 = "test2";
  window.addEventListener("mousemove", mousemove(e, val1, val2));
 
})
function mousemove(e, value1, value2){
      return e.clientX;
}

You shouldn’t define a second event listener inside the callback function of the first. This means that the second event listener will be added over and over, every time the callback function is run.

I might be missing the point of what you are trying to do, but would something like this work?

const div = document.getElementById('myDiv');

div.addEventListener("mousedown", function(e){
  const val1 = "test1";
  const val2 = "test2";
  mousemove(e, val1, val2);
})

function mousemove(e, value1, value2){
  console.log(e.clientX);
}

Just to add. I remember asking similar questions on here years ago and struggling to wrap my head around the answers given. I seem to remember call and apply being offered at the time.

When you pass a handler function to addEventListener e.g.

document.addEventListener('click', handlerFunction)

The handlerFunction is saved to be run at a later time.

So for instance 10 seconds after the page is loaded, when you perform a mouse click the handler function will be called and an event object will be passed to it as an argument.

However if you do the following

document.addEventListener('click', handlerFunction(x, y))

Due to the use of the parentheses the handler function is being called immediately. The only thing being saved by addEventListener for later is what ever handlerFunction returns and that could quite possibly be undefined.

To illustrate you could make that function return another function

function getHandlerFunction() {
  // return a handler function to the addEventListener
  return function(event) {
    console.log(event.type) // click
  }
}

document.addEventListener('click', getHandlerFunction())

getHandlerFunction is called immeditately and returns the inner function

function(event) {
  console.log(event.type) // click
}

This is the function that will be assigned to the eventListener, and could actually just be typed in as

document.addEventListener('click', function(event) {
  console.log(event.type) // click
})
1 Like

Hi James, Thanks for your reply. You said “You shouldn’t define a second event listener inside the callback function of the first. This means that the second event listener will be added over and over, every time the callback function is run” but that’s exactly what I want to do. The event listener is removed on mouseup but I did not show the code here since it is irrelevant to my question.

Hi RPG,
I remember a post online somewhere that said that if we write a callback function inside of addeventlistener or removeeventlister as follows
document.addEventListener(‘click’, callback)

then when the callback gets invoked, the event object is passed as the first parameter by default.
However, if we write a callback function inside of addeventlistener or removeeventlister as follows

document.addEventListener(‘click’, callback())

it won’t pass in the event object and we have to pass it in by ourselves. I know that in Vue you can pass the event object in as a parameter but it has to have the dollar sign in front like $event.

Well it seems nothing we discussed here solves my problem as I can’t get it to work using vanilla javascript.

@oangodd Unless callback returns a handler function, this is just wrong.

Here are some common patterns though

document.addEventListener(‘click’, function(event) { callback(event, x, y) })

// or in arrow form and maybe a scenario where the event object isn't needed

document.addEventListener(‘click’, () => callback(props) )

Regardless addEventListener expects a function/method to handle that event

In basic form

addEventListener(type, listener);

MDN

listener
The object that receives a notification (an object that implements the Event interface) when an event of the specified type occurs. This must be an object with a handleEvent() method, or a JavaScript function. See The event listener callback for details on the callback itself.

@oangodd

Given we can pass an object with a handleEvent object as a handler, this is one possibility that comes to mind.

const box = document.querySelector('.box')

const mouseMoveHandler = {
  colour: '',
  backgroundColour: '',
  handleEvent(event) {
    console.log(event.clientX, event.clientY);
    console.log(`Colour: ${this.colour}, Background Colour: ${this.backgroundColour}`);
  }
}

box.addEventListener('mousedown', function(event){
  mouseMoveHandler.colour = 'white';
  mouseMoveHandler.backgroundColour = 'red';
  
  box.addEventListener('mousemove', mouseMoveHandler)
})

box.addEventListener('mouseup', function(event){

  box.removeEventListener('mousemove', mouseMoveHandler)
})


Note we are mutating state here, which isn’t great, but as I say one possibility.

I need to go out now, so won’t be around for a few hours.

It would help to know what val1 and val2 are? They are only created when you mousedown? Knowing what these values are and where they come from, might present another solution.

Just playing with another approach.

(function() {
  const box = document.querySelector('.box')
  
  const getMouseMoveHandler = function({x, y}) {
    return function(event) {
      console.log(event.clientX, event.clientY)
      console.log(`x: ${x}, y: ${y}`)
    }
  }
  
  // Same handler needs to be accessible
  // to both addEventListener and removeEventListener
  let mouseMoveHandler

  box.addEventListener('mousedown', function(event) {
    const someData = { x: 5, y: 10 }

    mouseMoveHandler = getMouseMoveHandler(someData)
    box.addEventListener('mousemove', mouseMoveHandler)
  })

  box.addEventListener('mouseup', function(event){
    box.removeEventListener('mousemove', mouseMoveHandler)
  })
}())

As the mousedown event has an element as the target, why not add what you want to pass to the event listener as custom data to the targeted element where the listener can access it?

div.addEventListener("mousedown", function(){
    div.data-val1 = "test1";
    div.data-val2 = "test2";
window.addEventListener("mousemove", mousemove(e, val1, val2));
 
})

function mousemove(e){
     let value1 = e.target.dataset.val1;
     let value2 = e.target.dataset.val2;
 
return e.clientX;
}

Hi @rpg_digital, to be able to pass arguments to my call back function I have wrapped it with an anonymous function as you have suggested but now I cannot use the removeEventListener to remove the mousemove event listener. Below is the pattern I used but I cannot get the removeEventListener to work.

window.addEventListener(‘mousemove’, function(event,x,y) { callback(event, x, y) }, false)
window.removeEventListener(‘mousemove’, function(event,x,y) { callback(event, x, y) }, false)

If it was permitted to remove the event listener then it would no longer exist in the callback function.
Just copy the event properties that you need into an object and use it in the callback function.

Hi Dennis, Can you please give an example code on what you’ve just stated.

I was a bit hasty in my previous reply.
If you study https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener#syntax you may notice that when registering the listener that use a named function. Using an anonymous function prevents the removeEventListener from identifying the listener. I may be wrong but try it with a named function.

The mousemove event will usually be fired when the JavaScript is idle. For variables val1 and val2 to be available when the mousemove callback function is invoked, they therefore need to be global variables (or equivalent).

Instead of adding and removing the mousemove event listener, I suggest keeping the state of the mouse button in a global variable as the example here:

1 Like

Another blast at this.

const displayCoords = function (event, props) {
  boxElem.innerText = 
      `${props.title}:\n Client X/Y: ${event.clientX}, ${event.clientY}`;
}

const addMoveHandler = function(event) {
  
  const props = { title: 'Mouse Position' };

  // add the handler as a property of addMoveHandler
  // functions are objects too.
  addMoveHandler.handler = function (event) {
    displayCoords(event, props);
  }
  
  boxElem.addEventListener('mousemove', addMoveHandler.handler);
};

// removing named moveHandler
const removeMoveHandler = function(event) {
  boxElem.removeEventListener('mousemove', addMoveHandler.handler);
  boxElem.innerText = '';
};

const boxElem = document.querySelector('.box');

boxElem.addEventListener('mousedown', addMoveHandler);
document.addEventListener('mouseup', removeMoveHandler);

Why do you need to define the properties inside of the handler?

Edit: Or probably simpler

// global handler
let moveHandler;

const addMoveHandler = function(event) {
  
  const props = { title: 'Mouse Position' };

  moveHandler = function (event) {
    displayCoords(event, props);
  }
  
  boxElem.addEventListener('mousemove', moveHandler);
};

const removeMoveHandler = function(event) {
  boxElem.removeEventListener('mousemove', moveHandler);
  boxElem.innerText = '';
};

When you use

window.addEventListener(‘mousemove’, function(event,x,y) { callback(event, x, y) }, false)
window.removeEventListener(‘mousemove’, function(event,x,y) { callback(event, x, y) }, false)

The event listener is an anonymous function.

Anonymous functions exist only where they occur in the code.

When you use the removeEventListener function the anonymous function in the parameter list it is a different anonymous function to the one created in the addEventListiner parameter list.

Ergo the parameter lists are different and the removeEventListener call fails.

Hi all,
Thanks for all of your replies, they give me lots of ideas on how to handle this situation. I did some digging online to see if there is a short and sweet way to circumvent this issue and I did find such a way. I came across a solution that allows me to remove an event listener even though the callback is wrapped in an anonymous function without having to make major changes to my existing code. All I need to do is create a variable before the addEventListener function and then assign this variable to the anonymous function inside of the addEventListener parameter list. Then we can use the variable to remove the event listener.

As in the examples in post #9 and post #16? Or is it something different?

If it is, it would be interesting to see as it might help others who stumble across this thread.

It is different in appearance from both #9 and #16 but I think the idea might be the same. Below is the code sample.

let functionToRemove;
window.addEventListener(‘mousemove’, functionToRemove = function(event) { callback(x, y) }, false);
window.removeEventListener(‘mousemove’, functionToRemove, false);

Thanks for showing us @oangodd :slight_smile: