如何在innerHTML内获取click事件?


12

我有一个动态创建的布局:

for (let i = 1; i < 10; i++) {
  document.querySelector('.card-body').innerHTML += `<div class="row" id="img_div">
        <div class="col-12 col-sm-12 col-md-2 text-center">
          <img src="http://placehold.it/120x80" alt="prewiew" width="120" height="80">
        </div>
        <div id="text_div" class="col-12 text-sm-center col-sm-12 text-md-left col-md-6">
        <h4 class="name"><a href="#" id="title` + i + `">Name</a></h4>
          <h4>
            <small>state</small>
          </h4>
          <h4>
            <small>city</small>
          </h4>
          <h4>
            <small>zip</small>
          </h4>
        </div>
        <div class="col-12 col-sm-12 text-sm-center col-md-4 text-md-right row">
        </div>
      </div>
     `
  document.getElementById("title" + i).addEventListener('click', function() {
    console.log(i)
  });
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<div class="card-body">
  <!-- person -->
</div>

我想让事件在每个事件上单击,h4 class="name"并显示带有i相关编号的日志。

但是,console.log仅显示最后一个i相关数字(i=9在这种情况下),并且不适用于其他i数字。为什么会这样?我需要做什么?


3
也许使用Document.createElement()而不是传递html-string,您将拥有更轻松的时间&我认为您最终将获得更好的代码。
admcfajn

1
您将不得不再次遍历节点并附加一个您无法附加到字符串中的事件
Eugen Sunic

很好的问题,可以肯定,这有点棘手,我认为有可能在事件渲染后添加一个事件。例如,在for循环调用将事件应用于这些元素的方法之后,但我不知道。将jsFiddle进行测试。
Pogrindis

创建了一个委托事件处理程序,.card-body并检查元素(target)是否是锚点,且ID以开头title
hungerstar

1
嗯,正要发布答案。投票重新开放。不需要关闭,问题是innerHTML += ...杀死先前设置的事件侦听器,而不是关闭。使用document.createElement代替。看到这个
话题

Answers:


8

innerHTML重新绘制完整的html,结果所有先前附加的事件都将丢失。采用insertAdjacentHTML()

for(let i=1;i<10;i++){
    document.querySelector('.card-body').insertAdjacentHTML('beforeend',`<div class="row" id="img_div">
        <div class="col-12 col-sm-12 col-md-2 text-center">
          <img src="http://placehold.it/120x80" alt="prewiew" width="120" height="80">
        </div>
        <div id="text_div" class="col-12 text-sm-center col-sm-12 text-md-left col-md-6">
        <h4 class="name"><a href="#" id="title`+i+`">Name</a></h4>
          <h4>
            <small>state</small>
          </h4>
          <h4>
            <small>city</small>
          </h4>
          <h4>
            <small>zip</small>
          </h4>
        </div>
        <div class="col-12 col-sm-12 text-sm-center col-md-4 text-md-right row">
        </div>
      </div>
     `);
   document.getElementById("title"+i).addEventListener('click', function () {
    console.log(i)
  });
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<div class="card-body">
<!-- person -->
</div>


3
哇..我一直到处都是类似的东西,从来没有遇到过insertAdjacentHTML-每天上课的日子,真的很好。
Pogrindis

1
非常酷,而且+1,但是document.getElementById添加HTML之后立即使用它似乎仍然很昂贵且不必要。如果要添加数百个元素,则需要进行大量遍历。
ggorlen

@ggorlen我有点同意对perf领域感兴趣,这会使用类name并在通过元素(或者可能只是从事件本身读取)的同时将事件附加到该类上来提高效率吗?
Pogrindis

1
不确定。取决于实际用法-我的建议可能是过早优化。您必须进行个人资料/基准测试并查看。但是,如果document.createElement可以使用,我认为这通常是最好的基准方法。这篇文章的另一种可能的改进是使用两个循环:一个循环构建所有HTML,然后另一个循环在一个document.querySelectorAll调用中使用一个类来获取所有元素并添加事件侦听器。
ggorlen

1
绝对值得一个基准,今晚我想不出更好的办法!:)
Pogrindis

8

使用+=on innerHTML破坏HTML内元素上的事件侦听器。正确的方法是使用document.createElement。这是一个最小的完整示例:

for (let i = 1; i < 10; i++) {
  const anchor = document.createElement("a");
  document.body.appendChild(anchor);
  anchor.id = "title" + i;
  anchor.href= "#";
  anchor.textContent = "link " + i;
  document.getElementById("title" + i)
    .addEventListener("click", e => console.log(i));
}
a {
  margin-left: 0.3em;
}

当然,document.getElementById这里不是必需的,因为我们只是在循环块中创建了对象。这样可以节省潜在的昂贵的树木漫步。

for (let i = 1; i < 10; i++) {
  const anchor = document.createElement("a");
  document.body.appendChild(anchor);    
  anchor.href= "#";
  anchor.textContent = "link " + i;
  anchor.addEventListener("click", e => console.log(i));
}
a {
  margin-left: 0.3em;
}

如果在示例中有任意块的字符串化HTML,则可以使用createElement("div"),将其设置innerHTML为块一次,然后根据需要添加侦听器。然后将div作为容器元素的子代附加。

for (let i = 1; i < 10; i++) {
  const rawHTML = `<div><a href="#" id="link-${i}">link ${i}</a></div>`;
  const div = document.createElement("div");
  document.body.appendChild(div);
  div.href= "#";
  div.innerHTML = rawHTML;
  div.querySelector(`#link-${i}`)
    .addEventListener("click", e => console.log(i));
}


1
这是我正在考虑的方法,我会毫不犹豫地给出解决方案,但另一个答案也很好。
Pogrindis

感谢您的答复,但我尝试使用createElement构建布局,但未成功
Luiz Adorno

1
没问题。希望您意识到,如果您有大量的HTML,可以创建一个根元素容器,如<div>,然后div.innerHTML += yourHugeChunkOfHTML。因此,没有理由在任何用例中都不可行。
ggorlen

1
我将尝试根据您的建议构建此布局,恐怕要多次使用“ document.getElementById”并降低性能
Luiz Adorno
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.