TypeScript+HTML动态按钮事件绑定异常:仅最后一个按钮生效
动态创建按钮仅最后一个绑定点击事件的问题分析与解决
问题描述
使用TypeScript结合HTML动态生成按钮时,预期将ChoiceBox.choices中的键作为按钮文本、对应值作为点击函数,在目标div中生成按钮。实际运行后,按钮能成功创建,但只有最后一个按钮绑定了点击功能。
复现代码
index.html
<div id="ChoiceBox" style="position: absolute; right: 40%; top: 30vh; width: 300px; max-height: 450px; min-height: 50px; background-color: rgba(43, 42, 42, 1);"> </div>
index.ts
import { ChoiceBox } from "choiceBox" let cb = new ChoiceBox() let choices:Map<string, ()=>void> = new Map() choices.set("Option1", ()=>{console.log("Option1")}) choices.set("Option2", ()=>{console.log("Option2")}) choices.set("Option3", ()=>{console.log("Option3")}) cb.setChoices(choices) cb.open()
choiceBox.ts
export class ChoiceBox{ choices:Map<string, ()=>void> = new Map() div:HTMLElement constructor(){ let d = document.getElementById("ChoiceBox") if(!d){ throw new Error("no element found") } this.div = d } setChoices(choices: Map<string, ()=>void>){ this.clear() this.choices = choices console.log(choices) } clear(){ this.choices = new Map() this.div.innerHTML = `` } open(){ //... this.choices.forEach((v, k)=>{ this.createButton(k, v) }) } private createButton(k:string, f:()=>void){ let inner = `<a href ="#" id="${k+this.div.id}_test" class="btn devbtn">${k}</a><br>` this.div.innerHTML += `${inner}` //bind test to button let t = document.getElementById(k+this.div.id+"_test") if(!t){ console.error("No button with id of: "+ k+this.div.id+"_test") }else{ //console log for debugging //console.log(t) t.addEventListener("click", f) } } }
问题原因
使用this.div.innerHTML += ${inner}``时,本质是先读取当前div的HTML内容,拼接新的HTML片段后重新赋值给innerHTML。这个过程会销毁原有的所有DOM节点,然后重新渲染整个div的内容。之前循环中创建的按钮虽然结构被重新生成,但它们之前绑定的事件监听会全部丢失。只有最后一次循环时,新生成的按钮是在重新渲染后的DOM上直接绑定事件,所以只有它能正常触发点击。
而insertAdjacentHTML方法会直接将新的HTML片段插入到DOM的指定位置,不会破坏已存在的DOM节点,因此之前创建的按钮及其事件监听都能完整保留,所有按钮的点击事件都能正常工作。
解决方案
将createButton方法中的this.div.innerHTML += ${inner}``替换为insertAdjacentHTML,如果需要保持原Map的顺序,推荐使用beforeend作为插入位置(若用afterbegin会倒序生成按钮):
private createButton(k:string, f:()=>void){ let inner = `<a href ="#" id="${k+this.div.id}_test" class="btn devbtn">${k}</a><br>` // 替换为insertAdjacentHTML,避免重新渲染原有DOM this.div.insertAdjacentHTML("beforeend", inner) //bind test to button let t = document.getElementById(k+this.div.id+"_test") if(!t){ console.error("No button with id of: "+ k+this.div.id+"_test") }else{ t.addEventListener("click", f) } }
内容的提问来源于stack exchange,提问作者NeVo
相关产品推荐
相关产品推荐

