Ist es möglich, an innerHTML anzuhängen, ohne die Ereignis-Listener der Nachkommen zu zerstören?

112

Im folgenden Beispielcode füge ich onclickdem Bereich, der den Text "foo" enthält, einen Ereignishandler hinzu. Der Handler ist eine anonyme Funktion, die eine anzeigt alert().

Wenn ich jedoch den übergeordneten Knoten zuweise innerHTML, wird dieser onclickEreignishandler zerstört. Wenn Sie auf "foo" klicken, wird das Warnfeld nicht angezeigt.

Ist das reparabel?

<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>
Mike
quelle
Diese Frage bezieht sich auf das Problem "Durch das Anhängen neuer Felder an ein Formular werden Benutzereingaben gelöscht". Die ausgewählte Antwort behebt beide Probleme sehr gut.
MattT
Die Ereignisdelegierung kann verwendet werden, um dieses Problem zu lösen.
Dasfdsa

Antworten:

126

Leider führt die Zuweisung zu innerHTMLzur Zerstörung aller untergeordneten Elemente, selbst wenn Sie versuchen, sie anzuhängen. Wenn Sie untergeordnete Knoten (und deren Ereignishandler) beibehalten möchten, müssen Sie DOM-Funktionen verwenden :

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

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

Edit: Bobs Lösung, aus den Kommentaren. Poste deine Antwort, Bob! Holen Sie sich Kredit dafür. :-)

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);
    }
}
Ben Blank
quelle
Gibt es einen Ersatz, der einen beliebigen HTML-Blob anhängen kann?
Mike
64
newcontent = document.createElement ('div'); newcontent.innerHTML = beliebiger_Blob; while (newcontent.firstChild) mydiv.appendChild (newcontent.firstChild);
Bobince
Schön, Bob! Wenn Sie das als gut formatierte Antwort posten, werde ich es auswählen.
Mike
@bobince - Ich habe meine Antwort mit Ihrer Technik aktualisiert, da Sie sie selbst noch nicht gepostet haben. Wenn Sie Ihre eigene Antwort erstellen, rollen Sie meine zurück. :-)
Ben Blank
2
Oh, eine letzte Sache, Sie möchten "var myspan", "var newcontent" usw., um zu vermeiden, dass versehentlich Globals verschüttet werden.
Bobince
85

Die Verwendung .insertAdjacentHTML()bewahrt Ereignis-Listener und wird von allen gängigen Browsern unterstützt . Es ist ein einfacher einzeiliger Ersatz für .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);

Das 'beforeend'Argument gibt an, wo im Element der HTML-Inhalt eingefügt werden soll. Die Optionen sind 'beforebegin', 'afterbegin', 'beforeend', und 'afterend'. Ihre entsprechenden Standorte sind:

<!-- beforebegin -->
<div id="mydiv">
  <!-- afterbegin -->
  <p>Existing content in #mydiv</p>
  <!-- beforeend -->
</div>
<!-- afterend -->
Josh de Leeuw
quelle
Du hast meinen Tag gerettet!
Tural Asgar
Beste Lösung. Danke dir!
Dawoodjee
3

Jetzt ist es 2012, und jQuery verfügt über Funktionen zum Anhängen und Voranstellen, die genau dies tun. Fügen Sie Inhalte hinzu, ohne die aktuellen Inhalte zu beeinflussen. Sehr hilfreich.

Raketen sind schnell
quelle
7
.insertAdjacentHTMLgibt es seit IE4
Esailija
1
@Tynach Ja, es ist ihre JavaScript-Site, die es am besten dokumentiert: developer.mozilla.org/en-US/docs/Web/API/Element/…
Jordon Bedwell
2
@JordonBedwell, ich habe ehrlich gesagt keine Ahnung, warum ich gesagt habe, was ich vorher getan habe. Du hast 100% Recht. Ich habe das Gefühl, als ich mich kurz damit befasst habe und es nicht finden konnte, aber ... Ehrlich gesagt habe ich keine Entschuldigung. Esailija verlinkt sogar auf diese Seite.
Tynach
OP spricht nicht über jQuery
André Marcondes Teixeira
2

Wenn Sie eine Javascript-Bibliothek wie jquery (v1.3) verwenden, um Ihre Dom-Manipulation durchzuführen, können Sie Live-Ereignisse verwenden, bei denen Sie einen Handler wie den folgenden einrichten:

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

und es wird zu jeder Zeit während jeder Art von Abfrage-Manipulation auf diesen Selektor angewendet. Live-Ereignisse finden Sie unter: docs.jquery.com/events/live. Informationen zur Manipulation von Abfragen finden Sie unter: docs.jquery.com/manipulation

Cargowire
quelle
1
OP spricht nicht über jQuery
André Marcondes Teixeira
2

Ich habe mein Markup erstellt, um es als Zeichenfolge einzufügen, da es weniger Code enthält und leichter zu lesen ist als die Arbeit mit dem ausgefallenen Dom-Zeug.

Dann habe ich es innerHTML eines temporären Elements gemacht, nur damit ich das einzige Kind dieses Elements nehmen und an den Körper binden kann.

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

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

Something.innerHTML + = 'füge hinzu, was immer du willst';

es hat bei mir funktioniert. Mit dieser Lösung habe ich einem Eingabetext eine Schaltfläche hinzugefügt

KawaiKx
quelle
Dies war die einfachste Methode, die mir begegnet ist. Danke!
Demo7up
4
Es zerstört alle Ereignisse.
Tural Asgar
1
Wie ist es wirklich eine Lösung? es zerstört alle Ereignisse !!
Dänisch
1

Es gibt noch eine andere Alternative: Verwenden setAttributeeines Ereignis-Listeners , anstatt ihn hinzuzufügen. So was:

<!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>

Frank Conijn
quelle
1

Ja, es ist möglich, wenn Sie Ereignisse mithilfe des Tag-Attributs onclick="sayHi()"direkt in einer Vorlage binden, die Ihrer ähnlich ist <body onload="start()">- dieser Ansatz ähnelt den Frameworks Angular / Vue / React / etc. Sie können auch verwenden <template>, um 'dynamisches' HTML wie hier zu bearbeiten . Es ist nicht streng unauffällig, aber es ist akzeptabel für kleine Projekte

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>

Kamil Kiełczewski
quelle
0

Der Verlust von Ereignishandlern ist, IMO, ein Fehler in der Art und Weise, wie Javascript mit dem DOM umgeht. Um dieses Verhalten zu vermeiden, können Sie Folgendes hinzufügen:

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
}
Ja - dieser Jake.
quelle
2
Ich halte das nicht für einen Fehler. Wenn Sie ein Element ersetzen, ersetzen Sie es vollständig. Es sollte nicht erben, was vorher da war.
Diodeus - James MacFarlane
Aber ich möchte kein Element ersetzen. Ich möchte nur neue an die Eltern anhängen.
Mike
Wenn Sie das Element ersetzen würden, würde ich zustimmen, @Diodeus. Es ist nicht die .innerHTML, die den Ereignishandler hat, sondern die übergeordnete .innerHtml.
Ja - dieser Jake.
2
Der Eigentümer von innerHTML verliert keine Handler, wenn sein innerHTML geändert wird, sondern nur irgendetwas in seinem Inhalt. Und JavaScript weiß nicht, dass Sie nur neue untergeordnete Elemente anhängen, da "x.innerHTML + = y" nur syntaktischer Zucker für die Zeichenfolgenoperation "x.innerHTML = x.innerHTML + y" ist.
Bobince
Sie müssen nicht erneut anhängen mydiv.onclick. Nur der Onclick der inneren Spanne wird von überschrieben innerHTML +=.
Crescent Fresh
0

Sie könnten es so machen:

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;
    }
}
Rishabh Gupta
quelle
0

Am einfachsten ist es, ein Array zu verwenden und Elemente darin zu verschieben und dann die nachfolgenden Werte des Arrays dynamisch in das Array einzufügen. Hier ist mein Code:

var namesArray = [];

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

Für jedes Objektarray mit Header und Daten.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>
AKS
quelle
-5

Ich bin ein fauler Programmierer. Ich benutze DOM nicht, weil es wie zusätzliches Tippen erscheint. Je weniger Code, desto besser für mich. So würde ich "bar" hinzufügen, ohne "foo" zu ersetzen:

function start(){
var innermyspan = document.getElementById("myspan").innerHTML;
document.getElementById("myspan").innerHTML=innermyspan+"bar";
}
klar
quelle
15
Diese Frage fragt, wie dies zu tun ist, ohne Event-Handler zu verlieren, daher ist Ihre Antwort nicht relevant, und übrigens ist dies das, was + = tut, was viel kürzer ist
wlf
2
Es ist komisch, dass in dieser völlig falschen Antwort, in der die Vermeidung zusätzlicher Eingaben als Rechtfertigung genannt wird, etwa die Hälfte des Codes redundant ist.
JLRishe