使用纯 JavaScript 取消绑定事件委托的多参数事件侦听器

Unbind an event-delegated, multi-argument event listener with plain JavaScript

我有一个问题已被不同的答案触及,但据我所知,其细节与我要求的不同。我希望取消绑定一个事件侦听器,但有一些限制。

  1. 事件委托给子项
  2. 事件侦听器采用次要参数
  3. 事件函数必须在 if 子句中解除自身绑定

我把它简化为下面的例子。当您单击第一个按钮时,您可以单击任何其他按钮。第一个按钮的文本将相应更改。但是,因为永远不会删除事件侦听器,所以您可以不断更改您的“选择”并单击任何其他按钮。我想要的是,当您单击第一个按钮时,您只能单击另一个按钮,然后删除侦听器。实际上,这意味着如果你想改变你的“选择”,你首先需要再次点击第一个按钮,然后重新附加事件监听器。

const btn = document.querySelector("button")
const div = document.querySelector("div")

function listen(text, evt) {
  if (evt.target.tagName == "BUTTON") {
    btn.textContent = text + evt.target.textContent
    // Unbind listen function from div so that that `btn` must be clicked every time
    // you want to change the selected button.
    // The following does NOT work.
    div.removeEventListener("click", this)
  }
}

btn.addEventListener("click", evt => {
  div.addEventListener("click", listen.bind(this, "I selected "))
})
<button>Click me</button>

<div>
  <button>Button 1</button>
  <button>Button 2</button>
  <button>Button 3</button>
</div>

如您所见,我尝试删除事件侦听器,但由于我使用了 bind(使第二个参数起作用),我认为 this 不再是相同的引用。我怎样才能在不依赖其他库的情况下让它工作?

this 无论如何都不会奏效。它通常指的是处理程序绑定到的 DOM 元素,而不是函数本身。

你可以用不同的方式解决这个问题。一种可能性是引入一个用于绑定事件处理程序的帮助程序,这反过来会将“删除”功能传递给处理程序。

const btn = document.querySelector("button")
const div = document.querySelector("div")

function addEventListener(element, type, handler, ...args) {
  const handlerWrapper = function(event) {
    return handler.call(this, event, remove);
  };
  const remove = () => element.removeEventListener(type, handlerWrapper);

  element.addEventListener(type, handlerWrapper, ...args);
}


function listen(text, evt, remove) {
  if (evt.target.tagName == "BUTTON") {
    btn.textContent = text + evt.target.textContent
    // Unbind listen function from div so that that `btn` must be clicked every time
    // you want to change the selected button.
    remove();
  }
}

btn.addEventListener("click", evt => {
  addEventListener(div, "click", listen.bind(this, "I selected "))
})
<button>Click me</button>

<div>
  <button>Button 1</button>
  <button>Button 2</button>
  <button>Button 3</button>
</div>


话虽如此,如果您总是希望最多执行一个事件处理程序一次,请在绑定事件处理程序时传递once: true option