Mode préféré de modification des éléments qui n'ont pas encore été créés (en plus des événements)

Il y a beaucoup de questions à propos de lier les manipulations futures à des éléments inexistants qui finissent par répondre avec live / delegate . Je me demande comment exécuter un rappel arbitraire (pour ajouter une classe ou déclencher un plugin, par exemple) à tous les éléments existants qui correspondent à un sélecteur et à tous les éléments futurs qui correspondent à ce même sélecteur qui doit encore être créé.

Il semble que la fonctionnalité principale du plugin livequery ait fait le noyau, mais l'autre partie, en attachant des rappels arbitraires, s'est perdue en quelque sorte.

Une autre réponse commune est la délégation d'événements, mais que faire si l'on n'a pas accès à tout le code du fournisseur qui crée les éléments pour que cela déclenche les événements?


Voici un code du monde réel:

// with livequery $('input[type=text], input[type=password], textarea, .basic_form .block select, .order_form .form_item select, .order_form .form_item input') .livequery(function(){ $(this) .focus(function(){ $(this).addClass('active'); }) .blur(function(){ $(this).removeClass('active'); }) .addClass('text'); }); // with live $('input[type=text], input[type=password], textarea, .basic_form .block select, .order_form .form_item select, .order_form .form_item input') .live('focus', function(){ $(this).addClass('active'); }) .live('blur', function(){ $(this).removeClass('active'); }); // now how to add the class to future elements? // (or apply another plugin or whatever arbitrary non-event thing) 

Une approche serait de surveiller lorsque de nouveaux nœuds sont ajoutés / supprimés et déclencher nos sélecteurs. Merci à @arnorhs que nous connaissons de l'événement DOMNodeInserted , dont j'ignore les problèmes de navigateurs croisés, dans l'espoir que ces petits correctifs IE pourraient quelque jour s'établir en amont à jQuery ou connaître les fonctions jQuery DOM pourraient être enveloppées.

Même si nous pouvions nous assurer que le navigateur multidirectionné par DOMNodeInserted aurait été ridicule, il serait ridicule de le lier à plusieurs sélecteurs. Des centaines d'éléments peuvent être créés à tout moment, et des dizaines d'appels sélecteurs potentiels sur chacun de ces éléments pourraient ramper.

Ma meilleure idée jusqu'à présent

Est-ce qu'il serait peut-être préférable de surveiller DOMNodeInserted / Deleted et / ou de s'accrocher aux routines de manipulation DOM de jQuery pour ne définir qu'un drapeau qu'un "re-init" devrait se produire? Ensuite, il pourrait y avoir une minuterie qui vérifie ce drapeau toutes les x secondes, en exécutant uniquement tous les sélecteurs / rappels lorsque le DOM a réellement changé.

Cela pourrait encore être très grave si vous ajoutez / supprimez des éléments en grand nombre à un rythme rapide (comme avec l'animation ou ____). La nécessité de re-analyser le DOM une fois pour chaque sélecteur enregistré toutes les x secondes pourrait être trop intense si x est faible et l'interface semble ralenti si x est élevé.

D'autres solutions nouvelles?

J'ajouterai une prime quand il me laissera. J'ai ajouté une bonté pour la solution la plus récente!

Fondamentalement, ce que je comprends est une approche plus orientée vers l'aspect de la manipulation du DOM. Celui qui peut permettre que de nouveaux éléments soient créés à l'avenir , et ils devraient être créés avec le document initial. Des modifications y sont également appliquées .

JS a pu faire tant de magie ces derniers temps que j'espère qu'il sera évident.

À mon avis, les activités de DOM Level 3, DOMNodeInserted (qui ne fonctionnent que pour les noeuds) et l' aide DOMSubtreeModified (qui déclenche pratiquement n'importe quelle modification, comme les modifications d'attribut) sont votre meilleur moyen d'accomplir cette tâche.

Bien sûr, le grand inconvénient de ces événements est que les Explorateurs Internet de ce monde ne les soutiennent pas
(… eh bien, IE9 le fait).

L'autre solution raisonnable pour ce problème, est de s'accrocher à toute méthode qui peut modifier le DOM. Mais alors, nous devons nous demander quelle est notre portée ici?

Est-ce que c'est juste assez pour traiter les méthodes de modification DOM à partir d'une bibliothèque spécifique comme jQuery? Que faire si, pour une raison ou une autre, une autre bibliothèque modifie le DOM ou même une méthode native?

Si c'est juste pour jQuery, nous n'avons pas besoin de .sub() du tout. Nous pourrions écrire des crochets sous la forme de:

HTML

 <div id="test">Test DIV</div> 

JS

 (function(_append, _appendTo, _after, _insertAfter, _before, _insertBefore) { $.fn.append = function() { this.trigger({ type: 'DOMChanged', newnode: arguments[0] }); return _append.apply(this, arguments); }; $.fn.appendTo = function() { this.trigger({ type: 'DOMChanged', newnode: this }); return _appendTo.apply(this, arguments); }; $.fn.after = function() { this.trigger({ type: 'DOMChanged', newnode: arguments[0] }); return _after.apply(this, arguments); }; // and so forth }($.fn.append, $.fn.appendTo, $.fn.after, $.fn.insertAfter, $.fn.before, $.fn.insertBefore)); $('#test').bind('DOMChanged', function(e) { console.log('New node: ', e.newnode); }); $('#test').after('<span>new span</span>'); $('#test').append('<p>new paragraph</p>'); $('<div>new div</div>').appendTo($('#test')); 

Un exemple en direct du code ci-dessus peut être trouvé ici: http://www.jsfiddle.net/RRfTZ/1/

Cela nécessite évidemment une liste complète des méthodes DOMmanip. Je ne sais pas si vous pouvez remplacer des méthodes natives comme .appendChild() avec cette approche. .appendChild est situé dans Element.prototype.appendChild par exemple, pourrait valoir un essai.

mettre à jour

J'ai testé l'écrasement de Element.prototype.appendChild etc. dans Chrome, Safari et Firefox (dernière version officielle). Fonctionne dans Chrome et Safari mais pas dans Firefox!


Il pourrait y avoir d'autres façons de s'attaquer à l'exigence. Mais je ne peux pas penser à une seule approche qui soit vraiment satisfaisante, comme compter / regarder tous les descendants d'un noeud (ce qui nécessiterait un interval ou un timeouts , eeek).

Conclusion

Un mélange d'événements DOM Level 3 où les méthodes DOMmanip prises en charge et accrochées sont probablement les meilleures que vous pouvez faire ici.

Je lisais la nouvelle version de jQuery, la version 1.5 et j'ai immédiatement pensé à cette question.

Avec jQuery 1.5, vous pouvez réellement créer votre propre version de jQuery en utilisant quelque chose appelé jQuery.sub ();

De cette façon, vous pouvez effectivement remplacer les fonctions .append (), insert (), .html (), .. dans jQuery et créer votre propre événement personnalisé appelé "mydomchange" – sans qu'il affecte tous les autres scripts.

Vous pouvez donc faire quelque chose comme ceci (copié à partir de la documentation .sub () avec mod mineur):

 var sub$ = jQuery.sub(); sub$.fn.insert = function() { // New functionality: Trigger a domchange event this.trigger("domchange"); // Be sure to call the original jQuery remove method return jQuery.fn.insert.apply( this, arguments ); }; 

Vous devriez le faire à toutes les méthodes de manipulation dom …

JQuery.sub () dans la documentation jQuery: http://api.jquery.com/jQuery.sub/

Grande question

Il semble y avoir un événement personnalisé que vous pouvez lier: http://javascript.gakaa.com/domnodeinserted-description.aspx

Je suppose donc que vous pourriez faire quelque chose comme:

 $(document).bind('DOMNodeInserted',function(){ /* do stuff */ }); 

Mais je n'ai pas essayé, donc je n'ai pas la moindre idée …

Btw .: question connexe: Java peut-il écouter "onDomChange" sur tous les éléments Dom?

Il n'y a pas de façon simple et évidente de le faire. La seule approche infaillible est le sondage actif, ce qui provoque l'apparition d'un rattrapage lorsque le nouvel élément est créé et lorsque le sondage l'observe. Cela peut également faire en sorte que votre page prenne beaucoup de ressources selon la fréquence à laquelle vous interrogez la page. Vous pouvez également coupler cela, comme vous l'avez observé, avec plusieurs événements spécifiques au navigateur, afin d'améliorer au mieux les résultats de ces navigateurs.

Vous pouvez remplacer les fonctions de modification DOM de jQuery pour déclencher un événement de changement personnalisé (et utiliser $ .live pour attraper ces événements pour la manipulation), mais lorsque j'ai essayé ceci dans le passé, il est subtilement cassé divers plugins jQuery (je pense que c'est un peu Ces plugins font quelque chose de similaire). À la fin, j'ai renoncé à faire de manière fiable car je ne veux pas renoncer à la performance et faire un hoquet aux sondages actifs, et il n'existe pas d'autre moyen complet de le faire. Au lieu de cela, j'ai un événement d'initialisation, je m'assure de déclencher pour chaque changement de DOM que je fais, et je lier mes événements de manipulation à ceux-ci.

Soyez prudent, il est facile de rester coincé dans une boucle d'événements infinie si vous ne pensez pas aux choses, et cela peut aussi être subtil et difficile à repérer; Et pire encore peut-être pour un cas d'angle que votre test d'unité n'a pas permis (de sorte que vos utilisateurs l'expérimentent au lieu de seulement vous). L'événement d'initialisation manuellement déclenché manuellement est plus facile à diagnostiquer dans ce sens, car vous savez toujours exactement quand vous le déclenchez.

Eh bien, tout d'abord, vous ne devriez même pas utiliser des sélecteurs comme celui-ci si vous êtes préoccupé par la perf.

 $('input[type=text], input[type=password], textarea, .basic_form .block select, .order_form .form_item select, .order_form .form_item input') 

Tout navigateur qui n'a pas d'implémentations natives de xpath (plus que IE iirc) ou getElementsByClassName (IE 7 et ci-dessous) pourrait facilement passer quelques secondes à mâcher cela sur un grand site, donc le sondage serait évidemment complètement hors de question Si vous le souhaitez tellement large.