46 votes

Pourquoi y.innerHTML = x.innerHTML? être évité?

Disons que nous avons un DIV x sur la page et nous voulons reproduire ("copier-coller") le contenu de la DIV dans un autre DIV y. Nous pourrions le faire comme ceci:

y.innerHTML = x.innerHTML;

ou avec jQuery:

$(y).html( $(x).html() );

Cependant, il semble que cette méthode n'est pas une bonne idée, et qu'il devrait être évitée.

(1) Pourquoi cette méthode-elle être évitée?

(2) Comment faut-il faire à la place?


Mise à jour:
Pour l'amour de cette question, supposons qu'il n'existe pas d'élément avec l'ID de l'intérieur de la DIV x.
(Désolé j'ai oublié de couvrir ce cas dans ma question initiale.)

Conclusion:
J'ai posté ma propre réponse à cette question ci-dessous (comme je l'ai prévu initialement). Maintenant, j'ai aussi prévu de les accepter ma propre réponse :P, mais lonesomeday la réponse est tellement incroyable que je dois l'accepter à la place.

78voto

lonesomeday Points 95456

Cette méthode de "copier" éléments HTML à partir d'un endroit à l'autre est le résultat d'une mauvaise compréhension de ce qu'est un navigateur. Les navigateurs ne pas conserver un document HTML en mémoire quelque part à plusieurs reprises de modifier le code HTML basés sur des commandes à partir de JavaScript.

Lorsqu'un navigateur charge une page, il analyse le document HTML et le transforme en un DOM structure. C'est une relation des objets suivant un standard du W3C (enfin, la plupart du temps...). Le HTML d'origine est à partir de là complètement redondant. Le navigateur ne prend pas soin de ce que l'original de la structure HTML a été; sa conception de la page web est le DOM de la structure qui a été créée à partir d'elle. Si votre balisage HTML est incorrect/non valide, il sera corrigé dans une certaine mesure par le navigateur web; les DOM structure ne contiennent pas le code non valide en quelque sorte.

Fondamentalement, HTML doit être considérée comme un moyen de serialising un DOM structure à être passé au-dessus de l'internet ou stockés dans un fichier local.

Il ne devrait pas, par conséquent, être utilisée pour la modification d'une page web existante. Le DOM (Document Object Model) est un système permettant de modifier le contenu d'une page. Ceci est basé sur la relation de nœuds, pas sur le HTML de la sérialisation. Ainsi, lorsque vous ajoutez un li d'un ul, vous disposez de ces deux options (en supposant ul est l'élément de la liste):

// option 1: innerHTML
ul.innerHTML += '<li>foobar</li>';

// option 2: DOM manipulation
var li = document.createElement('li');
li.appendChild(document.createTextNode('foobar'));
ul.appendChild(li);

Maintenant, la première option semble beaucoup plus simple, mais c'est seulement parce que le navigateur dispose d'une abstraction de beaucoup loin pour vous: en interne, le navigateur a convertir les enfants de l'élément d'une chaîne de caractères, puis ajouter un peu de contenu, puis de convertir la chaîne vers un DOM structure. La deuxième option correspond au navigateur natif de la compréhension de ce qui se passe.

La seconde considération importante est de réfléchir sur les limites du langage HTML. Quand vous pensez à une page web, pas tout ce qui est pertinent pour l'élément peut être sérialisé pour HTML. Par exemple, les gestionnaires d'événements liés avec x.onclick = function(); ou x.addEventListener(...) ne sera pas reproduit dans d' innerHTML, de sorte qu'ils ne soient pas copiés partout. Ainsi, les nouveaux éléments en y de ne pas avoir les écouteurs d'événement. Ce n'est probablement pas ce que vous voulez.

Donc, la façon de contourner cela est de travailler avec les natifs de DOM méthodes:

for (var i = 0; i < x.childNodes.length; i++) {
    y.appendChild(x.childNodes[i].cloneNode(true));
}

La lecture du MDN documentation sera probablement aider à comprendre cette façon de faire les choses:

Maintenant, le problème avec ce (comme avec l'option 2 dans l'exemple de code ci-dessus), c'est qu'il est très détaillé, bien plus que l' innerHTML option serait. C'est lorsque l'on apprécie d'avoir une bibliothèque JavaScript qui fait ce genre de chose pour vous. Par exemple, en jQuery:

$('#y').html($('#x').clone(true, true).contents());

C'est beaucoup plus explicite sur ce que vous voulez arriver. Ainsi que d'avoir divers avantages de performance et de préservation des gestionnaires d'événements, par exemple, il vous aide également à comprendre ce que votre code est en train de faire. Ce qui est bon pour votre âme comme un programmeur JavaScript et fait bizarre d'erreurs significativement moins de chances!

9voto

Joe Points 34413
  1. Vous pouvez dupliquer les Id qui ont besoin d'être unique.
  2. jQuery méthode clone appel comme, $(element).clone(true); permet de cloner les données et les écouteurs d'événement, mais l'ID sera encore aussi être cloné. Afin d'éviter de dupliquer les Identifiants, n'utilisez pas de codes pour les éléments qui doivent être cloné.

7voto

Neal Points 68710
  1. Elle devrait être évitée, car alors vous perdez tout des gestionnaires qui peuvent avoir été sur que Élément du DOM.

  2. Vous pouvez essayer de contourner en ajoutant des clones des éléments du DOM au lieu de les écraser.

7voto

Šime Vidas Points 59994

Tout d'abord, nous allons définir la tâche qui doit être accomplie ici:

Tous les nœuds enfants de DIV x à être "copié" (avec toutes ses descendants = copie en profondeur) et "collé" dans le DIV y. Si l'un des descendants de l' x a un ou plusieurs gestionnaires d'événements liés à lui, nous serions sans doute voulez que ces gestionnaires de continuer à travailler sur les copies (une fois qu'ils ont été placés à l'intérieur d' y).

Maintenant, ce n'est pas une tâche triviale. Heureusement, la bibliothèque jQuery (et tous les autres bibliothèques populaires ainsi je suppose) offre une méthode pratique pour accomplir cette tâche: .clone(). En utilisant cette méthode, la solution pourrait être écrite comme suit:

$( x ).contents().clone( true ).appendTo( y );

La solution ci-dessus est la réponse à la question (2). Maintenant, nous allons aborder la question (1):

Cette

y.innerHTML = x.innerHTML;

n'est pas une mauvaise idée - c'est une terrible une. Laissez-moi vous expliquer...

La déclaration ci-dessus peut être décomposée en deux étapes.

  1. L'expression x.innerHTML est évaluée,

  2. Que la valeur de retour de cette expression (qui est une chaîne) est affecté y.innerHTML.

Les nœuds que nous voulons copier (les nœuds enfants de l' x) sont les nœuds DOM. Ce sont des objets qui existent dans la mémoire du navigateur. Lors de l'évaluation d' x.innerHTML, le navigateur sérialise (stringifies) les nœuds DOM dans une chaîne de caractères (code source HTML de la chaîne).

Maintenant, si nous avons besoin de cette chaîne (à stocker dans une base de données, par exemple), alors cette sérialisation serait compréhensible. Cependant, nous n'avons pas besoin d'une telle chaîne de caractères (au moins pas comme un produit fini).

Dans l'étape 2, nous sommes de l'affectation de cette chaîne d' y.innerHTML. Le navigateur évalue ce par l'analyse de la chaîne qui aboutit à un ensemble de nœuds qui sont ensuite insérées dans des DIV y (en tant que nœuds enfants).

Donc, pour résumer:

Nœuds enfants de l' x --> stringifying --> code source HTML de la chaîne --> analyse --> Nœuds (copies)

Alors, quel est le problème avec cette approche? Ainsi, les nœuds DOM peut contenir des propriétés et des fonctions qui ne peuvent pas et ne sera donc pas être sérialisé. La plus importante de la fonctionnalité sont des gestionnaires d'événements qui sont liés à des descendants d' x - les copies de ces éléments n'aurez pas de gestionnaires d'événement lié à eux. Les gestionnaires se sont perdus dans le processus.

Un parallèle intéressant peut être fait ici:

Signal numérique --> D/A conversion --> signal Analogique --> conversion A/D --> signal Numérique

Comme vous le savez probablement, le signal numérique n'est pas une copie exacte de l'original de signal numérique - quelques informations s'est perdu dans le processus.

J'espère que vous comprenez maintenant pourquoi y.innerHTML = x.innerHTML doit être évitée.

4voto

user113716 Points 143363

Je ne voudrais pas le faire tout simplement parce que vous demandez au navigateur de l'analyser à nouveau le balisage HTML a déjà été analysée.

Je serais plus enclin à utiliser le natif cloneNode(true) de dupliquer l'existant des éléments du DOM.

var node, i=0;

while( node = x.childNodes[ i++ ] ) {
    y.appendChild( node.cloneNode( true ) );
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X