Như đã đề cập trong một số câu trả lời khác, các sự kiện đột biến không được dùng nữa, vì vậy bạn nên sử dụng MutationObserver để thay thế. Vì chưa ai đưa ra bất kỳ chi tiết nào về điều đó, nên đây là ...
API JavaScript cơ bản
API cho MutationObserver khá đơn giản. Nó không hoàn toàn đơn giản như các sự kiện đột biến, nhưng nó vẫn ổn.
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') {
console.log(list[i]);
}
}
});
}
var observer = new MutationObserver(callback);
var targetNode = document.body;
observer.observe(targetNode, { childList: true, subtree: true });
<script>
setTimeout(function() {
var $el = document.createElement('select');
document.body.appendChild($el);
}, 500);
</script>
Hãy phá vỡ điều đó.
var observer = new MutationObserver(callback);
Điều này tạo ra người quan sát. Người quan sát vẫn chưa xem gì cả; đây chỉ là nơi người nghe sự kiện được đính kèm.
observer.observe(targetNode, { childList: true, subtree: true });
Điều này khiến người quan sát phải giật mình. Đối số đầu tiên là nút mà người quan sát sẽ theo dõi các thay đổi trên. Đối số thứ hai là các tùy chọn cho những gì cần xem .
childList
nghĩa là tôi muốn xem các phần tử con được thêm vào hoặc bị xóa.
subtree
là một công cụ sửa đổi mở rộng childList
để theo dõi các thay đổi ở bất kỳ đâu trong cây con của phần tử này (nếu không, nó sẽ chỉ xem xét các thay đổi trực tiếp bên trong targetNode
).
Hai tùy chọn chính khác bên cạnh đó childList
là attributes
và characterData
, có nghĩa là về âm thanh của chúng. Bạn phải sử dụng một trong ba cái đó.
function callback(records) {
records.forEach(function (record) {
Mọi thứ trở nên phức tạp một chút bên trong lệnh gọi lại. Lệnh gọi lại nhận một mảng các MutationRecord . Mỗi MutationRecord có thể mô tả một số thay đổi của một loại ( childList
, attributes
hoặc characterData
). Vì tôi chỉ nói với người quan sát để theo dõi childList
, tôi sẽ không bận tâm kiểm tra loại.
var list = record.addedNodes;
Ngay tại đây, tôi lấy một NodeList của tất cả các nút con đã được thêm vào. Điều này sẽ trống cho tất cả các bản ghi mà các nút không được thêm vào (và có thể có nhiều bản ghi như vậy).
Từ đó trở đi, tôi lặp qua các nút đã thêm và tìm bất kỳ nút nào là <select>
phần tử.
Không có gì thực sự phức tạp ở đây.
jQuery
... nhưng bạn đã yêu cầu jQuery. Khỏe.
(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);
Điều này tạo ra một sự kiện mới được gọi domNodeInserted
, sử dụng API sự kiện đặc biệt của jQuery . Bạn có thể sử dụng nó như vậy:
$(document).on("domNodeInserted", "select", function () {
$(this).combobox();
});
Cá nhân tôi khuyên bạn nên tìm kiếm một lớp vì một số thư viện sẽ tạo ra select
các phần tử cho mục đích thử nghiệm.
Đương nhiên, bạn cũng có thể sử dụng .off("domNodeInserted", ...)
hoặc tinh chỉnh chế độ xem bằng cách chuyển dữ liệu như sau:
$(document.body).on("domNodeInserted", "select.test", {
attributes: true,
subtree: false
}, function () {
$(this).combobox();
});
Điều này sẽ kích hoạt kiểm tra sự xuất hiện của một select.test
phần tử bất cứ khi nào các thuộc tính thay đổi cho các phần tử trực tiếp bên trong phần thân.
Bạn có thể xem trực tiếp bên dưới hoặc trên jsFiddle .
(function($) {
$(document).on("domNodeInserted", "select", function() {
console.log(this);
});
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
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>
Ghi chú
Mã jQuery này là một triển khai khá cơ bản. Nó không kích hoạt trong trường hợp các sửa đổi ở nơi khác làm cho bộ chọn của bạn hợp lệ.
Ví dụ: giả sử bộ chọn của bạn là .test select
và tài liệu đã có <select>
. Thêm lớp test
vào <body>
sẽ làm cho bộ chọn hợp lệ, nhưng vì tôi chỉ kiểm tra record.target
và record.addedNodes
, sự kiện sẽ không kích hoạt. Thay đổi phải xảy ra với phần tử bạn muốn chọn chính nó.
Điều này có thể tránh được bằng cách truy vấn bộ chọn bất cứ khi nào đột biến xảy ra. Tôi đã chọn không làm điều đó để tránh gây ra các sự kiện trùng lặp cho các phần tử đã được xử lý. Xử lý thích hợp với các tổ hợp anh chị em kế cận hoặc chung sẽ làm cho mọi thứ trở nên phức tạp hơn.
Để có giải pháp toàn diện hơn, hãy xem https://github.com/pie6k/jquery.initialize , như đã đề cập trong câu trả lời của Damien Ó Ceallaigh . Tuy nhiên, tác giả của thư viện đó đã thông báo rằng thư viện đã cũ và gợi ý rằng bạn không nên sử dụng jQuery cho việc này.
$(select).ready(function() { });