JavaScript将事件附加到动态呈现的元素

pau*_*dru 10 html javascript events dom addeventlistener

我有2个文件index.htmlall.js.

all.js

(function(){
    if (document.getElementById('btn1')){
        document.getElementById("btn1").addEventListener("click", displayMessage);
    }

    function displayMessage(){
        alert("Hi!");
    }
})()
Run Code Online (Sandbox Code Playgroud)

的index.html

示例1: - 工作示例

<button id="btn1">Hit me !</button>
<script type="text/javascript" src="all.js"></script>
Run Code Online (Sandbox Code Playgroud)

示例2: - 不工作示例

<button id="btn2" onclick="show()">Show</button>
<script type="text/javascript">
    function show(){
        var btn = document.createElement("BUTTON");
            btn.setAttribute("id", "btn1");
            btn.innerHTML = "Hit me !";
        document.getElementById("btn2");
        document.body.insertBefore(btn, btn2);
    }
</script>
<script type="text/javascript" src="all.js"></script>
Run Code Online (Sandbox Code Playgroud)

描述:在第一个示例中btn1,立即渲染并将事件附加到其中.在第二个示例btn1中,仅当您单击时才会呈现btn2,之后如果单击btn1则不会发生任何事情.

如何在btn1不改变的情况下附加活动all.js

这是整个代码

注意:不要问我为什么尝试做这样的事情,不要问我为什么我不被允许改变,all.js因为这个例子是一个简化的例子,在我的情况下all.js文件中包含数千行,其中包含大量事件元素.JavaScript解决方案意味着我不允许使用其他外部库.

更新:接受验收

我得到了很多不同的答案.你们中的一些人更努力地解决这个问题,你们中的一些人工作得更少,但好的是我决定了.我得到了一些有效的解决方案,但其中一些只针对这个特殊情况工作而没有考虑到我的真正的all.js文件有4029行代码,一些解决方案建议使用事件委托我同意但不幸的是我不能改变我的所有.js文件现在.最后我选择了最好的解决方案,我也考虑过谁首先回答,我也考虑到了很多工作,等等.不管怎样,我要实现的解决方案是合并2个解决方案,Aruna和Vlacav的解决方案(+我自己的一些改变),这里是:

function show(){
    var btn = document.createElement("BUTTON");
        btn.setAttribute("id", "btn1");
        btn.innerHTML = "Hit me !";
    document.getElementById("btn2");
    document.body.insertBefore(btn, btn2);
    resetJs('all.js');
}

function resetJs(path) {
    var scripts = document.getElementsByTagName('script')
        , newScript = document.createElement("script");

    newScript.src = path + "?timestamp=" + Math.round(new Date().getTime()/1000);   

    for (var i = 0; i < scripts.length; ++i) {      
        var srcUrl = scripts[i].getAttribute('src');
        if (srcUrl && srcUrl.indexOf(path) > -1) {                 
           scripts[i].parentNode.replaceChild(newScript, scripts[i]);
        }           
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,我无法分享声誉,我只能将其分配给其中一个,我想把它交给Vlacav,因为他是第一个发布我正在搜索的解决方案的人,他也做了一些改变.当我让他这样做的时候.我还评价了Aruna的答案,因为他应得的.

我已经在这里的一个单独的分支上发布了解决方案,并且我还使用我收到的其他解决方案在此repo上创建了其他几个分支,因为它们在其他情况下可能很有用.

Vac*_*tny 1

您必须all.js在每个新元素之后重新加载。这是我能想到的唯一解决方案。

创建元素的操作之后,您将调用一个函数:

// removed, see Edit
Run Code Online (Sandbox Code Playgroud)

这将重新评估all.js,并且侦听器all.js将附加到 DOM 中的元素。

这是非常低效的解决方案,因此如果您进行多次动态更新,请all.js在所有更新结束时重新加载,而不是在每次更新后重新加载。

这也意味着,一些有状态变量all.js可能会被重新初始化,计数器可能会重置为零等。

我个人永远不会做这种小猪修补,但如果你别无选择,请尝试一下。

编辑

经过测试的工作代码

function reloadAllJs(){
    var path = "all.js"
        , oldScript = document.querySelector("script[src^='" + path + "']")
        , newScript = document.createElement("script")
        ;       
    // adding random get parameter to prevent caching
    newScript.src = path + "?timestamp=" + Date.now();
    oldScript.parentNode.replaceChild(newScript, oldScript);
}
Run Code Online (Sandbox Code Playgroud)

querySelector并且Date.now()兼容IE9+,为了支持IE8-必须修改代码。

更新

IE8-兼容版本(在IE7中测试)

function reloadAllJs(){
    var path = "all.js"
        , scripts = document.getElementsByTagName("script")
        , oldScript
        , newScript = document.createElement("script")
        ;

    for(var i = 0, s; s = scripts[i]; i++) {
        if (s.src.indexOf(path) !== -1) {
            oldScript = s;
            break;
        }
    }

    // adding random get parameter to prevent caching
    newScript.src = path + "?timestamp=" + (new Date()).getTime();
    oldScript.parentNode.replaceChild(newScript, oldScript);
}
Run Code Online (Sandbox Code Playgroud)

注意: IE8不支持addEventListener(它有非常相似的方法attachEvent),因此如果您all.js依赖addEventListener,则all.js仅兼容IE9+。