正如其他几个答案中提到的,突变事件已被弃用,因此您应该改用MutationObserver。由于没有人提供任何详细信息,所以这里......
基本 JavaScript API
MutationObserver 的 API 相当简单。它不像突变事件那么简单,但它仍然可以。
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>
让我们分解一下。
var observer = new MutationObserver(callback);
这创建了观察者。观察者还没有看任何东西;这正是事件监听器被附加的地方。
observer.observe(targetNode, { childList: true, subtree: true });
这使观察者启动。第一个参数是观察者将观察变化的节点。第二个参数是观察什么的选项。
childList
表示我想观察添加或删除的子元素。
subtree
是一个修饰符,可扩展 childList
以监视此元素子树中任何位置的更改(否则,它只会直接查看 内部的更改targetNode
)。
除此以外的其他两个主要选项childList
是attributes
和characterData
,这意味着它们听起来像什么。您必须使用这三个之一。
function callback(records) {
records.forEach(function (record) {
回调内部的事情变得有点棘手。回调接收一个MutationRecord数组。每个 MutationRecord 可以描述一种类型( 、 或 )的childList
多个attributes
变化characterData
。由于我只告诉观察者注意childList
,我不会费心检查类型。
var list = record.addedNodes;
在这里,我抓取了所有添加的子节点的 NodeList。对于所有未添加节点的记录,这将为空(并且可能有很多这样的记录)。
从那里开始,我遍历添加的节点并找到任何<select>
元素。
这里没有什么复杂的。
jQuery
...但是您要求使用 jQuery。美好的。
(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);
domNodeInserted
这将使用jQuery 特殊事件 API创建一个名为 的新事件。你可以像这样使用它:
$(document).on("domNodeInserted", "select", function () {
$(this).combobox();
});
我个人建议寻找一个类,因为一些库会创建select
用于测试目的的元素。
当然,您也可以.off("domNodeInserted", ...)
通过传入如下数据来使用或微调观看:
$(document.body).on("domNodeInserted", "select.test", {
attributes: true,
subtree: false
}, function () {
$(this).combobox();
});
select.test
每当直接在正文内的元素的属性发生更改时,这将触发对元素外观的检查。
你可以在下面或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>
笔记
这个 jQuery 代码是一个相当基本的实现。如果其他地方的修改使您的选择器有效,它不会触发。
例如,假设您的选择器是.test select
并且文档已经有一个<select>
. 添加类将使选择器有效,但因为我只检查test
and ,所以事件不会触发。更改必须发生在您希望自己选择的元素上。<body>
record.target
record.addedNodes
这可以通过在发生突变时查询选择器来避免。我选择不这样做是为了避免对已经处理过的元素造成重复事件。正确处理相邻的或一般的兄弟组合器会使事情变得更加棘手。
如需更全面的解决方案,请参阅https://github.com/pie6k/jquery.initialize,如Damien Ó Ceallaigh的回答中所述。但是,该库的作者已宣布该库已过时,并建议您不要为此使用 jQuery。