jquery détectant le div de certaines classes a été ajouté au DOM

90

J'utilise .on()pour lier les événements de divs créés après le chargement de la page. Cela fonctionne bien pour cliquer, mouseenter ... mais j'ai besoin de savoir quand un nouveau div de la classe MyClass a été ajouté. Je recherche ceci:

$('#MyContainer').on({

  wascreated: function () { DoSomething($(this)); }

}, '.MyClass');

Comment puis-je faire cela? J'ai réussi à écrire toute mon application sans plugin et je souhaite que cela reste ainsi.

Merci.

frenchie
la source
Est-ce votre propre code qui génère les éléments? Si tel est le cas, vous pouvez vérifier au moment de sa création et agir en conséquence.
Surreal Dreams
Oui, c'est vrai, mais avec .on (), je peux lier des événements à document.ready et je n'ai pas à me soucier de la liaison lorsque je génère le HTML. Je recherche le même comportement avec DoSomething.
frenchie
vous pouvez utiliser $ ("div / your-selector"). hasClass ("MyClass"); pour vérifier si un div a la classe particulière ou non. Je suppose que vous pourriez mettre cette API à votre disposition.
KBN
.on () est juste pour les événements. Vous pouvez configurer un événement personnalisé, mais vous devez toujours déclencher cet événement lorsque vous créez de nouveaux éléments.
Surreal Dreams
3
Duplicate of Event lorsque l'élément ajouté à la page
Dan Dascalescu

Réponses:

102

Auparavant, on pouvait se connecter à la domManipméthode de jQuery pour attraper toutes les manipulations de jQuery dom et voir quels éléments étaient insérés, etc. J'ai fait en sorte que l'internedomManip méthode ne soit plus disponible en dehors du code jQuery principal.

Les événements de mutation sont également obsolètes, car auparavant on pouvait faire quelque chose comme

$(document).on('DOMNodeInserted', function(e) {
    if ( $(e.target).hasClass('MyClass') ) {
       //element with .MyClass was inserted.
    }
});

cela devrait être évité, et aujourd'hui les observateurs de mutation devraient être utilisés à la place, qui fonctionneraient comme ceci

var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        console.log(mutation)
        if (mutation.addedNodes && mutation.addedNodes.length > 0) {
            // element added to DOM
            var hasClass = [].some.call(mutation.addedNodes, function(el) {
                return el.classList.contains('MyClass')
            });
            if (hasClass) {
                // element has class `MyClass`
                console.log('element ".MyClass" added');
            }
        }
    });
});

var config = {
    attributes: true,
    childList: true,
    characterData: true
};

observer.observe(document.body, config);
adeneo
la source
Firefox et webkit devraient à peu près être OK, mais le support pour cela est quelque peu médiocre dans IE. Cela devrait fonctionner dans IE9, mais probablement pas autant dans IE8. Je sais que j'ai vu des solutions de contournement, et je n'ai pas vraiment testé cela dans IE, alors testez-le d'abord, si cela ne fonctionne pas, voyez s'il existe une solution de contournement. Il y a un plugin qui semble ajouter le support IE ici , mais je ne l'ai pas essayé, je l'ai juste trouvé dans une recherche Google.
adeneo
2
J'ai d'abord utilisé votre réponse, mais le fait que cela ne fonctionnait pas entre les navigateurs était un problème. Ensuite, j'ai rapidement trouvé une solution pour éviter ce problème en premier lieu. Et puis, il y a quelques semaines, ce problème de détection de l'insertion de nœuds est revenu à nouveau dans mon code. Cette fois, le code que j'ai posté dans ma réponse fonctionne EXACTEMENT comme il se doit, cross-browser et rien de bogué. C'est pourquoi j'ai proposé ma réponse comme acceptée; c'est ainsi que fonctionne SO, le PO décide quelle réponse il accepte et la foule vote les réponses. Au fil du temps, les gens voteront et décideront quelle réponse leur convient le mieux.
frenchie
2
Et aussi, je voulais dire que vous m'avez aidé à plusieurs reprises avec des réponses de haute qualité qui ont sauvé ma journée. Changer la réponse acceptée par la mienne est purement basé sur le code et la façon dont il correspond au problème que j'ai dû résoudre.
frenchie
got "Uncaught TypeError: Cannot read property 'contains' of undefined" avec ce code
gordie
53

Voici mon plugin qui fait exactement cela - jquery.initialize

L'utilisation est la même que celle que vous utiliseriez pour la .eachfonction, mais avec la .initializefonction sur l'élément, la différence .eachest qu'elle initialisera également les éléments ajoutés à l'avenir sans aucun code supplémentaire - peu importe si vous l'ajoutez avec AJAX ou autre chose.

Initialize a exactement la même syntaxe qu'avec la fonction .each

$(".some-element").initialize( function(){
    $(this).css("color", "blue");
});

Mais maintenant, si un nouvel élément correspondant au sélecteur d'élément .some apparaîtra sur la page, il sera instantanément initialisé. La façon dont un nouvel élément est ajouté n'est pas importante, vous n'avez pas besoin de vous soucier des rappels, etc.

$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!

Le plugin est basé sur MutationObserver

tarte6k
la source
Merci beaucoup.
Brandon Harris
comment arrêter l'initialisation? Je veux arrêter de regarder une fois que ce que je cherchais est ajouté ...
Evgeny Vostok
$ (". some-element"). initialize [..] est obsolète. Utilisez $ .initialize (". AddDialog", function () {$ ('. NoData'). Hide ();}); au lieu.
Heimi
Est-il possible d'exécuter la .initializefonction callback uniquement pour les éléments ajoutés dynamiquement (par exemple via Ajax), et d'ignorer le rappel pour les éléments déjà présents lors du chargement initial de la page? J'essaye d'ajouter un paramètre à optionsafin d'accomplir ceci, mais jusqu'à présent, je n'ai pas réussi à le faire.
Nevermore
Vous pouvez ajouter un problème sur github à ce sujet. Pour l'instant, techniquement, vous pouvez le faire$('.myClass').addClass('ignore-initialize'); $.initialize('.myClass:not(.ignore-initialize)', callback);
pie6k
19

Après avoir examiné ceci et plusieurs autres articles, j'ai essayé de distiller ce que je pensais être le meilleur de chacun en quelque chose de simple qui me permettait de détecter lorsqu'une classe d'éléments est insérée, puis d' agir sur ces éléments .

function onElementInserted(containerSelector, elementSelector, callback) {

    var onMutationsObserved = function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.addedNodes.length) {
                var elements = $(mutation.addedNodes).find(elementSelector);
                for (var i = 0, len = elements.length; i < len; i++) {
                    callback(elements[i]);
                }
            }
        });
    };

    var target = $(containerSelector)[0];
    var config = { childList: true, subtree: true };
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    var observer = new MutationObserver(onMutationsObserved);    
    observer.observe(target, config);

}

onElementInserted('body', '.myTargetElement', function(element) {
    console.log(element);
});

Ce qui est important pour moi, cela permet a) à l'élément cible d'exister à n'importe quelle profondeur dans "addedNodes" et b) la possibilité de traiter l'élément uniquement lors de son insertion initiale (pas besoin de rechercher l'ensemble du paramètre du document ou d'ignorer "déjà traité" drapeaux).

John Clegg
la source
1
Votre condition devrait être if(mutation.addedNodes.length)parce que addedNodeset les removedNodestableaux sont toujours mutationde type childList(vérifié dans IE11, Chrome53, FF49). Toujours +1, car la vôtre est la seule réponse où elle addedNodesest prise en compte (elle callbackn'est appelée que lorsqu'un nœud est ajouté); non seulement sur ce fil, mais sur de nombreux autres fils. Tout le monde appelle simplement le callbackpour toutes les mutations, même si le nœud est en cours de suppression.
Vivek Athalye
17

3 ans d'expérience plus tard, c'est ainsi que j'écoute "élément d'une certaine classe ajoutée au DOM" : vous ajoutez simplement un hook dans la html()fonction jQuery , comme ceci:

function Start() {

   var OldHtml = window.jQuery.fn.html;

   window.jQuery.fn.html = function () {

     var EnhancedHtml = OldHtml.apply(this, arguments);

     if (arguments.length && EnhancedHtml.find('.MyClass').length) {

         var TheElementAdded = EnhancedHtml.find('.MyClass'); //there it is
     }

     return EnhancedHtml;
   }
}

$(Start);

Cela fonctionne si vous utilisez jQuery, ce que je fais. Et cela ne dépend pas de l'événement spécifique au navigateur DOMNodeInserted, qui n'est pas compatible avec plusieurs navigateurs. J'ai également ajouté la même implémentation pour.prepend()

Dans l'ensemble, cela fonctionne comme un charme pour moi et, espérons-le, pour vous aussi.

frenchie
la source
6
Hmm - Je préciserais que cela fonctionne si vous utilisez exclusivement l'aide de jQuery htmlpour modifier le DOM, pas seulement "en utilisant jQuery" en général. Les éléments ajoutés à l'aide, par exemple, jQuery.appendne déclenchent pas cet événement.
iameli
1
Oui, et c'est pourquoi j'ai précisé que j'ai ajouté la même implémentation avec .preprend () et si vous utilisez .append () alors faites la même chose avec .append ()
frenchie
Il existe une approche beaucoup plus rapide, utilisant des animations CSS3 . La bibliothèque open-source prête également à un code beaucoup plus simple:insertionQ('#intercom-container').every(function(/element){ element.style.display = 'none'; });
Dan Dascalescu
3
Pourquoi le vote négatif? Cela fonctionne réellement: pas de plugin et pas de fonction hacky setTimeout.
frenchie
2
Vote positif - c'est une option valable - pourquoi diable (!!!) cela a-t-il été décliné ?????? Dan, les animations CSS3 ne sont pas XBC: caniuse.com/#search=keyframes ; davidwalsh.name/detect-node-insertion .
Cody
6

vous pourriez utiliser des événements de mutation

http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-mutationevents

ÉDITER

depuis MDN: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events

Obsolète Cette fonctionnalité a été supprimée du Web. Bien que certains navigateurs puissent toujours le prendre en charge, il est en cours de suppression. Ne l'utilisez pas dans des projets anciens ou nouveaux. Les pages ou applications Web qui l'utilisent peuvent être interrompues à tout moment.

Les observateurs de mutation sont le remplacement proposé pour les événements de mutation dans DOM4. Ils doivent être inclus dans Firefox 14 et Chrome 18.

https://developer.mozilla.org/en/docs/Web/API/MutationObserver

MutationObserverfournit aux développeurs un moyen de réagir aux changements dans un DOM. Il est conçu pour remplacer les événements de mutation définis dans la spécification des événements DOM3.

Exemple d'utilisation

L'exemple suivant est tiré de http://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/ .

// select the target node
var target = document.querySelector('#some-id');

// create an observer instance
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log(mutation.type);
  });    
});

// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true };

// pass in the target node, as well as the observer options
observer.observe(target, config);

// later, you can stop observing
observer.disconnect();
RVB
la source
2
Les événements de mutation sont à peu près obsolètes selon mdn , en faveur de MutationObservers
Olga
Maintenant que IE10 est EOL, il devrait être presque sûr de dire que MutationObserver est largement pris en charge.
rymo
0

Deux ajouts à la réponse d'Adeneo:

(1) nous pouvons changer la ligne

return el.classList.contains('MyClass');

À

if( el.classList ) {
    return el.classList.contains('MyClass');
} else {
    return false;
}

Nous ne verrons donc pas l' "Uncaught TypeError: Cannot read property 'contains' of undefined"erreur.

(2) Ajouter subtree: trueà configtrouver des noeuds ajoutés récursive dans tous les éléments ajoutés.

agrul
la source