È possibile aggiungere a innerHTML senza distruggere i listener di eventi dei discendenti?


112

Nel seguente codice di esempio, allego un onclickgestore di eventi allo span contenente il testo "foo". Il gestore è una funzione anonima che fa apparire un file alert().

Tuttavia, se assegno al nodo genitore innerHTML, questo onclickgestore di eventi viene distrutto - facendo clic su "pippo" non viene visualizzata la finestra di avviso.

È risolvibile?

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

Questa domanda è correlata al problema "l'aggiunta di nuovi campi a un modulo cancella l'input dell'utente". La risposta selezionata risolve molto bene entrambi questi problemi.
MattT

La delega degli eventi può essere utilizzata per affrontare questo problema.
dasfdsa

Risposte:


127

Sfortunatamente, l'assegnazione a innerHTMLcausa la distruzione di tutti gli elementi figlio, anche se stai cercando di aggiungere. Se vuoi preservare i nodi figli (e i loro gestori di eventi), dovrai usare le funzioni DOM :

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

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

Modifica: la soluzione di Bob, dai commenti. Pubblica la tua risposta, Bob! Ottieni credito per questo. :-)

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

Esiste un sostituto in grado di aggiungere un blob arbitrario di HTML?
mike

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

Bello, Bob! Se la pubblichi come una risposta ben formattata, la selezionerò.
mike

@bobince - Ho aggiornato la mia risposta con la tua tecnica, dato che non l'hai ancora pubblicata tu stesso. Se crei la tua risposta, vai avanti e ripristina la mia. :-)
Ben Blank

2
Oh, un'ultima cosa, ti consigliamo "var myspan", "var newcontent" ecc. Per evitare la fuoriuscita accidentale di globali.
bobince

85

L'uso di .insertAdjacentHTML()conserva i listener di eventi ed è supportato da tutti i principali browser . È un semplice sostituto di una riga per .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);

L' 'beforeend'argomento specifica dove nell'elemento inserire il contenuto HTML. Le opzioni sono 'beforebegin', 'afterbegin', 'beforeend', e 'afterend'. Le loro posizioni corrispondenti sono:

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

Mi hai salvato la giornata!
Tural Asgar

Soluzione migliore. Grazie!
Dawoodjee

3

Ora, è il 2012 e jQuery ha funzioni di aggiunta e anteporre che fanno esattamente questo, aggiungono contenuto senza influenzare il contenuto corrente. Molto utile.


7
.insertAdjacentHTMLè in circolazione da IE4
Esailija

1
@Tynach sì, è il loro sito JavaScript che lo documenta al meglio: developer.mozilla.org/en-US/docs/Web/API/Element/…
Jordon Bedwell,

2
@JordonBedwell, onestamente non ho idea del motivo per cui ho detto quello che ho fatto prima. Hai ragione al 100%. Mi sembra di averlo esaminato brevemente e non sono riuscito a trovarlo, ma ... Onestamente non ho scuse. Esailija collega anche a quella pagina.
Tynach

OP non parla di jQuery
André Marcondes Teixeira

2

Come un leggero (ma correlato) vantaggio, se usi una libreria javascript come jquery (v1.3) per fare la tua manipolazione dom puoi fare uso di eventi live in cui imposti un gestore come:

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

e verrà applicato a quel selettore in ogni momento durante qualsiasi tipo di manipolazione jquery. Per gli eventi dal vivo vedere: docs.jquery.com/events/live per la manipolazione di jquery vedere: docs.jquery.com/manipulation


1
OP non sta parlando di jQuery
André Marcondes Teixeira

2

Ho creato il mio markup da inserire come stringa poiché è meno codice e più facile da leggere che lavorare con le fantasiose cose dom.

Poi l'ho trasformato in innerHTML di un elemento temporaneo solo per poter prendere l'unico figlio di quell'elemento e attaccarlo al corpo.

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 + = 'aggiungi quello che vuoi';

ha funzionato per me. Ho aggiunto un pulsante a un testo di input utilizzando questa soluzione


Questo è stato il metodo più semplice che abbia mai incontrato grazie!
demo7up

4
Distrugge tutti gli eventi.
Tural Asgar

1
come è davvero una soluzione? distrugge tutti gli eventi !!
Danese

1

C'è un'altra alternativa: usare setAttributeinvece di aggiungere un listener di eventi. Come questo:

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

Sì, è possibile associare eventi utilizzando l'attributo tag onclick="sayHi()"direttamente nel modello simile al tuo <body onload="start()">: questo approccio è simile a framework angular / vue / react / ecc. Puoi anche utilizzare <template>per operare su html "dinamico" come qui . Non è un js rigoroso e discreto, tuttavia è accettabile per piccoli progetti

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

La perdita dei gestori di eventi è, IMO, un bug nel modo in cui Javascript gestisce il DOM. Per evitare questo comportamento, puoi aggiungere quanto segue:

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
Non lo considero un bug. Sostituire un elemento significa sostituirlo completamente. Non dovrebbe ereditare ciò che c'era prima.
Diodeus - James MacFarlane,

Ma non voglio sostituire un elemento; Voglio solo aggiungerne di nuovi al genitore.
mike

Se sostituissi l'elemento, sarei d'accordo, @Diodeus. Non è il .innerHTML che ha il gestore di eventi, ma il genitore .innerHtml.
Sì, quel Jake.

2
Il proprietario di innerHTML non perde i gestori quando viene modificato il suo innerHTML, solo qualsiasi cosa nel suo contenuto. E JavaScript non sa che stai solo aggiungendo nuovi figli, poiché "x.innerHTML + = y" è solo zucchero sintattico per l'operazione stringa "x.innerHTML = x.innerHTML + y".
bobince

Non è necessario ricollegarlo mydiv.onclick. Solo il clic dell'estensione interna viene sovrascritto da innerHTML +=.
Crescent Fresh,

0

Potresti farlo in questo modo:

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

Il modo più semplice è utilizzare un array e inserirvi gli elementi, quindi inserire dinamicamente i valori successivi dell'array nell'array. Ecco il mio codice:

var namesArray = [];

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

0

Per qualsiasi matrice di oggetti con intestazione e dati.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

Sono un programmatore pigro. Non uso DOM perché sembra una digitazione extra. Per me, meno codice è meglio. Ecco come aggiungerei "bar" senza sostituire "foo":

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

15
Questa domanda sta chiedendo come farlo senza perdere i gestori di eventi, quindi la tua risposta non è rilevante, e btw questo è ciò che + = fa, che è molto più breve
wlf

2
È esilarante che in questa risposta completamente sbagliata che cita l'evitare la digitazione extra come giustificazione, circa la metà del codice è ridondante.
JLRishe
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.