javascriptevent-listenerjavascript-scope

Variable scope and event listeners callback


I have the following code, which is working now. The question is why?

    let resizing = false
    let startX = 0
    let startY = 0
    window.addEventListener('mousedown', (e) => {
      resizing = true
      startX = e.clientX
      startY = e.clientY
      console.log('startX ' + startX)
      document.body.addEventListener('mouseup', (e) => {
        if (resizing) {
          let endX = e.screenX
          console.log('endX ' + endX)
          let endY = e.screenY
          this.resize(startX, endX, startY, endY, window)
        }
        resizing = false
        e.target.removeEventListener('mouseup', window)
      })
    })

Previously I had defined my startX and startY inside the mouseup callback like so:

    let resizing = false
    window.addEventListener('mousedown', (e) => {
      resizing = true
      let startX = e.clientX
      let startY = e.clientY
      console.log('startX ' + startX)
      document.body.addEventListener('mouseup', (e) => {
        if (resizing) {
          let endX = e.screenX
          console.log('endX ' + endX)
          let endY = e.screenY
          this.resize(startX, endX, startY, endY, window)
        }
        resizing = false
        e.target.removeEventListener('mouseup', window)
      })
    })

But I was getting the same values for startX and startY each time the event was triggered after the first time. Why? This dosen't make sense to me as the scope let should have the variable be reset every time the callback function for the mouseup event is done?

I updated my code according to Taplars comment and now the scope works as I expected

let window = this
window.addEventListener('mousedown', (e) => {
  let startX = e.clientX
  let startY = e.clientY
  console.log('startX ' + startX)
  var mouseUpHandler = function (e) {
    console.log('mouseup')
    let endX = e.screenX
    console.log('endX ' + endX)
    let endY = e.screenY
    window.resize(startX, endX, startY, endY, window)
    document.body.removeEventListener('mouseup', mouseUpHandler)
  }
  document.body.addEventListener('mouseup', mouseUpHandler)
})
  }


Solution

  • Your original logic had e.target.removeEventListener('mouseup', window), where e.target resolves to document.body. So it is effectively performing:

    document.body.removeEventListener('mouseup', window);
    

    One issue here is that the second argument that is passed into the removeEventListener() method is expected to be one of the methods you previously attached. Ref. https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener

    Given that you are passing in the window, which is not one of the methods you previously attached (nor is it a method at all), my assumption would be that the logic either tests that the parameter is not a function and does not do any thing, or it tries to remove it, sees that it doesn't match any of the attached methods, and simply does nothing. This is, however, an assumption.

    However, given that your modification to the logic to fix the passing in of the second argument resolved your issue, this leans towards this being the issue and your observed issue was most likely due to listener methods not being removed and garbage being observed by duplicate bindings.

    You could test this by changing the parameter back to window, and if the issue appears again, this would pretty much assert this assumption.