【问题标题】:How to bind a click event to a handler that is in a class?如何将单击事件绑定到类中的处理程序?
【发布时间】:2021-07-29 06:30:51
【问题描述】:

当我尝试将类方法绑定到事件时,我收到错误消息“无法读取未定义的属性‘绑定’。”这告诉我,我绑定的对象是未定义的,但它确实存在并且可以调用。考虑以下代码:

someClass = ({

    event: function(workObject, event, action) {
        workObject.addEventListener(event, window[action].bind(null, arguments[3]));
    },

    methodEventHandler: function(param) {
        // Do Something
    },
});


function someEventHandler(param) {
    // Do Something
}

现在当我尝试像这样绑定事件监听器时:

someClass.event(workObject, "click", "someEventHandler", someParameter);

它有效。但是,当我尝试以这种方式绑定它时:

someClass.event(workObject, "click", "someClass.methodEventHandler", someParameter);

它因上述错误而失败。我的问题是为什么?我认为它与范围有关,因为常规函数在全局上下文中并且有效,但不在全局范围内的方法失败。我使用包装函数解决了这个问题,但我真的很想知道它为什么会表现出这种行为。我这样做是为了可以使用函数名作为字符串绑定事件处理程序。使动态生成网页时更容易处理。我唯一能想到的是事件处理程序的名称需要采用某种格式,但我不确定它是什么样的。有什么见解吗?

我对此进行了一些研究,并发现了很多关于如何使用各种框架(如 jQuery、AngularJS 和 Node.js)将处理程序绑定到事件等的问题和答案。其中一些在这里:

How to access the correct `this` inside a callback?

Bind click event to open a link in new tab

Bind a click event to a method inside a class

【问题讨论】:

  • 我认为您的问题与this 绑定无关 - 它更简单。您失败的第二个示例最终尝试读取window["someClass.methodEventHandler"]bind 属性,这与window.someClass.methodEventHandler 完全不同。您要么需要编写一个函数来从点分隔的字符串动态访问对象的嵌套属性并使用它 - 或者几乎可以肯定有更好的方法来实现您真正想要的。 (换句话说,这看起来像一个XY problem。)
  • @RobinZigmond 我不会称其为 XY 问题,因为它在大多数情况下都有效。正如我所说,我认为这是一个范围界定问题。我这样做是为了在解析 JSON 并呈现页面时动态处理事件绑定。正如我所提到的,我使用了一个包装函数来解决这个问题。但是,正如您所指出的,两者并不相同。我不知道为什么我之前没有看到。

标签: javascript html events binding


【解决方案1】:

someEventHandler 是一个全局函数,因此它可以称为window["someEventHandler"],因为someEventHandler 成为window 的属性。

someClass.methodEventHandler 不是全局的,因此window["someClass.methodEventHandler"] 不会返回任何内容,因为没有window.someClass.methodEventHandler 属性,因此无法调用.bind()

您可以致电:window["someClass"].methodEventHandler

someClass = ({

    event: function(workObject, event, action) {
        workObject.addEventListener(event, action.bind(null, arguments[3]));
    },

    methodEventHandler: function(param) {
        console.log(param);
    },
});

function someEventHandler(param) {
    // Do Something
}

const workObject = document.querySelector("div");
someClass.event(workObject, "click", window["someClass"].methodEventHandler, "x");
<div>Click Me</div>

【讨论】:

  • 好吧,虽然someClass 可能不存在于全局对象上,但眼前的明显问题是window["someClass.methodEventHandler"]window.someClass.methodEventHandler 并不相同。
  • @MinusFour 实际上,someClass 确实作为全局存在,但正如您所说(正如我指出的那样),"someClass.methodEventHanlder" 被作为单个属性而不是属性的属性传递.这就是为什么我说你可以打电话给:window["someClass"].methodEventHandler
【解决方案2】:

字符串可能会出现问题并且更难调试,但解析它是继续使用字符串的一种选择。除了字符串问题,您还需要使用var,因为letconst 不会成为window 对象的一部分。

const someParameter = "placeholder";

var someClass = ({ // note the use of `var` :/

  event: function(workObject, event, action) {
    const func = action.split(".")
      .reduce((obj, name) => obj[name], window); // walk down the window object

    workObject.addEventListener(event, func.bind(null, arguments[3]));
  },

  methodEventHandler: function(param) {
    console.log("I am methodEventHandler");
  },
});


function someEventHandler(param) {
  console.log("I am someEventHandler");
}

const workObject = document.querySelector("button"); // just the button

someClass.event(workObject, "click", "someEventHandler", someParameter);

someClass.event(workObject, "click", "someClass.methodEventHandler", someParameter);
<button>Click me for stuff</button>

【讨论】:

  • 我喜欢你的解决方案。它是否适用于两个以上级别的属性?像 someClass.someSubClass.someMethod 之类的东西?
  • 是的,它是递归的,因此任何数量的子级别都是可能的。
  • 由于我正在处理其他事情,我需要几天时间才能回到那个代码。但如果它适用于我的用例,我会将您的解决方案标记为答案。
  • 我终于开始测试这个了。它将调用有问题的函数,但上下文位于被单击的节点中。当我在处理程序方法中执行 console.log(this) 时,我得到的是按钮控件而不是处理程序所在的对象。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-20
  • 1970-01-01
  • 2015-12-07
  • 2020-07-04
  • 1970-01-01
  • 2010-09-12
  • 1970-01-01
相关资源
最近更新 更多