Comme mentionné dans plusieurs autres réponses, événements de mutation ont été dépréciés, vous devriez donc utiliser MutationObserver à la place. Puisque personne n'a encore donné de détails à ce sujet, voici...
API JavaScript de base
L'API de MutationObserver est assez simple. Elle n'est pas aussi simple que celle des événements de mutation, mais elle est tout de même acceptable.
function callback(records) {
records.forEach(function (record) {
var list = record.addedNodes;
var i = list.length - 1;
for ( ; i > -1; i-- ) {
if (list[i].nodeName === 'SELECT') {
// Insert code here...
console.log(list[i]);
}
}
});
}
var observer = new MutationObserver(callback);
var targetNode = document.body;
observer.observe(targetNode, { childList: true, subtree: true });
<script>
// For testing
setTimeout(function() {
var $el = document.createElement('select');
document.body.appendChild($el);
}, 500);
</script>
Décomposons ça.
var observer = new MutationObserver(callback);
Cela crée l'observateur. L'observateur ne regarde encore rien ; c'est juste l'endroit où l'écouteur d'événement est attaché.
observer.observe(targetNode, { childList: true, subtree: true });
Cela fait démarrer l'observateur. Le premier argument est le nœud sur lequel l'observateur va surveiller les changements. Le deuxième argument est le options pour savoir ce qu'il faut surveiller .
-
childList
signifie que je veux surveiller les éléments enfants qui sont ajoutés ou retirés.
-
subtree
est un modificateur qui étend childList
pour surveiller les changements survenant n'importe où dans la sous-arborescence de cet élément (autrement, il ne regarderait que les changements directement dans l'arborescence targetNode
).
Les deux autres options principales, outre childList
sont attributes
y characterData
ce qui signifie à peu près ce à quoi ils ressemblent. Vous devez utiliser l'un de ces trois.
function callback(records) {
records.forEach(function (record) {
Les choses se compliquent un peu dans le callback. Le callback reçoit un tableau de MutationRecord s. Chaque MutationRecord peut décrire plusieurs modifications d'un même type ( childList
, attributes
o characterData
). Puisque j'ai seulement dit à l'observateur de surveiller childList
je ne prendrai pas la peine de vérifier le type.
var list = record.addedNodes;
Ici, je récupère une NodeList de tous les nœuds enfants qui ont été ajoutés. Cette liste sera vide pour tous les enregistrements où des noeuds n'ont pas été ajoutés (et il peut y avoir beaucoup de tels enregistrements).
A partir de là, je fais une boucle à travers les noeuds ajoutés et trouve ceux qui sont <select>
éléments.
Rien de vraiment complexe ici.
jQuery
...mais vous avez demandé jQuery. Bien.
(function($) {
var observers = [];
$.event.special.domNodeInserted = {
setup: function setup(data, namespaces) {
var observer = new MutationObserver(checkObservers);
observers.push([this, observer, []]);
},
teardown: function teardown(namespaces) {
var obs = getObserverData(this);
obs[1].disconnect();
observers = $.grep(observers, function(item) {
return item !== obs;
});
},
remove: function remove(handleObj) {
var obs = getObserverData(this);
obs[2] = obs[2].filter(function(event) {
return event[0] !== handleObj.selector && event[1] !== handleObj.handler;
});
},
add: function add(handleObj) {
var obs = getObserverData(this);
var opts = $.extend({}, {
childList: true,
subtree: true
}, handleObj.data);
obs[1].observe(this, opts);
obs[2].push([handleObj.selector, handleObj.handler]);
}
};
function getObserverData(element) {
var $el = $(element);
return $.grep(observers, function(item) {
return $el.is(item[0]);
})[0];
}
function checkObservers(records, observer) {
var obs = $.grep(observers, function(item) {
return item[1] === observer;
})[0];
var triggers = obs[2];
var changes = [];
records.forEach(function(record) {
if (record.type === 'attributes') {
if (changes.indexOf(record.target) === -1) {
changes.push(record.target);
}
return;
}
$(record.addedNodes).toArray().forEach(function(el) {
if (changes.indexOf(el) === -1) {
changes.push(el);
}
})
});
triggers.forEach(function checkTrigger(item) {
changes.forEach(function(el) {
var $el = $(el);
if ($el.is(item[0])) {
$el.trigger('domNodeInserted');
}
});
});
}
})(jQuery);
Cela crée un nouvel événement appelé domNodeInserted
en utilisant le API pour les événements spéciaux de jQuery . Vous pouvez l'utiliser comme suit :
$(document).on("domNodeInserted", "select", function () {
$(this).combobox();
});
Je suggérerais personnellement de chercher un cours, car certaines bibliothèques créent select
éléments à des fins de test.
Naturellement, vous pouvez aussi utiliser .off("domNodeInserted", ...)
ou affiner la surveillance en passant dans les données comme ceci :
$(document.body).on("domNodeInserted", "select.test", {
attributes: true,
subtree: false
}, function () {
$(this).combobox();
});
Cela déclencherait la vérification de l'apparition d'un select.test
chaque fois que les attributs des éléments situés directement dans le corps sont modifiés.
Vous pouvez le voir en direct ci-dessous ou sur jsFiddle .
(function($) {
$(document).on("domNodeInserted", "select", function() {
console.log(this);
//$(this).combobox();
});
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
// For testing
setTimeout(function() {
var $el = document.createElement('select');
document.body.appendChild($el);
}, 500);
</script>
<script>
(function($) {
var observers = [];
$.event.special.domNodeInserted = {
setup: function setup(data, namespaces) {
var observer = new MutationObserver(checkObservers);
observers.push([this, observer, []]);
},
teardown: function teardown(namespaces) {
var obs = getObserverData(this);
obs[1].disconnect();
observers = $.grep(observers, function(item) {
return item !== obs;
});
},
remove: function remove(handleObj) {
var obs = getObserverData(this);
obs[2] = obs[2].filter(function(event) {
return event[0] !== handleObj.selector && event[1] !== handleObj.handler;
});
},
add: function add(handleObj) {
var obs = getObserverData(this);
var opts = $.extend({}, {
childList: true,
subtree: true
}, handleObj.data);
obs[1].observe(this, opts);
obs[2].push([handleObj.selector, handleObj.handler]);
}
};
function getObserverData(element) {
var $el = $(element);
return $.grep(observers, function(item) {
return $el.is(item[0]);
})[0];
}
function checkObservers(records, observer) {
var obs = $.grep(observers, function(item) {
return item[1] === observer;
})[0];
var triggers = obs[2];
var changes = [];
records.forEach(function(record) {
if (record.type === 'attributes') {
if (changes.indexOf(record.target) === -1) {
changes.push(record.target);
}
return;
}
$(record.addedNodes).toArray().forEach(function(el) {
if (changes.indexOf(el) === -1) {
changes.push(el);
}
})
});
triggers.forEach(function checkTrigger(item) {
changes.forEach(function(el) {
var $el = $(el);
if ($el.is(item[0])) {
$el.trigger('domNodeInserted');
}
});
});
}
})(jQuery);
</script>
Note
Ce code jQuery est une implémentation assez basique. Il ne se déclenche pas dans les cas où des modifications apportées ailleurs rendent votre sélecteur valide.
Par exemple, supposons que votre sélecteur soit .test select
et le document a déjà un <select>
. Ajout de la classe test
a <body>
rendra le sélecteur valide, mais parce que je vérifie seulement record.target
y record.addedNodes
l'événement ne se déclenche pas. Le changement doit se produire dans l'élément que vous souhaitez sélectionner lui-même.
Cela pourrait être évité en demandant le sélecteur à chaque fois que des mutations se produisent. J'ai choisi de ne pas le faire pour éviter de provoquer des événements en double pour des éléments qui ont déjà été traités. Traiter correctement les adjacent o combinateurs généraux de frères et sœurs rendrait les choses encore plus délicates.
Pour une solution plus complète, voir https://github.com/pie6k/jquery.initialize comme indiqué dans Damien Ó Ceallaigh 's réponse . Cependant, l'auteur de cette bibliothèque a annoncé que celle-ci était ancienne et suggère de ne pas utiliser jQuery pour cela.