【问题标题】:Is it possible to append to innerHTML without destroying descendants' event listeners?是否可以在不破坏后代事件侦听器的情况下附加到 innerHTML?
【发布时间】:2021-08-20 07:56:33
【问题描述】:

在下面的示例代码中,我将onclick 事件处理程序附加到包含文本“foo”的跨度。处理程序是一个匿名函数,会弹出一个alert()

但是,如果我分配给父节点的innerHTML,则此onclick 事件处理程序将被破坏-单击“foo”无法弹出警告框。

这可以修复吗?

<html>
 <head>
 <script type="text/javascript">

  function start () {
    myspan = document.getElementById("myspan");
    myspan.onclick = function() { alert ("hi"); };

    mydiv = document.getElementById("mydiv");
    mydiv.innerHTML += "bar";
  }

 </script>
 </head>

 <body onload="start()">
   <div id="mydiv" style="border: solid red 2px">
     <span id="myspan">foo</span>
   </div>
 </body>

</html>

【问题讨论】:

  • 此问题与“将新字段附加到表单会删除用户输入”问题有关。选择的答案很好地解决了这两个问题。
  • 事件委托可以用来解决这个问题。

标签: javascript html innerhtml dom-events


【解决方案1】:

不幸的是,分配给innerHTML 会导致所有子元素的破坏,即使您尝试追加也是如此。如果要保留子节点(及其事件处理程序),则需要使用DOM functions

function start() {
    var myspan = document.getElementById("myspan");
    myspan.onclick = function() { alert ("hi"); };

    var mydiv = document.getElementById("mydiv");
    mydiv.appendChild(document.createTextNode("bar"));
}

编辑: Bob 的解决方案,来自 cmets。发布你的答案,鲍勃!获得荣誉。 :-)

function start() {
    var myspan = document.getElementById("myspan");
    myspan.onclick = function() { alert ("hi"); };

    var mydiv = document.getElementById("mydiv");
    var newcontent = document.createElement('div');
    newcontent.innerHTML = "bar";

    while (newcontent.firstChild) {
        mydiv.appendChild(newcontent.firstChild);
    }
}

【讨论】:

  • 是否有可以附加任意 HTML 块的替代品?
  • newcontent= document.createElement('div'); newcontent.innerHTML=任意_blob; while (newcontent.firstChild) mydiv.appendChild(newcontent.firstChild);
  • 很好,鲍勃!如果您将其发布为格式正确的答案,我会选择它。
  • @bobince — 我已经用你的技术更新了我的答案,因为你还没有自己发布它。如果您创建自己的答案,请继续并回滚我的答案。 :-)
  • 哦,最后一件事,您需要“var myspan”、“var newcontent”等以避免意外溢出全局变量。
【解决方案2】:

使用.insertAdjacentHTML() 保留事件侦听器,使用is supported by all major browsers。这是.innerHTML 的简单单行替换。

var html_to_insert = "<p>New paragraph</p>";

// with .innerHTML, destroys event listeners
document.getElementById('mydiv').innerHTML += html_to_insert;

// with .insertAdjacentHTML, preserves event listeners
document.getElementById('mydiv').insertAdjacentHTML('beforeend', html_to_insert);

'beforeend' 参数指定在元素中插入 HTML 内容的位置。选项为 'beforebegin''afterbegin''beforeend''afterend'。它们对应的位置是:

<!-- beforebegin -->
<div id="mydiv">
  <!-- afterbegin -->
  <p>Existing content in #mydiv</p>
  <!-- beforeend -->
</div>
<!-- afterend -->

【讨论】:

  • 你拯救了我的一天!
  • 最佳解决方案。谢谢!
  • 小心 Chrome / Edge 抱怨区分大小写:即 afterbegin 不被接受 afterBegin 是!
【解决方案3】:

现在是 2012 年,jQuery 有 append 和 prepend 函数可以做到这一点,在不影响当前内容的情况下添加内容。很有用。

【讨论】:

  • .insertAdjacentHTML 自 IE4 以来一直存在
  • @Tynach 是的,确实如此,这是他们的 JavaScript 网站记录的最好的:developer.mozilla.org/en-US/docs/Web/API/Element/…
  • @JordonBedwell,老实说,我不知道我为什么要这么说。你是 100% 正确的。我觉得当时我简单地查看了一下并找不到它,但是......老实说,我没有任何借口。 Esailija 甚至 链接 到该页面。
  • OP 不是在谈论 jQuery
【解决方案4】:

顺便说一句(但相关),如果您使用诸如 jquery (v1.3) 之类的 javascript 库来进行 dom 操作,您可以利用实时事件来设置如下处理程序:

 $("#myspan").live("click", function(){
  alert('hi');
});

在任何类型的 jquery 操作过程中,它将始终应用于该选择器。对于现场活动,请参阅:docs.jquery.com/events/live 对于 jquery 操作,请参阅:docs.jquery.com/manipulation

【讨论】:

  • OP 不是在谈论 jQuery
【解决方案5】:

我创建了要作为字符串插入的标记,因为它的代码更少,并且比使用花哨的 dom 东西更容易阅读。

然后我将其设置为一个临时元素的 innerHTML,这样我就可以获取该元素的唯一子元素并附加到正文。

var html = '<div>';
html += 'Hello div!';
html += '</div>';

var tempElement = document.createElement('div');
tempElement.innerHTML = html;
document.getElementsByTagName('body')[0].appendChild(tempElement.firstChild);

【讨论】:

    【解决方案6】:

    是的,如果您直接在模板中使用标签属性 onclick="sayHi()" 绑定事件,就像您的 &lt;body onload="start()"&gt; 一样 - 这种方法类似于框架 angular/vue/react/etc。您还可以使用&lt;template&gt; 对“动态”html 进行操作,例如here。它不是严格不显眼的 js,但它是 acceptable 用于小型项目

    function start() {
      mydiv.innerHTML += "bar";
    }
    
    function sayHi() {
      alert("hi");
    }
    <body onload="start()">
      <div id="mydiv" style="border: solid red 2px">
        <span id="myspan" onclick="sayHi()">foo</span>
      </div>
    </body>

    【讨论】:

      【解决方案7】:

      还有另一种选择:使用setAttribute 而不是添加事件侦听器。像这样:

      <!DOCTYPE html>
      <html>
      <head>
          <meta charset="utf-8">
          <title>Demo innerHTML and event listeners</title>
      <style>
          div {
              border: 1px solid black;
              padding: 10px;
          }
      </style>
      </head>
      <body>
          <div>
              <span>Click here.</span>
          </div>
          <script>
              document.querySelector('span').setAttribute("onclick","alert('Hi.')");
              document.querySelector('div').innerHTML += ' Added text.';
          </script>
      </body>
      </html>

      【讨论】:

        【解决方案8】:

        在 IMO 中,丢失事件处理程序是 Javascript 处理 DOM 方式中的一个错误。为避免这种行为,您可以添加以下内容:

        function start () {
          myspan = document.getElementById("myspan");
          myspan.onclick = function() { alert ("hi"); };
        
          mydiv = document.getElementById("mydiv");
          clickHandler = mydiv.onclick;  // add
          mydiv.innerHTML += "bar";
          mydiv.onclick = clickHandler;  // add
        }
        

        【讨论】:

        • 我不认为这是一个错误。替换元素意味着您完全替换它。它不应该继承之前的东西。
        • 但是我不想替换一个元素;我只想将新的附加到父级。
        • 如果您要替换元素,我会同意,@Diodeus。拥有事件处理程序的不是 .innerHTML,而是 .innerHtml 的父级。
        • innerHTML 的所有者在更改其 innerHTML 时不会丢失处理程序,只会丢失其内容中的任何内容。而且 JavaScript 不知道您只是在添加新的子元素,因为“x.innerHTML+= y”只是字符串操作“x.innerHTML= x.innerHTML+y”的语法糖。
        • 您不需要重新附加mydiv.onclick。只有内部跨度的 onclick 会被 innerHTML += 覆盖。
        【解决方案9】:

        你可以这样做:

        var anchors = document.getElementsByTagName('a'); 
        var index_a = 0;
        var uls = document.getElementsByTagName('UL'); 
        window.onload=function()          {alert(anchors.length);};
        for(var i=0 ; i<uls.length;  i++)
        {
            lis = uls[i].getElementsByTagName('LI');
            for(var j=0 ;j<lis.length;j++)
            {
                var first = lis[j].innerHTML; 
                string = "<img src=\"http://g.etfv.co/" +  anchors[index_a++] + 
                    "\"  width=\"32\" 
                height=\"32\" />   " + first;
                lis[j].innerHTML = string;
            }
        }
        

        【讨论】:

          【解决方案10】:

          something.innerHTML += '添加任何你想要的东西';

          它对我有用。我使用此解决方案在输入文本中添加了一个按钮

          【讨论】:

          • 这是我遇到的最简单的方法,谢谢!
          • 它会破坏所有事件。
          • 真正的解决方案如何?它破坏了所有的事件!
          【解决方案11】:

          最简单的方法是使用数组并将元素压入其中,然后将数组后续值动态插入到数组中。 这是我的代码:

          var namesArray = [];
          
          function myclick(){
              var readhere = prompt ("Insert value");
              namesArray.push(readhere);
              document.getElementById('demo').innerHTML= namesArray;
          }
          

          【讨论】:

            【解决方案12】:

            对于任何带有标头和数据的对象数组。jsfiddle

            https://jsfiddle.net/AmrendraKumar/9ac75Lg0/2/

            <table id="myTable" border='1|1'></table>
            
            <script>
              const userObjectArray = [{
                name: "Ajay",
                age: 27,
                height: 5.10,
                address: "Bangalore"
              }, {
                name: "Vijay",
                age: 24,
                height: 5.10,
                address: "Bangalore"
              }, {
                name: "Dinesh",
                age: 27,
                height: 5.10,
                address: "Bangalore"
              }];
              const headers = Object.keys(userObjectArray[0]);
              var tr1 = document.createElement('tr');
              var htmlHeaderStr = '';
              for (let i = 0; i < headers.length; i++) {
                htmlHeaderStr += "<th>" + headers[i] + "</th>"
              }
              tr1.innerHTML = htmlHeaderStr;
              document.getElementById('myTable').appendChild(tr1);
            
              for (var j = 0; j < userObjectArray.length; j++) {
                var tr = document.createElement('tr');
                var htmlDataString = '';
                for (var k = 0; k < headers.length; k++) {
                  htmlDataString += "<td>" + userObjectArray[j][headers[k]] + "</td>"
                }
                tr.innerHTML = htmlDataString;
                document.getElementById('myTable').appendChild(tr);
              }
            
            </script>
            

            【讨论】:

              【解决方案13】:

              我是一个懒惰的程序员。我不使用 DOM,因为它看起来像是额外的输入。对我来说,代码越少越好。这是我如何添加“bar”而不替换“foo”:

              function start(){
              var innermyspan = document.getElementById("myspan").innerHTML;
              document.getElementById("myspan").innerHTML=innermyspan+"bar";
              }
              

              【讨论】:

              • 这个问题是问如何在不丢失事件处理程序的情况下做到这一点,所以你的答案是不相关的,顺便说一句,这就是 += 所做的,这要短得多
              • 有趣的是,在这个以避免额外输入为理由的完全错误的答案中,大约一半的代码是多余的。
              猜你喜欢
              • 2022-12-14
              • 2017-09-14
              • 1970-01-01
              • 2014-04-14
              • 1970-01-01
              • 2013-09-10
              • 2019-06-27
              相关资源
              最近更新 更多