如何在 innerHTML 中获取点击事件?

Posted

技术标签:

【中文标题】如何在 innerHTML 中获取点击事件?【英文标题】:How can I get the click event inside an innerHTML? 【发布时间】:2020-05-30 17:36:35 【问题描述】:

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

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"   >
        </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 数字。为什么会这样?我该怎么办?

【问题讨论】:

也许使用 Document.createElement() 而不是传递 html-string & 你会更轻松 & 我认为你最终会得到更好的代码。 你将不得不再次遍历你的节点并将你无法附加到字符串中的事件附加到字符串中 很好的问题,这肯定有点棘手,我认为有可能在渲染后附加一个事件。例如,在您的for 循环调用一个方法以将事件应用于这些元素之后,但是我不知道。将jsFiddle进行测试。 .card-body 创建了一个委托事件处理程序,并检查元素 (target) 是否是一个锚点,其 ID 以 title 开头。 啊,正要发布答案。投票决定重新开放。闭包不是必需的,问题是innerHTML += ... 杀死了先前设置的事件侦听器,而不是闭包。请改用document.createElement。见this thread 【参考方案1】:

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

【讨论】:

这是我正在考虑的方法,我会毫不犹豫地给出解决方案,但另一个答案也很棒。 感谢您的回答,但我尝试使用 createElement 构建布局,但没有成功 没问题。希望你意识到如果你有一大块 HTML,你可以创建一个根元素容器,比如 &lt;div&gt;,然后是 div.innerHTML += yourHugeChunkOfHTML。所以没有理由这不适用于任何用例。 我会根据您的建议尝试构建此布局,我怕多次使用“document.getElementById”并降低性能【参考方案2】:

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"   >
        </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>

【讨论】:

哇.. 我一直在搞些类似的事情,从来没有遇到过insertAdjacentHTML - 每天都是学生时代,真的很好。 非常酷并且 +1,但在添加 HTML 后必须立即使用 document.getElementById 似乎仍然很昂贵且没有必要。如果要添加数百个元素,则需要大量遍历。 @ggorlen 我有点同意 perf 领域出于兴趣将使用类 name 并在通过元素时将事件附加到该类(或者可能只是从事件本身读取)更有效率? 不确定。取决于实际使用情况——我的建议可能是过早的优化。您必须进行分析/基准测试并查看。但是如果可以使用document.createElement,我认为这通常是最好的基线方法。这篇文章的另一个可能的改进是使用两个循环:一个用于构建所有 HTML,另一个用于在一个 document.querySelectorAll 调用中使用一个类获取所有元素并添加事件侦听器。 绝对值得做一个基准测试,今晚我想不出比这更好的了! :)

以上是关于如何在 innerHTML 中获取点击事件?的主要内容,如果未能解决你的问题,请参考以下文章

js获取鼠标点击事件的相对位置

JS:当我点击一个 TD 元素时,如何获取它的 innerHTML?

JS中如何获取元素

如何在 selenium 驱动程序中获取整个页面的 innerHTML?

javascript,有很多个li,给每个li写同一个单击事件,怎么让它实现点击其中一个li,弹出对应的innerHTML.

BaseQuickAdapter和BaseViewHolder使用 RecyclerView点击事件 RecyclerView添加子view点击事件 RecyclerView添加子view点击事