【问题标题】:How adding event handler inside a class with a class-method as the callback?如何在使用类方法作为回调的类中添加事件处理程序?
【发布时间】:2017-09-29 08:45:39
【问题描述】:

如何在使用类方法作为回调的类中添加事件处理程序?

<div id="test">move over here</div>
<script>
    oClass = new CClass();
    function CClass()
    {
        this.m_s = "hello :-/";
        this.OnEvent = OnEvent;
        with(this)
        {
            var r = document.getElementById("test");
            r.addEventListener('mouseover', this.OnEvent);  // this does NOT work :-/
        }
      
        function OnEvent()
        {
            alert(this);    // this will be the HTML div-element
            alert(this.m_s);    // will be undefined :-()
        }
    }
</script>

是的,我知道一些使它起作用的怪癖,但是当引入这些事件处理程序时,预期的方式是什么???我再次有一种苦涩的感觉,没有人真正生活在 OOP 中:-(

在这里给你玩:https://jsfiddle.net/sepfsvyo/1/

【问题讨论】:

标签: javascript oop event-handling


【解决方案1】:

事件侦听器回调中的this 将是触发事件的元素。如果您希望 this 成为您的类的实例,那么:

将函数绑定到类实例:

使用Function.prototype.bind,将创建一个新函数,其this 值将始终是您指定的值(类实例):

r.addEventListener('mouseover', this.OnEvent.bind(this));
//                                          ^^^^^^^^^^^

将函数包装在匿名函数中:

var that = this;
r.addEventListener('mouseover', function(ev) { that.OnEvent(ev); });
//                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

或者使用arrow function(所以不需要that):

r.addEventListener('mouseover', ev => this.OnEvent(ev));
//                              ^^^^^^^^^^^^^^^^^^^^^^

注意:正如下面评论中提到的,上述两种方法都将不同的函数传递给addEventListener(带有bind的那个创建了一个新函数,而异常函数显然是!== this.OnEvent)。如果您稍后要删除事件侦听器,则必须存储对该函数的引用:

var reference;
r.addEventListener('mouseover', reference = this.OnEvent.bind(this));
//                              ^^^^^^^^^^^^

或:

var reference;
var that = this;
r.addEventListener('mouseover', reference = function(ev) { that.OnEvent(ev); });
//                              ^^^^^^^^^^^^

然后你可以像这样删除事件监听器:

r.removeEventListener('mouseover', reference);

【讨论】:

  • 感谢您的绑定功能。我想正确的 oop 样式是在方法声明中绑定 this.OnEvent = OnEvent.bind(this); :-) 定义末尾的绑定将冻结(仅)Microsoft Edge 38.14393.0.0 :-( 请参见此处:jsfiddle.net/sepfsvyo/3
  • 如果您添加了几个这样的事件处理程序,您将如何删除其中一个?似乎使用匿名功能后,您失去了关闭 this.OnEvent 的能力。?
  • @AwokeKnowing 当我发布答案时,我打算添加一个关于它的注释,但觉得没有必要。现在我对此有了第二意见,我添加了注释。
  • @ibrahimmahrir 啊,这就是我所缺少的。所以在课堂上,window.addEventListener('message', this.msgref= function(e){that.handleMessage(e)}) 会起作用吗?
  • @AwokeKnowing 是的,如果 this 是类的实例(如果该代码行在方法或构造函数中,而不是在将其 this 绑定到其他东西的另一个函数中)。
【解决方案2】:

您实际上可以将对象作为 EventListener 回调返回,这样 JS 将在类中搜索 handleEvent 方法并相应地执行:

var myInstance = new myClass;
myInstance.addEventListener("mousedown",myInstance);

//  To remove the event you can follow the same pattern
myInstance.removeEventListener("mousedown",myInstance);

你必须以这种方式构建你的类:

class myClass {
   
    constructor(){
        //  Whatever this is supposed to do.
        //  You can also add events listener within the class this way :
        this.addEventListener("mousedown",this);
    }
    
    mouseDownEvent(e)(){
        //  Some action related to the mouse down event (e)
        console.log(e.target);
    }
    mouseMoveEvent(e)(){
        //  Some action related to the mouse move event (e)
    }
    mouseUpEvent(e)(){
        // Some action related to the mouse up event (e)
    }

    handleEvent(e) {
        switch(e.type) {
            case "mousedown":
                this.mouseDownEvent(e);
            break;
            case "mousemove":
                this.mouseMoveEvent(e);
            break;
            case "mouseup":
                this.mouseUpEvent(e);
            break;
        }
    }
}

来源:

https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38

https://www.thecssninja.com/javascript/handleevent

https://metafizzy.co/blog/this-in-event-listeners/

我发现这种方法更清晰,同时在 this 类中声明事件也非常明确。 希望我对某人有所帮助。

【讨论】:

【解决方案3】:

@ibrahimmahrir 的回答确实有效,但我想巩固几点。

正如许多 JavaScript 开发人员难以理解的那样,this 关键字是一个移动的目标。在传统的 OOP 语言中,对象方法是对象独有的,因此this 被定义为它所附加的对象。

JavaScript 函数更加混杂,可以附加到多个对象。在 JavaScript 中,this 指的是 当前 调用函数的对象,不一定是它最初附加的对象。

对于事件处理函数,调用对象是它所附加的元素,而不是原始对象;因此this 指的是元素。 通常的安全方法是将原始对象的引用存储在不同的变量中,通常称为that

oClass = new CClass();
function CClass() {
    var that = this;        //  a reference to the original object
    this.m_s = "hello :-/";
    this.OnEvent = OnEvent;
    var r = document.getElementById("test");
    r.addEventListener('click', this.OnEvent);

    function OnEvent() {
        alert(that);        //  this is now the object
        alert(that.m_s);    //  this works
    }
}

上面的 cmets 是我更新的 cmets。我还删除了 with 声明,该声明贡献不大,严重不鼓励。

哦,我已将事件更改为 click 以便于测试。

虽然我们对 this 感到困惑,但它并不一定是启动事件的元素。假设我们现在包含一个span

<div id="test">click <span>over</span> here</div>

单击span 将触发事件侦听器,即使您实际上并未单击它所附加的div。在这种情况下,事件会从 span 冒泡到 div。

这里的this 仅指带有事件监听器的div 元素。如果要引用span,则需要event.target

function OnEvent(event) {   //  include event parameter
    alert(this);            //  the element attached
    alert(event.target);    //  the element clicked
    alert(that);            //  this is now the object
    alert(that.m_s);        //  this works
}

这是一个更新的小提琴:https://jsfiddle.net/osk083xv/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-29
    • 2013-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多