动态创建元素绑定事件监听器重复触发问题求助
问题:多次创建图形元素后,点击触发重复输出
每次点击提交按钮创建新的图形元素后,点击任意元素都会触发和元素总数相同次数的属性输出。比如创建红色正方形、绿色圆形两个元素后,点击任一都会输出两组属性值。原代码每次点击submit按钮时,都会给shapeContainer添加新的点击事件监听器,尝试过检查监听器存在性、调用removeEventListener均无效。
原代码如下:
// Utility function function onEvent(event, selector, callback) { return selector.addEventListener(event, callback); } onEvent('click', submit, function() { let shape = new Shape(shapeInput.value, colorInput.value); shape.createShape(); output.innerHTML = `${shape.shapeColor} ${shape.shapeName} ` shapeContainer.addEventListener('click', function (e) { let target = e.target; if (target.matches('div')) { output.innerHTML = `${shape.getInfo()}`; } }); });
问题原因
你每次点击submit按钮时,都会给shapeContainer新增一个独立的点击监听器,这些监听器会叠加存储。当你点击图形元素时,所有已添加的监听器都会依次执行,导致输出次数等于创建过的元素总数。另外,你用的是匿名函数作为监听器,removeEventListener无法匹配到对应的函数,所以删除操作无效。
解决方法
方法一:使用事件委托(推荐)
把shapeContainer的点击监听器移到submit事件外面,只绑定一次。创建元素时,把形状的信息存储在元素本身(比如dataset属性或直接挂载实例),点击时从元素上读取信息即可。
修改后代码:
// Utility function function onEvent(event, selector, callback) { return selector.addEventListener(event, callback); } // 仅绑定一次容器的点击事件,放在submit事件外部 onEvent('click', shapeContainer, function (e) { let target = e.target; if (target.matches('div')) { // 从元素的dataset中读取预存的形状信息 output.innerHTML = `${target.dataset.color} ${target.dataset.shape}`; // 如果要调用getInfo方法,可直接挂载shape实例到元素上: // output.innerHTML = target.shape.getInfo(); } }); onEvent('click', submit, function() { let shape = new Shape(shapeInput.value, colorInput.value); let shapeElement = shape.createShape(); // 将形状信息存入元素的dataset shapeElement.dataset.shape = shape.shapeName; shapeElement.dataset.color = shape.shapeColor; // 或者直接挂载shape实例到元素上(方便调用类方法) // shapeElement.shape = shape; output.innerHTML = `${shape.shapeColor} ${shape.shapeName} `; });
方法二:复用具名监听器(不推荐)
如果必须在submit事件内处理,可将监听器抽成具名函数,每次添加前先移除旧的监听器。这种方法需要维护监听器的引用,不如事件委托优雅。
修改后代码:
// Utility function function onEvent(event, selector, callback) { return selector.addEventListener(event, callback); } // 声明全局变量存储监听器引用 let shapeClickHandler; onEvent('click', submit, function() { let shape = new Shape(shapeInput.value, colorInput.value); shape.createShape(); output.innerHTML = `${shape.shapeColor} ${shape.shapeName} `; // 移除之前的监听器 if (shapeClickHandler) { shapeContainer.removeEventListener('click', shapeClickHandler); } // 重新定义监听器,捕获当前的shape实例 shapeClickHandler = function (e) { let target = e.target; if (target.matches('div')) { output.innerHTML = `${shape.getInfo()}`; } }; // 添加新的监听器 shapeContainer.addEventListener('click', shapeClickHandler); });
说明
事件委托是更优方案,只需要绑定一次监听器,避免了重复绑定的问题,同时性能更好、逻辑更清晰。
内容的提问来源于stack exchange,提问作者Apricosma
相关产品推荐
相关产品推荐

