如何删除事件处理程序的绑定版本?



我有一个问题与我的绑定事件,我想删除每个事件时,更改事件被触发。没有循环也能正常工作。

this.objectInstance.on('selected', () => {
document.querySelectorAll('select').forEach((el) => {
this.test = this.fontFamilyHandler.bind(this, el.getAttribute('id'));
el.addEventListener('change', this.test);
}) 
});
this.objectInstance.on('deselected', () => {
document.querySelectorAll('select').forEach((el) => {
el.removeEventListener('change', this.test);
})
});
fontFamilyHandler = (key, evt) => {
this.objectInstance.set(key, evt.target.value);
this.canvas.requestRenderAll();
}

你知道为什么它不工作吗?

从上面的注释…

"我们需要查看周围的代码,以便了解OP正在处理的实际this…我特别好奇的是fontFamilyHandler = (key, evt) => { /* ... */ }这个函数可以自由浮动而不是第3行突然看到this.fontFamilyHandler.bind(this, el.getAttribute('id'));">

在此之前,下一个提供的答案假设了一个假想的MyType类的类语法/系统。

this.fontFamilyHandler.bind(this, el.getAttribute('id'));

…完全没有必要,因为OP已经访问了这个处理程序中的event.target,它等于上面的el

fontFamilyHandler = (key, evt) => {
this.objectInstance.set(key, evt.target.value);
this.canvas.requestRenderAll();
}

因此,没有必要绑定每个元素的id,因为该属性可以通过evt.target.id访问,就像已经通过evt.target.value访问的值一样。

最后的代码将归结为更可读的东西,如…

//class MyType {
//  constructor() {
//    this.objectInstance = { on: () => {} };
this.objectInstance.on('selected', () => document
.querySelectorAll('select')
.forEach(el =>
el.addEventListener('change', this.fontFamilyHandler)
)
);
this.objectInstance.on('deselected', () => document
.querySelectorAll('select')
.forEach(el =>
el.removeEventListener('change', this.fontFamilyHandler)
)
);
this.fontFamilyHandler = ({ target }) => {
this.objectInstance.set(target.id, target.value);
this.canvas.requestRenderAll();
};
//  }
//}

在每次迭代中,都用下一个侦听器覆盖this.test。因此,最后,this.test将包含附加在上的最后一个侦听器,因此它只能删除该侦听器。

我可以在这里看到两个解决方案:

  1. 您可以保存所有侦听器函数,然后再次删除它们,将元素映射到侦听器:
this.objectInstance.on('selected', () => {
this.test = new WeakMap();
document.querySelectorAll('select').forEach((el) => {
const handler = this.fontFamilyHandler.bind(this, el.getAttribute('id'));
this.test.set(el, handler);
el.addEventListener('change', handler);
}) 
});
this.objectInstance.on('deselected', () => {
document.querySelectorAll('select').forEach((el) => {
if (!this.test.has(el)) continue;
el.removeEventListener('change', this.test.get(el));
})
});

(注意:仅使用数组将不稳定,因为如果文档中<select>元素的数量发生变化,索引将不同步。)

(注意:它也可以与Map而不是WeakMap一起工作,但后者允许从DOM中删除的任何<select>元素的早期垃圾收集。)

  1. 对所有元素使用相同的函数,而不是每次都绑定。由于您访问的是放置侦听器的相同元素,并且它是<select>,这意味着change事件将只来自<select>本身,因为它不能有任何能够发送change事件的子事件,使用event.target.id也可以工作:
const eventHandler = event => this.fontFamilyHandler(event.target.id, event);
this.objectInstance.on('selected', () => {
document.querySelectorAll('select').forEach((el) => {
el.addEventListener('change', eventHandler);
}) 
});
this.objectInstance.on('deselected', () => {
document.querySelectorAll('select').forEach((el) => {
el.removeEventListener('change', eventHandler);
})
});

(注意:el.idel.getAttribute('id')更简单,并且做同样的事情-参见Element#id)

  1. 或者,您可以直接使用fontFamilyHandler并将其更改为从元素本身读取ID。
this.objectInstance.on('selected', () => {
document.querySelectorAll('select').forEach((el) => {
el.addEventListener('change', this.fontFamilyHandler);
}) 
});
this.objectInstance.on('deselected', () => {
document.querySelectorAll('select').forEach((el) => {
el.removeEventListener('change', this.fontFamilyHandler);
})
});
// I removed the `key` argument here
this.fontFamilyHandler = evt => {
this.objectInstance.set(evt.target.id, evt.target.value);
this.canvas.requestRenderAll();
}

最新更新