You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 23:35:15