是否可以在不破坏后代事件监听器的情况下附加到innerHTML?


112

在以下示例代码中,我将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>

此问题与“在表单上添加新字段会删除用户输入”问题有关。选择的答案很好地解决了这两个问题。
MattT

事件委托可用于解决此问题。
dasfdsa

Answers:


126

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

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

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

编辑:鲍勃的解决方案,从评论。发表您的答案,鲍勃!为此获得信誉。:-)

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的任意斑点?
mike

64
newcontent = document.createElement('div'); newcontent.innerHTML = random_blob; while(newcontent.firstChild)mydiv.appendChild(newcontent.firstChild);
bobince

很好,鲍勃!如果您将其发布为格式正确的答案,我会选择它。
mike

@bobince —我已经用您的技术更新了我的答案,因为您自己还没有发布它。如果您创建自己的答案,请继续进行操作。:-)
Ben Blank

2
哦,最后一件事,您需要“ var myspan”,“ var newcontent”等,以避免意外溢出全局变量。
bobince

85

使用.insertAdjacentHTML()保留事件侦听器,并且所有主要浏览器都支持。这是的简单的单行替换.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 -->

你救了我的一天!
Tural Asgar

最佳解决方案。谢谢!
达伍德吉

3

现在是2012年,jQuery具有恰好可以做到这一点的追加和前置功能,可以在不影响当前内容的情况下添加内容。很有用。


7
.insertAdjacentHTMLIE4以来一直围绕
Esailija

1
@Tynach是的,它是JavaScript网站上最好的文档:developer.mozilla.org/en-US/docs/Web/API/Element/…
佐敦贝德韦尔

2
@JordonBedwell,老实说,我不知道为什么我要说以前的事情。您是100%正确的。我觉得当时我曾短暂地查看过它,却找不到它,但是...老实说,我没有任何借口。Esailija甚至链接到该页面。
Tynach

OP是不是在谈论的jQuery
安德烈·马孔德斯·特谢拉

2

稍微(但又相关),如果您使用诸如jquery(v1.3)之类的JavaScript库来进行dom操作,则可以利用实时事件来设置如下处理程序:

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

并且它将在任何类型的jquery操作期间始终应用于该选择器。有关现场活动,请参阅: docs.jquery.com/events/live以进行jquery操作,请参阅:docs.jquery.com/manipulation


1
OP是不是在谈论的jQuery
安德烈·马孔德斯·特谢拉

2

我创建了要作为字符串插入的标记,因为与使用花哨的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);

2

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

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


这是我遇到的最简单的方法,谢谢!
demo7up

4
它破坏了所有事件。
Tural Asgar

1
真正的解决方案是什么?它摧毁了所有事件!
丹麦

1

还有另一种选择:使用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>


1

是的,如果您onclick="sayHi()"直接在类似模板的模板中使用tag属性绑定事件是可能的<body onload="start()">-这种方法类似于框架angular / vue / react / etc。您也可以<template>此处一样在“动态” html上进行操作。这不是严格的js,但是对于小型项目是可以接受

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>


0

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
}

2
我不认为这是一个错误。替换元素意味着您完全替换了它。它不应该继承以前的内容。
Diodeus-James MacFarlane,

但是我不想替换一个元素。我只想向父母添加新的。
mike

如果您要替换元素,我会同意@Diodeus。具有事件处理程序的不是.innerHTML,而是.innerHtml的父级。
是的-那个杰克。

2
更改innerHTML时,innerHTML的所有者不会丢失处理程序,仅会更改其内容中的任何内容。而且JavaScript不知道您只是追加新的子代,因为“ x.innerHTML + = y”只是字符串操作“ x.innerHTML = x.innerHTML + y”的语法糖。
bobince

您无需重新连接mydiv.onclick。只有内部范围的onclick被覆盖innerHTML +=
月牙食

0

您可以这样做:

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

0

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

var namesArray = [];

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

0

对于具有标题和数据的任何对象数组。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>

-5

我是一个懒惰的程序员。我不使用DOM,因为它似乎需要额外输入。对我来说,代码越少越好。这是我添加“ bar”而不替换“ foo”的方法:

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

15
这个问题问的是如何做到这一点而又不丢失事件处理程序,所以您的答案不相关,顺便说一句,这就是+ =所做的,这要短得多
wlf 2012年

2
有趣的是,在这个完全错误的答案中,避免了额外的输入作为理由,大约一半的代码是多余的。
JLRishe
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.