Muszę mieć możliwość dynamicznego tworzenia <select>
elementu i przekształcania go w jQuery .combobox()
. Powinno to być zdarzenie tworzenia elementu, w przeciwieństwie do jakiegoś zdarzenia „kliknięcia”, w którym to przypadku mógłbym po prostu użyć jQuery .on()
.
Więc czy coś takiego istnieje?
$(document).on("create", "select", function() {
$(this).combobox();
}
Niechętnie korzystam z zapytania na żywo, ponieważ jest bardzo przestarzałe.
AKTUALIZACJA Wspomniany select / combobox jest ładowany przez ajax do colorbox jQuery (okno modalne), stąd problem - mogę zainicjować combobox tylko przy użyciu colorbox onComplete
, jednak przy zmianie jednego combobox inny select / combobox musi być utworzony dynamicznie, dlatego potrzebuję bardziej ogólny sposób wykrywania tworzenia elementu ( select
w tym przypadku).
UPDATE2 Aby spróbować dalej wyjaśnić problem - mam select/combobox
elementy tworzone rekurencyjnie, wewnątrz jest też dużo kodu inicjującego .combobox()
, dlatego gdybym użył klasycznego podejścia, jak w odpowiedzi @ bipen , mój kod nadymałby się do szalonych poziomów. Mam nadzieję, że to lepiej wyjaśnia problem.
UPDATE3 Dziękuję wszystkim, teraz rozumiem, że od czasu wycofania DOMNodeInserted
mutacji DOM pozostała pustka i nie ma rozwiązania tego problemu. Muszę tylko przemyśleć moją aplikację.
źródło
$(select).ready(function() { });
ready
służy do: „Określania funkcji do wykonania, gdy DOM jest w pełni załadowany”. (z jquery.com)combobox()
metody w momencie tworzenia / wstawiania, a nie później?$(document).on('onComplete', 'select', function(){ // bind here? });
? (Oczywiście użyj bliższego rodzica niż tendocument
.)Odpowiedzi:
Można wydarzenie uzyskać zdarzenie dla gdy jest on dodawany do dokumentu o kodzie.
on
DOMNodeInserted
$('body').on('DOMNodeInserted', 'select', function () { //$(this).combobox(); }); $('<select>').appendTo('body'); $('<select>').appendTo('body');
Fiddled tutaj: http://jsfiddle.net/Codesleuth/qLAB2/3/
EDYCJA: po przeczytaniu wystarczy, że dwukrotnie sprawdzę,
DOMNodeInserted
czy nie spowoduje to problemów w przeglądarkach. To pytanie z 2010 roku sugeruje, że IE nie obsługuje tego wydarzenia, więc wypróbuj je, jeśli możesz.Zobacz tutaj: [link] Ostrzeżenie! typ zdarzenia DOMNodeInserted jest zdefiniowany w tej specyfikacji w celu uzyskania odniesienia i kompletności, ale ta specyfikacja jest przestarzała, gdy używa się tego typu zdarzenia.
źródło
.delegate
przestarzałe z wersji 1.7 jQuery i zastąpione przez.on
?DOMNodeInserted
jest teraz przestarzała.on('DOMNodeInserted'
zanim opublikowałem to pytanie. To oczywiście nie zadziałało.Możesz użyćDOMNodeInserted
zdarzenia mutacji (nie ma potrzeby delegowania):$('body').on('DOMNodeInserted', function(e) { var target = e.target; //inserted element; });
EDYCJA: zdarzenia mutacji są przestarzałe , zamiast tego użyj obserwatora mutacji
źródło
Jak wspomniano w kilku innych odpowiedziach, zdarzenia mutacji zostały wycofane, więc powinieneś używać MutationObserver zamiast tego . Ponieważ nikt jeszcze nie podał żadnych szczegółów na ten temat, proszę bardzo ...
Podstawowy interfejs API JavaScript
Interfejs API dla MutationObserver jest dość prosty. Nie jest to tak proste, jak zdarzenia mutacji, ale nadal jest w porządku.
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>
Rozbijmy to.
var observer = new MutationObserver(callback);
To tworzy obserwatora. Obserwator jeszcze niczego nie ogląda; to jest właśnie miejsce, w którym zostaje dołączony detektor zdarzeń.
observer.observe(targetNode, { childList: true, subtree: true });
To sprawia, że obserwator zaczyna działać. Pierwszym argumentem jest węzeł, na którym obserwator będzie obserwował zmiany. Drugi argument to opcje, na co należy uważać .
childList
oznacza, że chcę obserwować dodawanie lub usuwanie elementów podrzędnych.subtree
to modyfikator, który rozciąga się,childList
aby obserwować zmiany w dowolnym miejscu poddrzewa tego elementu (w przeciwnym razie sprawdzałby tylko zmiany bezpośrednio w obrębie tego elementutargetNode
).Pozostałe dwie główne opcje
childList
toattributes
icharacterData
, co oznacza, jak brzmią. Musisz użyć jednego z tych trzech.function callback(records) { records.forEach(function (record) {
Sprawy stają się trochę skomplikowane w wywołaniu zwrotnym. Wywołanie zwrotne otrzymuje tablicę MutationRecord s. Każdy MutationRecord można opisać kilka zmian jednego typu (
childList
,attributes
lubcharacterData
). Ponieważ powiedziałem obserwatorowi tylko, żeby na niego uważałchildList
, nie będę się trudził sprawdzaniem typu.var list = record.addedNodes;
Tutaj pobieram listę NodeList wszystkich dodanych węzłów podrzędnych. Będzie to puste dla wszystkich rekordów, do których nie dodano węzłów (a takich rekordów może być wiele).
Od tego momentu przeglądam dodane węzły i znajduję te, które są
<select>
elementy.Nie ma tu nic naprawdę skomplikowanego.
jQuery
... ale prosiłeś o jQuery. W porządku.
(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);
Spowoduje to utworzenie nowego zdarzenia o nazwie
domNodeInserted
przy użyciu interfejsu API wydarzeń specjalnych jQuery . Możesz go używać w ten sposób:$(document).on("domNodeInserted", "select", function () { $(this).combobox(); });
Osobiście sugerowałbym szukanie klasy, ponieważ niektóre biblioteki będą tworzyć
select
elementy do celów testowych.Oczywiście możesz również użyć
.off("domNodeInserted", ...)
lub dostosować obserwację, przekazując dane w następujący sposób:$(document.body).on("domNodeInserted", "select.test", { attributes: true, subtree: false }, function () { $(this).combobox(); });
Spowodowałoby to sprawdzanie wyglądu
select.test
elementu, gdy atrybuty elementów uległy zmianie bezpośrednio w treści.Możesz to zobaczyć na żywo poniżej lub na jsFiddle .
Pokaż fragment kodu
(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>
Uwaga
Ten kod jQuery jest dość podstawową implementacją. Nie uruchamia się w przypadkach, gdy modyfikacje w innym miejscu powodują, że twój selektor jest ważny.
Na przykład załóżmy, że twój selektor to,
.test select
a dokument ma już rozszerzenie<select>
. Dodanie klasytest
do<body>
spowoduje, że selektor będzie prawidłowy, ale ponieważ sprawdzam tylkorecord.target
irecord.addedNodes
, zdarzenie nie zostanie uruchomione. Zmiana musi nastąpić w elemencie, który chcesz sam zaznaczyć.Można tego uniknąć, wysyłając zapytanie o selektor za każdym razem, gdy wystąpią mutacje. Zdecydowałem się tego nie robić, aby uniknąć powielania zdarzeń dla elementów, które zostały już obsłużone. Właściwe postępowanie z sąsiadującymi lub ogólnymi kombinatorami rodzeństwa mogłoby jeszcze bardziej utrudnić sprawę.
Dla bardziej kompleksowego rozwiązania, zobacz https://github.com/pie6k/jquery.initialize , jak wspomniano w Damien Ó Ceallaigh „s odpowiedź . Jednak autor tej biblioteki ogłosił, że biblioteka jest stara i sugeruje, aby nie używać do tego jQuery.
źródło
Właśnie wymyśliłem to rozwiązanie, które wydaje się rozwiązać wszystkie moje problemy Ajax.
W przypadku gotowych wydarzeń używam teraz tego:
function loaded(selector, callback){ //trigger after page load. $(function () { callback($(selector)); }); //trigger after page update eg ajax event or jquery insert. $(document).on('DOMNodeInserted', selector, function () { callback($(this)); }); } loaded('.foo', function(el){ //some action el.css('background', 'black'); });
A dla normalnych zdarzeń wyzwalających używam teraz tego:
$(document).on('click', '.foo', function () { //some action $(this).css('background', 'pink'); });
źródło
Istnieje wtyczka adampietrasiak / jquery.initialize , która jest oparta na
MutationObserver
tym, że osiąga to w prosty sposób.$.initialize(".some-element", function() { $(this).css("color", "blue"); });
źródło
Można to zrobić,
DOM4 MutationObservers
ale będzie działać tylko w Firefox 14 + / Chrome 18+ (na razie).Istnieje jednak „ epicki hack ” (słowa autora nie moje!), Który działa we wszystkich przeglądarkach obsługujących animacje CSS3, którymi są: IE10, Firefox 5+, Chrome 3+, Opera 12, Android 2.0+, Safari 4+. Zobacz demo z bloga. Hack polega na użyciu zdarzenia animacji CSS3 o podanej nazwie, które jest obserwowane i wykonywane w JavaScript.
źródło
Jednym ze sposobów, który wydaje się niezawodny (chociaż został przetestowany tylko w przeglądarkach Firefox i Chrome), jest użycie JavaScript do nasłuchiwania zdarzenia
animationend
(lub jego camelCased i poprzedzonego prefiksu, rodzeństwaanimationEnd
) i zastosowanie krótkotrwałej (w wersji demonstracyjnej 0,01 sekundy) animacji do typ elementu, który planujesz dodać. Nie jest to oczywiścieonCreate
zdarzenie, ale przybliża (w zgodnych przeglądarkach)onInsertion
typ zdarzenia; poniżej przedstawiono dowód słuszności koncepcji:$(document).on('webkitAnimationEnd animationend MSAnimationEnd oanimationend', function(e){ var eTarget = e.target; console.log(eTarget.tagName.toLowerCase() + ' added to ' + eTarget.parentNode.tagName.toLowerCase()); $(eTarget).draggable(); // or whatever other method you'd prefer });
Z następującym kodem HTML:
<div class="wrapper"> <button class="add">add a div element</button> </div>
Oraz (w skrócie, wersje z prefiksem-usunięte, chociaż obecne w Fiddle, poniżej) CSS:
/* vendor-prefixed alternatives removed for brevity */ @keyframes added { 0% { color: #fff; } } div { color: #000; /* vendor-prefixed properties removed for brevity */ animation: added 0.01s linear; animation-iteration-count: 1; }
Demo JS Fiddle .
Oczywiście CSS można dostosować tak, aby pasował do umieszczenia odpowiednich elementów, a także selektora używanego w jQuery (powinien znajdować się jak najbliżej punktu wstawienia).
Dokumentacja nazw wydarzeń:
Bibliografia:
animationend
Wsparcie dostawców JavaScript .źródło
U mnie wiązanie do ciała nie działa. Wiązanie z dokumentem za pomocą jQuery.bind () działa.
$(document).bind('DOMNodeInserted',function(e){ var target = e.target; });
źródło
Myślę, że warto wspomnieć, że w niektórych przypadkach to zadziała:
$( document ).ajaxComplete(function() { // Do Stuff });
źródło
zamiast...
$(".class").click( function() { // do something });
Możesz pisać...
$('body').on('click', '.class', function() { // do something });
źródło
utwórz
<select>
z id, dołącz go do dokumentu ... i zadzwoń.combobox
var dynamicScript='<select id="selectid"><option value="1">...</option>.....</select>' $('body').append(dynamicScript); //append this to the place your wanted. $('#selectid').combobox(); //get the id and add .combobox();
to powinno załatwić
.combobox
sprawę ... możesz ukryć zaznaczenie, jeśli chcesz, a po wyświetleniu go ... albo użyj funkcji znajdź ...$(document).find('select').combobox() //though this is not good performancewise
źródło
jeśli używasz angularjs, możesz napisać własną dyrektywę. Miałem ten sam problem z bootstrapSwitch. Muszę zadzwonić
$("[name='my-checkbox']").bootstrapSwitch();
w javascript, ale mój obiekt wejściowy html nie został wówczas utworzony. Więc piszę własną dyrektywę i tworzę element wejściowy z<input type="checkbox" checkbox-switch>
W dyrektywie kompiluję element, aby uzyskać dostęp za pośrednictwem javascript i wykonać polecenie jquery (jak twoje
.combobox()
polecenie). Bardzo ważne jest, aby usunąć atrybut. W przeciwnym razie ta dyrektywa wywoła samą siebie i utworzysz pętlęapp.directive("checkboxSwitch", function($compile) { return { link: function($scope, element) { var input = element[0]; input.removeAttribute("checkbox-switch"); var inputCompiled = $compile(input)($scope.$parent); inputCompiled.bootstrapSwitch(); } } });
źródło