如其他几个答案中所述,不建议使用突变事件,因此应改用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') {
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>
让我们分解一下。
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);
});
})(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>
注意
此jQuery代码是一个相当基本的实现。在其他地方进行的修改使选择器有效的情况下,它不会触发。
例如,假设您的选择器为.test select
并且文档已经有一个<select>
。添加类test
,以<body>
将选择有效的,但因为我只检查record.target
和record.addedNodes
,该事件将不会触发。您必须自行选择要更改的元素。
每当发生突变时,都可以通过查询选择器来避免这种情况。我选择不这样做是为了避免对已处理的元素造成重复事件。适当地处理相邻或通用的同级组合器会使事情变得更加棘手。
有关更全面的解决方案,请参见https://github.com/pie6k/jquery.initialize,如DamienÓCeallaigh的回答中所述。但是,该库的作者宣布该库已过时,建议您不要为此使用jQuery。
$(select).ready(function() { });