2009-02-27 13 views
64

Nel seguente codice di esempio, allego un gestore di eventi onclick allo span contenente il testo "foo". Il gestore è una funzione anonima che fa apparire un avviso().È possibile aggiungere a innerHTML senza distruggere gli ascoltatori di eventi dei discendenti?

Tuttavia, se si assegna al nodo genitore innerHTML, questo gestore di eventi onclick viene distrutto - facendo clic su "foo" non si apre 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> 
+0

Questa domanda è correlata al problema "annulla nuovi campi in un modulo cancella input utente". La risposta selezionata risolve entrambi questi problemi molto bene. – MattT

risposta

88

Purtroppo, l'assegnazione a innerHTML provoca la distruzione di tutti gli elementi figlio, anche se si sta cercando di aggiungere. Se si desidera mantenere nodi figlio (ed i loro gestori di eventi), è necessario utilizzare :

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

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

Edit: 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); 
    } 
} 
+0

C'è un sostituto che può aggiungere un blob arbitrario di HTML? – mike

+43

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

+0

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

1

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

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 considero questo un bug. Sostituire un elemento significa sostituirlo completamente. Non dovrebbe ereditare ciò che c'era prima. –

+0

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

+0

Se stavi sostituendo l'elemento, sarei d'accordo, @Diodeus. Non è il .innerHTML con il gestore di eventi, ma il genitore di .innerHtml. –

3

Come un leggero (ma correlato) asside, se si utilizza una libreria javascript come jQuery (v1.3) per fare la tua manipolazione del DOM è possibile fare uso di eventi dal vivo in cui si imposta un gestore come:

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

e sarà applicato a quella di selezione in ogni momento durante ogni tipo di manipolazione jquery. Per eventi dal vivo vedi: docs.jquery.com/events/live per manipolazione jquery vedere: docs.jquery.com/manipulation

-3

Sono un programmatore pigro. Non uso DOM perché sembra una digitazione aggiuntiva. Per me, meno codice e meglio è. Ecco come vorrei aggiungere "bar" senza sostituire "pippo":

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

Questa domanda ti chiede come fare senza perdere i gestori di eventi, quindi la tua risposta non è pertinente, e questo è ciò che + = fa, che è molto più breve – wlf

+0

È esilarante che in questa risposta completamente sbagliata che cita evitando la digitazione extra come giustificazione, circa la metà del codice è ridondante. – JLRishe

4

Ora, è il 2012, e jQuery ha aggiungere e anteporre funzioni che fanno esattamente questo, aggiungere il contenuto senza effettuare contenuto corrente. Molto utile.

+4

['.insertAdjacentHTML'] (https://developer.mozilla.org/en/DOM/element.insertAdjacentHTML) esiste da IE4 – Esailija

+1

@Esailija, che non funziona in Firefox. – Tynach

+0

@Tynach sì, è il loro sito JavaScript che lo documenta meglio: https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML –

2

Ho creato il mio markup da inserire come stringa dal momento che è meno codice e più facile da leggere rispetto a lavorare con le cose fantastiche dom.

Quindi ho reso HTMLHTML 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); 
0

Si potrebbe fare così:

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

Utilizzando .insertAdjacentHTML() conserva listener di eventi, e is supported by all major browsers. È una semplice sostituzione di una linea 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'argomento 'beforeend' specifica dove l'elemento per 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 --> 
+0

Molto interessante! Grazie per questa soluzione :) –

+0

Molto la soluzione più accurata, IMO – Velojet

Problemi correlati