你可以如何从一个JavaScript类内部移除事件监听器?

huangapple go评论57阅读模式
英文:

How can I remove the event listeners from within a javascript class?

问题

这是一个例子类,其构造函数接收一个元素作为参数,可以通过拖动它来移动。但是在鼠标弹起时它不会停止拖动。如果我理解正确,根据我在其他帖子中阅读的内容,我猜我需要将方法分配给变量,但我不知道如何做,因为它们需要event参数。

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', event => this.onMouseMove(event));
        document.removeEventListener('mouseup', event => this.onMouseUp(event));
    }
}

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 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:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

class Example
{
    rect;

    constructor(rect)
    {
        this.rect = rect;
        this.rect.addEventListener(&#39;mousedown&#39;, event =&gt; this.onMouseDown(event));
    }

    onMouseDown(event)
    {
        document.addEventListener(&#39;mousemove&#39;, event =&gt; this.onMouseMove(event));
        document.addEventListener(&#39;mouseup&#39;, event =&gt; this.onMouseUp(event));
    }

    onMouseMove(event)
    {
        this.rect.style.left = event.clientX;
        this.rect.style.top = event.clientY;
    }

    onMouseUp(event)
    {
        document.removeEventListener(&#39;mousemove&#39;, this.onMouseMove);
        document.removeEventListener(&#39;mouseup&#39;, this.onMouseUp);
    }
}

document.addEventListener(&quot;DOMContentLoaded&quot;, function()
{
    var rect = document.getElementById(&#39;rect&#39;);
    var ex = new Example(rect);
});

<!-- language: lang-css -->

#rect {
    position: absolute;
    border: 1px solid #ccc;
    background-color: #DFD;
    width: 100px;
    height: 100px;
}

<!-- language: lang-html -->

&lt;html&gt;
    &lt;head&gt;
        &lt;meta charset=&quot;utf-8&quot;/&gt;
        &lt;title&gt;Test&lt;/title&gt;
    &lt;/head&gt;

    &lt;body&gt;
        &lt;div id=&quot;rect&quot;&gt;&lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;

<!-- end snippet -->

I don't know why the snippet here does not work, I copied the code from some files, that perfectly work.

答案1

得分: 1

TL;DR 最简单的方法是使用 this.rect.addEventListener('mousedown', this.onMouseDown); 并将你的类方法改为箭头函数 onMouseDown = (event) => {...}

问题

当你添加事件监听器时,你需要保留对已添加的函数的引用以便移除它。当你这样做时:

document.addEventListener('mousemove', event => this.onMouseMove(event));

你正在使用 => "箭头函数" 操作符创建一个全新的函数。你的代码等效于这样:

onMouseDown(event) {
    const listener = event => this.onMouseMove(event);
    document.addEventListener('mousemove', listener);
}

你可以看到 listener 函数只存在于 onMouseDown 方法内部。没有办法在其他方法中访问它。

因此,当你稍后运行这行代码时:

document.removeEventListener('mousemove', this.onMouseMove);

什么都不会发生,因为对你的方法 this.onMouseMove 的引用与 listener 不同。它们是不同的函数。

解决方法

你应该研究 JavaScript 中的 this,以及方法与函数的区别。在处理类和事件时,这种类型的问题经常出现。

有几种方法可以解决这个问题。首先,修改你的代码以绑定类方法,以便稍后可以移除它:

this.rect.addEventListener('mousedown', this.onMouseDown);

但这里有一个问题。当你将 this.onMouseDown 传递给 addEventListener 时,你正在将事件监听器绑定到函数 onMouseDown。这样做后,它变成了一个 "函数",而不是一个 "方法"。它失去了与 Example 类的关联,因此当 onMouseDown 运行时,this 将不再是 Example 实例。因此,当代码执行以下操作时:

document.addEventListener('mousemove', event => this.onMouseMove(event));

this 将不再指向类实例,代码将引发错误,而不是添加事件监听器。

JavaScript 中一个方便的技巧是使用箭头函数操作符来定义方法,这使它们始终使用父级的 this,这将是你的类实例。以下是完整的代码:

class Example {
    rect;

    constructor(rect) {
        this.rect = rect;
        this.rect.addEventListener('mousedown', this.onMouseDown);
    }

    onMouseDown = () => {
        document.addEventListener('mousemove', this.onMouseMove);
        document.addEventListener('mouseup', this.onMouseUp);
    }

    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);
    }
}
英文:

TL;DR the easiest thing to do here is this.rect.addEventListener(&#39;mousedown&#39;, this.onMouseDown); and change your class methods to fat arrow functions onMouseDown = (event) =&gt; {...}

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:

 document.addEventListener(&#39;mousemove&#39;, event =&gt; this.onMouseMove(event));

You're creating an entirely new function using the =&gt; "fat arrow" operator. Your code is equivalent to this:

onMouseDown(event) {
    const listener = event =&gt; this.onMouseMove(event);
    document.addEventListener(&#39;mousemove&#39;, listener);
}

You can see that the listener function only exists inside the onMouseDown method. There's no way to access it in other methods.

So when you run this line, later:

document.removeEventListener(&#39;mousemove&#39;, this.onMouseMove);

Nothing happens, because the reference to your method this.onMouseMove is not the same as listener. They are different functions.

Solution(s)

You should research this, and methods vs functions 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:

this.rect.addEventListener(&#39;mousedown&#39;, this.onMouseDown);

But there's a problem here. When you pass in this.onMouseDown to addEventListener, you're binding the event listener to the function onMouseDown. When you do this, it becomes a "function," not a "method". It loses its relationship to the Example class, so that when onMouseDown runs, this will no longer be the instance of Example. So when the code executes with:

document.addEventListener(&#39;mousemove&#39;, event =&gt; this.onMouseMove(event));

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:

class Example {
    rect;

    constructor(rect) {
        this.rect = rect;
        this.rect.addEventListener(&#39;mousedown&#39;, this.onMouseDown);
    }

    onMouseDown = () =&gt; {
        document.addEventListener(&#39;mousemove&#39;, this.onMouseMove);
        document.addEventListener(&#39;mouseup&#39;, this.onMouseUp);
    }

    onMouseMove = (event) =&gt; {
        this.rect.style.left = event.clientX;
        this.rect.style.top = event.clientY;
    }

    onMouseUp = (event) =&gt; {
        document.removeEventListener(&#39;mousemove&#39;, this.onMouseMove);
        document.removeEventListener(&#39;mouseup&#39;, this.onMouseUp);
    }
}

答案2

得分: 1

你需要在后续能够移除事件处理函数,因此需要存储对事件处理函数的引用。

此外,你需要在构造函数中使用箭头函数来分配事件处理函数,以确保这些函数保留对 "this" 的正确引用,并防止不必要地创建新的函数实例。

最后,在 onMouseMove 事件处理程序内部,你没有指定 "left" 和 "top" CSS 属性的单位,以使其可拖动。

class Example {
  rect;

  onMouseMoveHandler;
  onMouseUpHandler;

  constructor(rect) {
    this.rect = rect;
    this.onMouseDownHandler = (event) => this.onMouseDown(event);
    this.rect.addEventListener('mousedown', this.onMouseDownHandler);
  }

  onMouseDown(event) {
    this.onMouseMoveHandler = (event) => this.onMouseMove(event);
    this.onMouseUpHandler = (event) => this.onMouseUp(event);

    document.addEventListener('mousemove', this.onMouseMoveHandler);
    document.addEventListener('mouseup', this.onMouseUpHandler);
  }

  onMouseMove(event) {
    this.rect.style.left = event.clientX + 'px'; // 你需要添加 'px' 以指定单位
    this.rect.style.top = event.clientY + 'px';
  }

  onMouseUp(event) {
    document.removeEventListener('mousemove', this.onMouseMoveHandler);
    document.removeEventListener('mouseup', this.onMouseUpHandler);
  }
}

document.addEventListener('DOMContentLoaded', function () {
  var rect = document.getElementById('rect');
  var ex = new Example(rect);
});
英文:

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

    class Example {
  rect;

  onMouseMoveHandler;
  onMouseUpHandler;

  constructor(rect) {
    this.rect = rect;
    this.onMouseDownHandler = (event) =&gt; this.onMouseDown(event);
    this.rect.addEventListener(&#39;mousedown&#39;, this.onMouseDownHandler);
  }

  onMouseDown(event) {
    this.onMouseMoveHandler = (event) =&gt; this.onMouseMove(event);
    this.onMouseUpHandler = (event) =&gt; this.onMouseUp(event);

    document.addEventListener(&#39;mousemove&#39;, this.onMouseMoveHandler);
    document.addEventListener(&#39;mouseup&#39;, this.onMouseUpHandler);
  }

  onMouseMove(event) {
    this.rect.style.left = event.clientX + &#39;px&#39;; // 
 you need to Add &#39;px&#39; to specify the unit

    this.rect.style.top = event.clientY + &#39;px&#39;;
  }

  onMouseUp(event) {
    document.removeEventListener(&#39;mousemove&#39;, this.onMouseMoveHandler);
    document.removeEventListener(&#39;mouseup&#39;, this.onMouseUpHandler);
  }
}

document.addEventListener(&#39;DOMContentLoaded&#39;, function () {
  var rect = document.getElementById(&#39;rect&#39;);
  var ex = new Example(rect);
});

答案3

得分: 1

Two already posted answers explains perfectly why your code isn't working - you have to pass exactly the same reference to the function both in addEventListener and removeEventListener. To add my two cents to existing answers - there is a very convenient method for removing event listeners (supported by all major browsers) - AbortController. As the third argument passed to the addEventListener function, you can pass an object with options, and you can pass the signal from AbortController as the signal property. Now you don't have to remember the reference to the passed function; just whenever you want to remove the event listener, you can call the abort() method from AbortController. Example:

const button = document.querySelector('button');
const abortController = new AbortController();

button.addEventListener(
  'click',
  () => {
    console.log('CLICK');
  },
  { signal: abortController.signal }
);

setTimeout(() => {
  abortController.abort();
}, 5000);
英文:

Two already posted answers explains perfectly why you code isn't working - you have to pass exactly same reference to function both in addEventListener and removeEventListener. 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 to addEventListener function you can pass object with options, and you can pass signal from AbortController as signal property. Now you don't have to remember reference to passed function, just whenever you want to remove event listener, you can call abort() method from AbortController. Example:

const button = document.querySelector(&#39;button&#39;);
const abortController = new AbortController();

button.addEventListener(
  &#39;click&#39;,
  () =&gt; {
    console.log(&#39;CLICK&#39;);
  },
  { signal: abortController.signal }
);

setTimeout(() =&gt; {
  abortController.abort();
}, 5000);

答案4

得分: 1

Method 1:

class Example {
    rect;

    constructor(rect) {
        this.rect = rect;
        this.rect.addEventListener('mousedown', this.onMouseDown);
    }

    onMouseDown = (event) => {
        document.addEventListener('mousemove', this.onMouseMove);
        document.addEventListener('mouseup', this.onMouseUp);
    }

    onMouseMove = (event) => {
        this.rect.style.left = event.clientX + 'px';
        this.rect.style.top = event.clientY + 'px';
    }

    onMouseUp = (event) => {
        document.removeEventListener('mousemove', this.onMouseMove);
        document.removeEventListener('mouseup', this.onMouseUp);
    }
}

function onDOMCLoaded(event) {
    var rect = document.getElementById('rect');
    var ex = new Example(rect);
}

document.addEventListener("DOMContentLoaded", onDOMCLoaded);

Method 2:

class Example {
    rect;

    constructor(rect) {
        this.rect = rect;
        this.onMouseDown = this.onMouseDown.bind(this);
        this.onMouseMove = this.onMouseMove.bind(this);
        this.onMouseUp = this.onMouseUp.bind(this);
        this.rect.addEventListener('mousedown', this.onMouseDown);
    }

    onMouseDown(event) {
        document.addEventListener('mousemove', this.onMouseMove);
        document.addEventListener('mouseup', this.onMouseUp);
    }

    onMouseMove(event) {
        this.rect.style.left = event.clientX + 'px';
        this.rect.style.top = event.clientY + 'px';
    }

    onMouseUp(event) {
        document.removeEventListener('mousemove', this.onMouseMove);
        document.removeEventListener('mouseup', this.onMouseUp);
    }
}

function onDOMCLoaded(event) {
    var rect = document.getElementById('rect');
    var ex = new Example(rect);
}

document.addEventListener("DOMContentLoaded", onDOMCLoaded);

CSS:

#rect {
    position: absolute;
    border: 1px solid #ccc;
    background-color: #DFD;
    width: 100px;
    height: 100px;
}

HTML:

<div id="rect"></div>
英文:

EDIT : Sorry there was no answer when I started to write my answer.
I'm glad that an answer is marked as accepted.
So, this is just for information...

There are two possibilities that should work in your case :

Method 1 :

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

		    class Example {
				rect;

				constructor(rect) {
					this.rect = rect;
					this.rect.addEventListener(&#39;mousedown&#39;, this.onMouseDown);
				}

				onMouseDown = (event) =&gt; {
					document.addEventListener(&#39;mousemove&#39;, this.onMouseMove);
					document.addEventListener(&#39;mouseup&#39;, this.onMouseUp);
				}

				onMouseMove = (event) =&gt; {
					this.rect.style.left = event.clientX + &#39;px&#39;;
					this.rect.style.top = event.clientY + &#39;px&#39;;
				}

				onMouseUp = (event) =&gt; {
					document.removeEventListener(&#39;mousemove&#39;, this.onMouseMove);
					document.removeEventListener(&#39;mouseup&#39;, this.onMouseUp);
				}
			}

			function onDOMCLoaded(event) {
				var rect = document.getElementById(&#39;rect&#39;);
				var ex = new Example(rect);
			}

			document.addEventListener(&quot;DOMContentLoaded&quot;, onDOMCLoaded);

<!-- language: lang-css -->

		#rect {
			position: absolute;
			border: 1px solid #ccc;
			background-color: #DFD;
			width: 100px;
			height: 100px;
		}

<!-- language: lang-html -->

&lt;div id=&quot;rect&quot;&gt;&lt;/div&gt;

<!-- end snippet -->

Or this possibility :

Method 2 :

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

		class Example
		{
			rect;

			constructor(rect)
			{
				this.rect = rect;
				this.onMouseDown = this.onMouseDown.bind(this);
				this.onMouseMove = this.onMouseMove.bind(this);
				this.onMouseUp = this.onMouseUp.bind(this);
				this.rect.addEventListener(&#39;mousedown&#39;, this.onMouseDown);
			}

			onMouseDown(event)
			{
				document.addEventListener(&#39;mousemove&#39;, this.onMouseMove);
				document.addEventListener(&#39;mouseup&#39;, this.onMouseUp);
			}

			onMouseMove(event)
			{
				this.rect.style.left = event.clientX + &#39;px&#39;;
  				this.rect.style.top = event.clientY + &#39;px&#39;;
			}

			onMouseUp(event)
			{
				document.removeEventListener(&#39;mousemove&#39;, this.onMouseMove);
				document.removeEventListener(&#39;mouseup&#39;, this.onMouseUp);
			}
		}
		function onDOMCLoaded(event)
		{
			var rect = document.getElementById(&#39;rect&#39;);
			var ex = new Example(rect);
		}
		document.addEventListener(&quot;DOMContentLoaded&quot;,onDOMCLoaded);

<!-- language: lang-css -->

		#rect {
			position: absolute;
			border: 1px solid #ccc;
			background-color: #DFD;
			width: 100px;
			height: 100px;
		}

<!-- language: lang-html -->

	&lt;div id=&quot;rect&quot;&gt;&lt;/div&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年5月30日 03:03:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76359778.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定