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

时间:2016-12-16 19:06:39

标签: javascript html events dom addeventlistener

我有2个文件index.htmlall.js

  

all.js

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

    function displayMessage(){
        alert("Hi!");
    }
})()
  

的index.html

示例1: - 工作示例

<button id="btn1">Hit me !</button>
<script type="text/javascript" src="all.js"></script>

示例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>

描述: 在第一个示例中,btn1立即被渲染,事件被附加到它上面。在第二个示例中,btn1仅在您点击btn2时呈现,之后如果您点击btn1则不会发生任何事情。

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

以下是整个代码repository

注意:不要问我为什么尝试这样做,不要问我为什么不允许我更改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]);
        }           
    }
}

不幸的是,我不能分享声誉,我只能把它给它们中的一个,我想把它交给Vlacav,因为他是第一个发布我正在寻找的解决方案的人,他也做了当我让他这样做时,它会有一些变化。我也评价了Aruna的答案,因为他应得的。

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

11 个答案:

答案 0 :(得分:3)

向元素btn添加onclick属性。

&#13;
&#13;
function displayMessage() {
  alert("Hi!");
}

function show() {
  var btn = document.createElement("BUTTON");
  btn.setAttribute("id", "btn1");
  btn.innerHTML = "Hit me !";
  btn.onclick = displayMessage;
  document.getElementById("btn2");
  document.body.insertBefore(btn, btn2);
}
&#13;
<button id="btn2" onclick="show()">Show</button>
&#13;
&#13;
&#13;

答案 1 :(得分:2)

问题代码:

问题是您在加载// dealDetailReducer // const initialStateOfDealDetail = fromJS({}) let details = {} action.data.details.map((detail) => { details[detail.id] = detail }) return state.merge(fromJS(details)) ... // dealTrackLogsReducer // initialStateOfDealTrackLogs = fromJS({}) if (state.get(action.data.id)) { // has id in state, update return state.withMutations(s => s.update( action.data.id, trackLog => trackLog.merge(fromJS(action.data.trackLogs)) ) ) } // no id in state, just set, id : data return state.set(action.data.id, fromJS(action.data) 文件时执行的块中使用addEventListener。因此,它会在调用all.js之前为页面中显示的所有ID EventListener添加( btn1 )。当您调用show()添加标识为all.js的新元素时,all.js无法为此元素添加btn1

所以,如果你要添加新元素,那么你必须再次使用addEventListener

解决方案1:

在元素插入到body

之后使用addEventListner
EventListner

我不认为您正在寻找此解决方案。

解决方案2: 如果你也在使用JQuery Library,那么它很容易,但你也使用纯javascript。

您只需在插入元素后重新加载all.js.

以下是您的index2.html,其中包含jquery的完美解决方案。如果你没有Jquery,那么用javascript重新加载你的all.js。

function show() {
  var btn = document.createElement("BUTTON");
  btn.setAttribute("id", "btn1");
  btn.innerHTML = "Hit me !";
 //  btn.onclick = displayMessage;  // you can use this also.
  document.getElementById("btn2");
  document.body.insertBefore(btn, btn2);
//document.getElementById("btn1").addEventListener("click", displayMessage); //like this
}

仅编辑Javascript

<html>
<body>
<button id="btn2" onclick="show()">Show</button>
<script type="text/javascript" src="../jquery.js"></script>
<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);

        //HERE YOU HAVE TO RELOAD YOUR all.js
        reload_js('all.js');    
    }
</script>
<script type="text/javascript">
    function reload_js(src) {
            $('script[src="' + src + '"]').remove();
            $('<script>').attr('src', src).appendTo('head');
        }
</script>
<script type="text/javascript" src="all.js"></script>
</body>
</html>

有关重新加载的更多选项。了解如何重新加载JavaScript文件here

以下是更新repository

答案 2 :(得分:1)

您还应该能够使用事件委派。

如果新的DOM元素都放在ID为“dynamic”的div中,您可以使用以下内容:

document.getElementById("dynamic").addEventListener("click",function(event){
    var target = event.target;
    console.log(target); 
});

这将捕获动态 div中发生的所有点击事件,您可以测试事件目标以确定单击了哪个元素。使用这种方法只需要一个事件处理程序。

答案 3 :(得分:1)

在加载all.js时,它正在寻找元素btn1并尝试附加事件(如果它出现)。由于元素是动态创建/加载的,因此在加载all.js时它不会附加到事件,因此元素在创建后没有附加到此事件。

根据我与你的讨论,我了解你的以下事项:

  • 您无法更改all.jsindex.html,因为两者都已维护 由您的客户和受保护。
  • index.html正在调用类似initHeader()的方法 它反过来用ajax请求调用web服务。
  • 然后index.html正在解析ajax调用返回的json 实际上你回来了,里面可以有html和js。
  • 然后将解析后的json(html)附加到页面上, document.getElementByID('nav').innerHtml = ajaxResponse;

根据上述理解,您可以更改/执行某项操作的唯一地方是您从网络服务返回的内容。

因此,我可以向您推荐以下脚本,您可以在最终添加到Web服务响应中,这将从all.js中删除现有index.html文件,并在ajax响应后再次加载该文件。

<script type="text/javascript">
    function reattachEvents() {
      // Remove 'all.js'
      var scripts = document.getElementsByTagName('script')
      for (var i = scripts.length - 1; i >= 0; i--){ // Loop backwards
         if (scripts[i]) {
            var srcUrl = scripts[i].getAttribute('src');
            if(srcUrl && srcUrl.indexOf('all.js') > -1) {
               scripts[i].parentNode.removeChild(scripts[i]);
            }
         }
      }

      // Load 'all.js'
      var head= document.getElementsByTagName('head')[0];
      var script= document.createElement('script');
      script.type= 'text/javascript';
      script.src= 'all.js';
      head.appendChild(script);
   }

   reattachEvents();
</script>

注意:我已在您的repository中创建了一个新的拉取请求并进行了此更改

答案 4 :(得分:1)

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

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

// removed, see Edit

这将重新评估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);
}

querySelectorDate.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);
}

注意: IE8不支持addEventListener(它的方法attachEvent非常相似),所以如果all.js依赖addEventListener,然后all.js只与IE9 +兼容。

答案 5 :(得分:0)

每次按下按钮时都可以使用jQuery加载脚本

<button id="btn2" onclick="show()">Show</button>
<script
  src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>
<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);
        jQuery.getScript("all.js");
    }
</script>

答案 6 :(得分:0)

我认为你应该在初始加载时用JS或HTML

创建元素

请检查这些examples

示例1:

<button id="btn2" onclick="show()">Show</button>
<script type="text/javascript">
  function initHiddenButton(id, text) {
    var btn = document.createElement("BUTTON");
    btn.setAttribute("id", id);
    btn.innerHTML = text;
    btn.style.display = 'none';
    document.body.appendChild(btn);
  }
  initHiddenButton("btn1", "Hit me !");
    function show(){
      var btn1 = document.getElementById("btn1");
      var btn2 = document.getElementById("btn2");
        btn1.style.display = 'inline-block';
        document.body.insertBefore(btn1, btn2);

        //others code...
    }
</script>
<script src="all.js"></script>

示例2:

<button id="btn2" onclick="show()">Show</button>

<!-- hidden buttons -->
<button id="btn1" onclick="show()" style="display: none;">Hit me !</button>

<script type="text/javascript">
    function show(){
      var btn1 = document.getElementById("btn1");
      var btn2 = document.getElementById("btn2");
        btn1.style.display = 'inline-block';
        document.body.insertBefore(btn1, btn2);

        //others code...
    }
</script>
<script src="all.js"></script>

答案 7 :(得分:0)

使用纯粹的js你可以这样做:

document.querySelector('body').addEventListener('click', function (event) {
    if (event.target.id !== 'btn1') {
        return; //other element than #btn1 has been clicked
    }

    //Do some stuff after clicking #btn1
});

如果你可以使用jQuery,它有“on”方法,可以像下面那样调用

$('body').on('click', '#btn1', function(event) {
    // do something when #btn has been clicked
});

你是绑定侦听到body(或任何其他包含#btn1的元素)的侦听器,如果它已经被trigered,那么第二个参数过滤器如果它已经从#btn1被触发,如果不是,它将不会触发回调。

在那种情况下,当登记观察者时,“on”方法的第二个参数中的元素匹配选择器不一定存在。

您可以在jQuery on documentation

中详细了解相关信息

快乐的黑客攻击!

答案 8 :(得分:-1)

使用MutationObserver收听dom更改并将事件添加到addedNodes

&#13;
&#13;
function displayMessage(){
  alert('Hi!');
}

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

(new MutationObserver(mutations => {
  mutations
    .reduce((prev, cur) => prev.concat(Array.from(cur.addedNodes)), [])
    .filter(node => node.id === "btn1")
    .forEach(button => button.addEventListener('click', displayMessage));
})).observe(document.body, {childList: true, subtree: true})
&#13;
<button id="btn2" onclick="show()">Show</button>
&#13;
&#13;
&#13;

更新:Monkey Patch!

&#13;
&#13;
// your hack

// listen for all nodes
(new MutationObserver(mutations => {
  mutations
    .reduce((prev, cur) => prev.concat(Array.from(cur.addedNodes)), [])
    .forEach(replicateEvents);
})).observe(document.body, {childList: true, subtree: true})

// override addEventListener
const initialEvents = {};
const oldAddListener = Element.prototype.addEventListener;
Element.prototype.addEventListener = function(type, cb) {
  if(this.id) {
    var events = initialEvents[this.id] || (initialEvents[this.id] = {types: {}});
    if(!events.types[type]){
      events.target = this;
      events.types[type] = [cb];
    } else if(events.target === this) {
      events.types[type].push(cb);
    }
  }
  oldAddListener.apply(this, Array.from(arguments));
};

function replicateEvents(node){
  let events = initialEvents[node.id];
  if(!events) return;
  Object.keys(events.types)
  .forEach(type => events.types[type].forEach(cb => oldAddListener.call(node, type, cb)));
}


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



// all.js

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

    function displayMessage(){
        alert("Hi!");
    }

    function displayMessage2(){
      console.log("Hi2!");
    }
})()
&#13;
<button id="btn2" onclick="show()">Show</button>
<br />
<button id="btn1">Some initial button</button>
&#13;
&#13;
&#13;

答案 9 :(得分:-1)

在您的情况下displayMessage() btn1处理程序将尝试在btn1注册DOM之前首先注册,当用户点击btn2 btn1时元素将在DOM中注册。因此不会触发任何处理程序。所以,请在注册btn1之后注册处理程序。

<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);
    btn.addEventListener("click", displayMessage);
}
</script>
<script type="text/javascript" src="all.js"></script>

答案 10 :(得分:-1)

创建一种方法,将事件列表器附加到 all.js 文件中的按钮

   function attachClickEvent(elementId, event){
      var element = document.getElementById(elementId);
      if (element ){
        element.addEventListener("click", event);
      }    
    }

    function displayMessage(){
        alert("Hi!");
    }

的index.html

<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);

        //now call attachClickEvent function to add event listner to btn1
        //(both displayMessage() and attachClickEvent() functions are loaded now)
        attachClickEvent('btn1', displayMessage);
    }
</script>
<script type="text/javascript" src="all.js"></script>