jquery que detecta div de cierta clase se ha agregado a DOM

90

Estoy usando .on()para vincular eventos de divs que se crean después de que se carga la página. Funciona bien para hacer clic, mouseenter ... pero necesito saber cuándo se ha agregado un nuevo div de la clase MyClass. Estoy buscando esto:

$('#MyContainer').on({

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

}, '.MyClass');

¿Cómo hago esto? Me las arreglé para escribir toda mi aplicación sin un complemento y quiero que siga siendo así.

Gracias.

Frenchie
fuente
¿Es tu propio código el que genera los elementos? Si es así, puede comprobarlo en el momento de crearlo y actuar en consecuencia.
Surreal Dreams
Sí, eso es cierto, pero con .on (), puedo vincular eventos en document.ready y luego no tengo que preocuparme por la vinculación cuando genero el HTML. Estoy buscando el mismo comportamiento con DoSomething.
frenchie
puede usar $ ("div / your-selector"). hasClass ("MyClass"); para comprobar si un div tiene la clase particular o no. Supongo que podrías poner esta API a tu disposición.
KBN
.on () es solo para eventos. Puede configurar un evento personalizado, pero aún tendrá que activar ese evento cuando cree nuevos elementos.
Surreal Dreams
3
Duplicado del evento cuando se agregó el elemento a la página
Dan Dascalescu

Respuestas:

102

Anteriormente, uno podía conectarse al domManipmétodo de jQuery para capturar todas las manipulaciones de jQuery dom y ver qué elementos se insertaron, etc., pero el equipo de jQuery lo cerró en jQuery 3.0+, ya que generalmente no es una buena solución conectarse a los métodos de jQuery de esa manera, y ellos ' Lo hemos hecho para que el domManipmétodo interno ya no esté disponible fuera del código jQuery principal.

Los eventos de mutación también se han desaprobado, ya que antes se podía hacer algo como

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

esto debe evitarse, y hoy en día se deben usar los observadores de mutaciones, que funcionarían así

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
fuente
Firefox y webkit deberían estar bastante bien, pero el soporte para esto es algo pobre en IE. Debería funcionar en IE9, pero probablemente no tanto en IE8. Sé que he visto soluciones alternativas, y realmente no lo he probado en IE, así que pruébelo primero, si no funciona, luego vea si hay una solución alternativa. Hay un complemento que parece que agrega compatibilidad con IE aquí , pero no lo he probado, solo lo encontré en una búsqueda de Google.
adeneo
2
Inicialmente usé su respuesta, pero el hecho de que no funcionara en varios navegadores fue un problema. Luego, rápidamente descubrí una solución alternativa para evitar este problema en primer lugar. Y luego, hace unas semanas, este problema de detección de inserción de nodos regresó nuevamente en mi código. Esta vez, el código que publiqué en mi respuesta funciona EXACTAMENTE como debería, en todos los navegadores y sin errores. Por eso moví mi respuesta como aceptada; así es como funciona SO, el OP decide qué respuesta acepta y la multitud vota por las respuestas. Con el tiempo, la gente votará y decidirá qué respuesta les conviene más.
frenchie
2
Y también, quería decir que me has ayudado varias veces con respuestas de alta calidad que me salvaron el día. Cambiar la respuesta aceptada a la mía se basa puramente en el código y cómo encaja con el problema que tenía que resolver.
frenchie
obtuvo "Uncaught TypeError: No se puede leer la propiedad 'contiene' de indefinido" con este código
gordie
53

Aquí está mi complemento que hace exactamente eso: jquery.initialize

El uso es el mismo que usaría la .eachfunción, pero con la .initializefunción en el elemento, la diferencia .eaches que también inicializará los elementos agregados en el futuro sin ningún código adicional, sin importar si lo agrega con AJAX o cualquier otra cosa.

Initialize tiene exactamente la misma sintaxis que con cada función.

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

Pero ahora, si un nuevo elemento que coincide con el selector de algún elemento aparecerá en la página, se inicializará instantáneamente. La forma en que se agrega el nuevo elemento no es importante, no necesita preocuparse por las devoluciones de llamada, etc.

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

El complemento se basa en MutationObserver

pie6k
fuente
Muchas gracias.
Brandon Harris
¿Cómo detener la inicialización? Quiero dejar de mirar una vez que se agreguen las cosas que estaba buscando ...
Evgeny Vostok
$ (". algún-elemento"). initialize [..] está obsoleto. Use $ .initialize (". AddDialog", function () {$ ('. NoData'). Hide ();}); en lugar.
Heimi
¿Es posible ejecutar la .initializefunción callback solo para elementos que se agregan dinámicamente (por ejemplo, a través de Ajax) y omitir la devolución de llamada para elementos ya presentes cuando la página se carga inicialmente? Estoy tratando de agregar un parámetro para optionslograr esto, pero hasta ahora no he logrado que esto funcione.
Nevermore
Puede agregar un problema en github sobre eso. Por ahora, técnicamente podría hacerlo$('.myClass').addClass('ignore-initialize'); $.initialize('.myClass:not(.ignore-initialize)', callback);
pie6k
19

Después de revisar esta y varias otras publicaciones, traté de destilar lo que pensé que era lo mejor de cada una en algo simple que me permitiera detectar cuándo se inserta una clase de elementos y luego actuar sobre esos elementos .

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

Es importante para mí que esto permite a) que el elemento de destino exista a cualquier profundidad dentro de "addedNodes" yb) la capacidad de tratar con el elemento solo cuando se inserta inicialmente (no es necesario buscar en toda la configuración del documento o ignorar "ya procesado" banderas).

John Clegg
fuente
1
Su condición debería ser if(mutation.addedNodes.length)porque addedNodesy las removedNodesmatrices siempre están allí en el mutationtipo childList(marcado en IE11, Chrome53, FF49). Aún así +1, porque la suya es la única respuesta donde addedNodesse considera ( callbackse llama solo cuando se agrega un nodo); no solo en este hilo sino en muchos otros hilos. Todos los demás están invocando callbackpara todas las mutaciones, incluso si el nodo se está eliminando.
Vivek Athalye
17

3 años de experiencia más tarde, así es como escucho el "elemento de una determinada clase agregado al DOM" : simplemente agrega un gancho en la html()función jQuery , así:

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);

Esto funciona si estás usando jQuery, que yo hago. Y no depende del evento específico del navegador DOMNodeInserted, que no es compatible con varios navegadores. También agregué la misma implementación para.prepend()

En general, esto funciona de maravilla para mí y, con suerte, también para ti.

Frenchie
fuente
6
Hmm, aclararía que esto funciona si está usando exclusivamente el htmlayudante de jQuery para modificar el DOM, no solo "usando jQuery" en general. Los elementos agregados usando, digamos, jQuery.appendno activarán este evento.
iameli
1
Sí, y es por eso que aclaré que agregué la misma implementación con .preprend () y si usa .append (), haga lo mismo con .append ()
frenchie
Hay un enfoque mucho más rápido, usando animaciones CSS3 . La biblioteca de código abierto también se presta a un código mucho más simple:insertionQ('#intercom-container').every(function(/element){ element.style.display = 'none'; });
Dan Dascalescu
3
¿Por qué el voto negativo? Esto realmente funciona: sin plugin ni función hacky setTimeout.
frenchie
2
Upvoted - esta es una opción válida - ¿por qué en el mundo (!!!) ha sido downvoted ?????? Dan, las animaciones CSS3 no son XBC: caniuse.com/#search=keyframes ; davidwalsh.name/detect-node-insertion .
Cody
6

podrías usar eventos de mutación

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

EDITAR

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

En desuso Esta función se ha eliminado de la Web. Aunque es posible que algunos navegadores aún lo admitan, está en proceso de descartarse. No lo utilice en proyectos nuevos o antiguos. Las páginas o aplicaciones web que lo utilizan pueden romperse en cualquier momento.

Los observadores de mutaciones son el reemplazo propuesto para los eventos de mutación en DOM4. Deben incluirse en Firefox 14 y Chrome 18.

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

MutationObserverproporciona a los desarrolladores una forma de reaccionar a los cambios en un DOM. Está diseñado para reemplazar los eventos de mutación definidos en la especificación de eventos DOM3.

Uso de ejemplo

El siguiente ejemplo se tomó 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();
RGB
fuente
2
Los eventos de mutación están bastante desaprobados según mdn , a favor de MutationObservers
Olga
Ahora que IE10 es EOL, debería ser casi seguro decir que MutationObserver es ampliamente compatible.
Rymo
0

Dos adiciones a la respuesta de adeneo:

(1) podemos cambiar la línea

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

A

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

Entonces no veremos el "Uncaught TypeError: Cannot read property 'contains' of undefined"error.

(2) Añadir subtree: truea configencontrar nodos añadidos de forma recursiva en todos los elementos añadidos.

agrul
fuente