Understanding anonymous function in this code

Understanding anonymous function in this code
0

#1

Hey fellow campers,
While looking for ways to resize a div using javascript, I have come across this piece of code in this article. I need a little guidance in understanding this function below:

function makeResizableDiv(div) {
  const element = document.querySelector(div);
  const resizers = document.querySelectorAll(div + ' .resizer')
  for (let i = 0;i < resizers.length; i++) {
    const currentResizer = resizers[i];
    currentResizer.addEventListener('mousedown', function(e) {
      currentResizer.addEventListener('mousemove', resize)
    })
    
    function resize(e) {
      if (currentResizer.classList.contains('bottom-right')) {
        element.style.width = e.pageX - element.getBoundingClientRect().left + 'px';
      }
    }
  }
}

makeResizableDiv('.resizable')

(There are CSS and HTML part of this code, but my question is not related to them so mentioning those did not matter)

My understanding of this function is this:

  1. whenever the makeResizaleDiv function is called, the constant element would equate to the element to be resized (passed in arguments). Similarly, 4 corners of the element to be resized would get stored in constant resizers array
  2. In the for loop, constant currentResizer would equate the first corner.
  3. so, I am facing problem in understanding this part of the code in particular:
  • the event listener is attached to currentResizer which would trigger the anonymous function when the mouse is pressed down AND that anonymous function would attach another event listener to the currentResizer which would trigger resize function on mouse move event.

  • resize function has access to constant element and currentResizer and event e as it is declared within the scope of makeResizaleDiv. What resize function does is that if the if condition gets true it would set the element (element to be resized) width to the difference of e.pageX - element.getBoundingClientRect().left + 'px' where :
    => element.getBoundingClientRect().left is the position relative to the viewport from left side
    => A floating-point number of pixels from the left edge of the document at which the mouse was clicked and e should be any mouse event.

My confusion here is

  • whether e here is mouse click OR mouse move event?
  • How this event got passed as an argument in function(e)?

Any guidance here would be a HUGE help!:no_mouth:


#2

Does it make it a little clearer if I just fiddle with the code slightly?

currentResizer.addEventListener('mousedown', function () {
  currentResizer.addEventListener('mousemove', resize);
})

function resize(resizeEvent) {
  if (currentResizer.classList.contains('bottom-right')) {
    element.style.width = resizeEvent.pageX - element.getBoundingClientRect().left + 'px';
  }
}

#3

This is the very nature of adding an event handler. When the event is fired, the callback function receives the event object as it’s first argument.

Referring back to your original code, the e in the mousedown’s anonymous callback function is different than the e in the mousemove function. Each refers to a different event object.


#4

Okay, so what I understand from the above code is that here e is the event triggered due to mouse move event, so as the mouse keeps moving, it would continue to invoke the resize function


#5

Correct me if I am wrong,
in the anonymous function, e would refer to the mouse down event, and it is passed as the first argument?


#6

You have it correct. If you would not have specified e as a parameter for the anonymous function (as in Dan’s example), it still would have been passed, so you would have needed to reference the arguments object to get the event object. For example:

currentResizer.addEventListener('mousedown', function () {
  const mouseDownEventObj = arguements[0]; // first argument passed to this anonymous function.
  currentResizer.addEventListener('mousemove', resize);
})

#7

That made it a lot clear!
so in the next step, the anonymous function would attach the mouse move event handler to currentResizer element. This event would fire as the mouse moves, which in turn would invoke the resize function and that too would take the mouse move event as its first argument, so here in e.pageX , e is the mouse move event. Did I get right till here?


#8

Yup: basically, the drag resize shouldn’t happen until the mouse button is depressed, so the ‘outer’ event (‘mousedown’) fires on that. That ‘inner’ event can only fire when the mouse is depressed, and when that ‘resize’ event fires, it triggers the resize callback function. It’s a way of making sure those events are picked up in the correct order - is the button depressed? Yes, so is the resize event firing? Yes, so call that innermost function


#9

Finally, this helped me to understand the logic! :sweat_smile: Now this function makes much more sense.

Okay, this might be a bit silly question, but just for the sake of clarity, if I continue to move my mouse (in depressed state), how many times would the resize function would invoke before the mouse up event takes place. Would it invoke just one time when the mouse started moving? or will it continue to invoke till mouse stops because mouse move event would continue to fire?


#10

That’s a good question! It will fire every. Single. Time you drag to resize with the mouse. Even you just move it a little bit, a single pixel right or up or whatever. It is going to try to constantly recalculate where stuff should be on the screen. This is quite possibly fine in this case - it’s tightly scoped to a mousedown on a specific element - but the only way you can tell is to play around and see if your browser throws a hissy fit.

Normally what is done to mitigate the issues with this (calling a function possibly tens/hundreds/thousands of times a second can obviously cause performance issues) is to throttle the ‘resize’ function - making sure that is only called every n milliseconds. This is actually quite easy to do, but I’ll leave it as an exercise if you want to try it.


#11

That actually sounds quite interesting, I would definitely dig further into this.:+1:


#12

@DanCouper @RandellDawson thank you so much for helping me out, this has really strengthened my concepts :smiley:


#13

This is going a little bit away from the original question, but there are two functions (or they can be modifications to existing functions) that are useful to have in your toolbox with JS when you are dealing with events:

  • throttle will cause a given function to only be executed periodically (every 500ms for example).
  • debounce will cause a function (generally one that is likely to be called many many times in a short period of time) to only be called once at the end of a burst of calls.

(There’s a third piece of the puzzle thats worth learning when you’re tweaking things to make performance smooth, the builtin requestAnimationFrame function, but that’s going a bit deep atm)


#14

That is a quite an informative article. This makes me wonder, a lot is going on, even behind a single drag