web-dev-qa-db-fra.com

Est-il possible d'ajouter à innerHTML sans détruire les écouteurs d'événements des descendants?

Dans l'exemple de code suivant, j'attache un gestionnaire d'événements onclick à la plage contenant le texte "foo". Le gestionnaire est une fonction anonyme qui affiche une alerte ().

Cependant, si j'assigne au innerHTML du nœud parent, ce gestionnaire d'événements onclick est détruit - un clic sur "foo" ne parvient pas à afficher la boîte d'alerte.

Est-ce réparable?

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

Malheureusement, l'attribution à innerHTML provoque la destruction de tous les éléments enfants, même si vous essayez d'ajouter. Si vous souhaitez conserver les nœuds enfants (et leurs gestionnaires d'événements), vous devez utiliser fonctions DOM :

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

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

Edit: La solution de Bob, à partir des commentaires. Postez votre réponse, Bob! Obtenez du crédit pour cela. :-)

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);
    }
}
104
Ben Blank

L'utilisation de .insertAdjacentHTML() préserve les écouteurs d'événements et est pris en charge par tous les principaux navigateurs C'est un simple remplacement d'une ligne pour .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'argument 'beforeend' spécifie où, dans l'élément, insérer le contenu HTML. Les options sont 'beforebegin', 'afterbegin', 'beforeend' et 'afterend'. Leurs emplacements correspondants sont:

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

Nous sommes maintenant en 2012 et jQuery a des fonctions d'ajout et de préfixe qui font exactement cela, ajouter du contenu sans affecter le contenu actuel. Très utile.

4
rocketsarefast

En guise d'assise légère (mais liée), si vous utilisez une bibliothèque javascript telle que jquery (v1.3) pour manipuler votre dom, vous pouvez utiliser des événements en direct dans lesquels vous configurez un gestionnaire tel que: 

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

et il sera appliqué à ce sélecteur à tout moment lors de tout type de manipulation de requête. Pour les événements en direct, voir: docs.jquery.com/events/live pour la manipulation jQuery, voir: docs.jquery.com/manipulation

3
Cargowire

quelque chose.innerHTML + = 'ajoutez ce que vous voulez';

cela a fonctionné pour moi. J'ai ajouté un bouton à un texte saisi en utilisant cette solution

2
KawaiKx

J'ai créé mon balisage pour l'insérer sous la forme d'une chaîne, car il s'agit de moins de code et de lisibilité plus facile que de travailler avec des éléments fantômes.

Ensuite, j'ai créé innerHTML d'un élément temporaire juste pour pouvoir prendre le seul et unique enfant de cet élément et l'attacher au corps.

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

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

Perdre des gestionnaires d’événements est, IMO, un bogue dans la façon dont Javascript gère le DOM. Pour éviter ce problème, vous pouvez ajouter les éléments suivants:

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
}
1
Yes - that Jake.

Il existe une autre alternative: utiliser setAttribute plutôt que d'ajouter un écouteur d'événement. Comme ça: 

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

Pour tout tableau d'objets avec en-tête et données .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>
0
AKS

Le moyen le plus simple consiste à utiliser un tableau et des éléments Push dans celui-ci, puis à insérer dynamiquement les valeurs suivantes du tableau dans le tableau .

var namesArray = [];

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

Vous pouvez le faire comme ceci:

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

Oui, cela est possible si vous liez des événements en utilisant l’argument onclick="sayHi()" directement dans un modèle similaire à votre <body onload="start()"> - cette approche est similaire à celle des cadres angular/vue/react/etc. Vous pouvez également utiliser <template> pour opérer sur du HTML 'dynamique' comme ici . Ce n'est pas strict discret js mais c'est acceptable pour les petits projets

_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