I already saw this thread, but I think my case is a bit different.
This is an example class, whose constructor receives an element as parameter, that can be moved dragging it. But it does not stop being dragged on mouse up. If I good understood what I read in the other thread, I guess I have to assign the methods to variables, but I don’t know how, as they take the event
parameter:
class Example
{
rect;
constructor(rect)
{
this.rect = rect;
this.rect.addEventListener('mousedown', event => this.onMouseDown(event));
}
onMouseDown(event)
{
document.addEventListener('mousemove', event => this.onMouseMove(event));
document.addEventListener('mouseup', event => this.onMouseUp(event));
}
onMouseMove(event)
{
this.rect.style.left = event.clientX;
this.rect.style.top = event.clientY;
}
onMouseUp(event)
{
document.removeEventListener('mousemove', this.onMouseMove);
document.removeEventListener('mouseup', this.onMouseUp);
}
}
document.addEventListener("DOMContentLoaded", function()
{
var rect = document.getElementById('rect');
var ex = new Example(rect);
});
#rect {
position: absolute;
border: 1px solid #ccc;
background-color: #DFD;
width: 100px;
height: 100px;
}
<html>
<head>
<meta charset="utf-8"/>
<title>Test</title>
</head>
<body>
<div id="rect"></div>
</body>
</html>
I don’t know why the snippet here does not work, I copied the code from some files, that perfectly work.
3
Answers
TL;DR the easiest thing to do here is
this.rect.addEventListener('mousedown', this.onMouseDown);
and change your class methods to fat arrow functionsonMouseDown = (event) => {...}
The problem
When you add an event listener, you need to keep a reference to the function you added to remove it. When you do this:
You’re creating an entirely new function using the
=>
"fat arrow" operator. Your code is equivalent to this:You can see that the
listener
function only exists inside theonMouseDown
method. There’s no way to access it in other methods.So when you run this line, later:
Nothing happens, because the reference to your method
this.onMouseMove
is not the same aslistener
. They are different functions.Solution(s)
You should research
this
, andmethods
vsfunctions
in Javascript. This type of issue comes up a lot when dealing with classes and events.There a few ways to solve this problem. First of all, change your code to bind the class method, so that we can remove it later:
But there’s a problem here. When you pass in
this.onMouseDown
toaddEventListener
, you’re binding the event listener to the functiononMouseDown
. When you do this, it becomes a "function," not a "method". It loses its relationship to theExample
class, so that whenonMouseDown
runs,this
will no longer be the instance ofExample
. So when the code executes with:this
will no longer point to the class instance, and the code will error rather than add an event listener.A handy trick in Javascript to get around this issue is to instead define methods with the fat arrow operator, which makes them always use the parent’s
this
, which will be your class instance. Here’s the full code:you will need to store references to the event handler functions in order to remove them later.
also you will need to assign the event handler functions using arrow functions in the constructor to make sure that the functions retain the correct reference to( this ) and prevent unnecessary creation of new function instances.
last thing ,Inside the onMouseMove event handler,you didn’t specify the unit to the "left" and "top" CSS properties to make it draggable
Two already posted answers explains perfectly why you code isn’t working – you have to pass exactly same reference to function both in
addEventListener
andremoveEventListener
. To add my two cents to existing answers – there is very convenient method for removing event listeners (supported by all major browsers) – AbortController. As third argument passed toaddEventListener
function you can pass object with options, and you can passsignal
fromAbortController
assignal
property. Now you don’t have to remember reference to passed function, just whenever you want to remove event listener, you can callabort()
method fromAbortController
. Example: