首页 > 解决方案 > 未将侦听器添加到创建的 div

问题描述

所以我有以下名为 addPanels() 的函数,它为每个具有“手风琴”类的元素添加一个事件侦听器

function addPanels(){
    var acc = document.getElementsByClassName("accordion");
    var i;

    for (i = 0; i < acc.length; i++) {
        acc[i].addEventListener("click", function() {
        var panel = this.nextElementSibling;
        if (panel.style.display === "block") {
            panel.style.display = "none";
        } else {
            panel.style.display = "block";
        }

        var arrow = this.children[0].children[0];

        if (panel.style.display === "block") {
            arrow.src = "Iconos/arrow_up.png";
        } else {
            arrow.src = "Iconos/arrow_down.png";
        }
    });
  } 
}

然后我有这个:

$(document).ready(function() {
    api.routines.getRoutines().done(function(data) {
       $.each(data, function(i, item){
       var list = document.getElementById("routines_list");
       var acc = document.createElement("div");
       acc.setAttribute("class", "accordion");
       var panel = document.createElement("div");
       panel.setAttribute("class", "panel");
        //more stuff 
      });
    acc.appendChild(panel);
    list.appendChild(acc);  
    // ...
    });
    addPanels(); //WORKS ONLY WITH PREVIOUS ACCORDIONS NOT WITH THE NEW ONE! WHY??

}).fail(function(jqXHR, textStatus, errorThrown) {
    //do sth
    });
});

addPanels 似乎只将侦听器添加到具有类 ACCORDION 且未从 API 加载的元素(它们已经在 HTML 中)。为什么会这样?

标签: javascriptjqueryhtml

解决方案


因为在添加侦听器时,您的新元素并不存在。

api.routines.getRoutines().done指定一个应该在未来某个时间点运行的回调函数——我们不知道什么时候会运行。因此,当该 API 开始其进程时,addPanels();会立即调用 next,它会设置事件处理程序,但getRoutines()尚未完成,因此您最终只能在getRoutines()完成之前存在的面板上设置处理程序。这就是异步处理的工作原理。

要解决此类问题,您需要使用事件委托,即在从一开始就存在的对象上设置事件处理程序,然后通过事件冒泡捕获由后代元素发起的事件。处理事件后,您检查事件的来源并查看它是否符合您想要的条件。这样,创建元素的时间就无关紧要了。

这是一个简化的示例:

// Set up a click event handler on the document (which all descendant element events will bubble up to)
document.addEventListener("click", function(evt){
  console.log("Click event handled!");
 
  // Is the clicked element NOT the document?
  if(evt.target.classList){
    // Check for dynamically created elements that were given
    // the "dynamic" class upon creation (you can use any criteria you want here)
    if(evt.target.classList.contains("dynamic")){
      console.log("Click event for dynamically created element, " + evt.target.nodeName + " handled by document");
    }
  }
});

// Now, we'll create some elements and inject them into the DOM AFTER the event handler
// has been set up. Also, these elements were not specifically set up with event handlers.
var div = document.createElement("div");
div.classList.add("dynamic");
div.textContent = "Click Me";
document.body.appendChild(div);

var btn = document.createElement("input");
btn.classList.add("dynamic");
btn.type = "button";
btn.value = "Click Me!";
document.body.appendChild(btn);
.dynamic { font-weight:bold; color:#800080; }


推荐阅读