jQuery 动态创建元素的jQuery“on create”事件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15268661/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
jQuery "on create" event for dynamically-created elements
提问by Caballero
I need to be able to dynamically create <select>
element and turn it into jQuery .combobox()
. This should be element creation event, as opposed to some "click" event in which case I could just use jQuery .on()
.
我需要能够动态创建<select>
元素并将其转换为 jQuery .combobox()
。这应该是元素创建事件,而不是一些“点击”事件,在这种情况下我可以只使用 jQuery .on()
。
So does something like this exist?
那么这样的东西存在吗?
$(document).on("create", "select", function() {
$(this).combobox();
}
I'm reluctant to use livequery, because it's very outdated.
我不愿意使用livequery,因为它已经过时了。
UPDATEThe mentioned select/combobox is loaded via ajax into a jQuery colorbox (modal window), thus the problem - I can only initiate combobox using colorbox onComplete
, however on change of one combobox another select/combobox must be dynamically created, therefor I need a more generic way to detect creation of an element (select
in this case).
更新提到的选择/组合框通过 ajax 加载到 jQuery 颜色框(模态窗口)中,因此问题 - 我只能使用 colorbox 启动组合框onComplete
,但是在更改一个组合框时必须动态创建另一个选择/组合框,因此我需要一个更通用的方法来检测元素的创建(select
在这种情况下)。
UPDATE2To try and explain the problem further - I have select/combobox
elements created recursively, there is also a lot of initiating code inside .combobox()
, therefore if I used a classic approach, like in @bipen's answer, my code would inflate to insane levels. Hope this explains the problem better.
UPDATE2尝试进一步解释问题 - 我有select/combobox
递归创建的元素,内部也有很多启动代码.combobox()
,因此如果我使用经典方法,如@bipen's answer,我的代码会膨胀到疯狂的水平。希望这能更好地解释问题。
UPDATE3Thanks everyone, I now understand that since deprecation of DOMNodeInserted
there is a void left in DOM mutation and there is no solution to this problem. I'll just have to rethink my application.
UPDATE3谢谢大家,我现在明白,由于弃用DOMNodeInserted
DOM 突变中存在空白,并且没有解决此问题的方法。我只需要重新考虑我的申请。
回答by Codesleuth
You can on
the DOMNodeInserted
event to get an event for when it's added to the document by your code.
您可以通过on
该DOMNodeInserted
事件获取代码将其添加到文档时的事件。
$('body').on('DOMNodeInserted', 'select', function () {
//$(this).combobox();
});
$('<select>').appendTo('body');
$('<select>').appendTo('body');
Fiddled here: http://jsfiddle.net/Codesleuth/qLAB2/3/
在这里摆弄:http: //jsfiddle.net/Codesleuth/qLAB2/3/
EDIT: after reading around I just need to double check DOMNodeInserted
won't cause problems across browsers. This questionfrom 2010 suggests IE doesn't support the event, so test it if you can.
编辑:阅读后我只需要仔细检查DOMNodeInserted
不会在浏览器中引起问题。这个2010 年的问题表明 IE 不支持该事件,因此如果可以,请对其进行测试。
See here: [link]Warning! the DOMNodeInserted event type is defined in this specification for reference and completeness, but this specification deprecatesthe use of this event type.
请参阅此处:[链接]警告!本规范中定义了 DOMNodeInserted 事件类型以供参考和完整性,但本规范不赞成使用此事件类型。
回答by Yukulélé
You can use DOMNodeInserted
mutation event (no need delegation):
您可以使用DOMNodeInserted
突变事件(无需委托):
$('body').on('DOMNodeInserted', function(e) {
var target = e.target; //inserted element;
});
$('body').on('DOMNodeInserted', function(e) {
var target = e.target; //inserted element;
});
EDIT: Mutation eventsare deprecated, use mutation observerinstead
回答by Andrew Myers
As mentioned in several other answers, mutation eventshave been deprecated, so you should use MutationObserverinstead. Since nobody has given any details on that yet, here it goes...
正如其他几个答案中提到的,突变事件已被弃用,因此您应该改用MutationObserver。由于还没有人提供任何详细信息,所以在这里......
Basic JavaScript API
基本 JavaScript API
The API for MutationObserver is fairly simple. It's not quite as simple as the mutation events, but it's still okay.
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>
Let's break that down.
让我们分解一下。
var observer = new MutationObserver(callback);
This creates the observer. The observer isn't watching anything yet; this is just where the event listener gets attached.
这将创建观察者。观察者还没有观察任何东西;这正是事件侦听器被附加的地方。
observer.observe(targetNode, { childList: true, subtree: true });
This makes the observer start up. The first argument is the node that the observer will watch for changes on. The second argument is the options for what to watch for.
这使得观察者启动。第一个参数是观察者将观察变化的节点。第二个参数是关注什么的选项。
childList
means I want to watch for child elements being added or removed.subtree
is a modifier that extendschildList
to watch for changes anywhere in this element's subtree (otherwise, it would just look at changes directly withintargetNode
).
childList
意味着我想观察被添加或删除的子元素。subtree
是一个修饰符,它扩展childList
到监视此元素子树中任何地方的更改(否则,它只会直接查看 中的更改targetNode
)。
The other two main options besides childList
are attributes
and characterData
, which mean about what they sound like. You must use one of those three.
其他两个主要选择,除了childList
有attributes
和characterData
,这意味着对他们的声音是什么样子。您必须使用这三个中的一个。
function callback(records) {
records.forEach(function (record) {
Things get a little tricky inside the callback. The callback receives an array of MutationRecords. Each MutationRecord can describe several changes of one type (childList
, attributes
, or characterData
). Since I only told the observer to watch for childList
, I won't bother checking the type.
在回调中事情变得有点棘手。回调接收一个MutationRecord数组。每个MutationRecord可以描述一种类型的几个变化(childList
,attributes
,或characterData
)。由于我只告诉观察者注意childList
,我不会费心检查类型。
var list = record.addedNodes;
Right here I grab a NodeList of all the child nodes that were added. This will be empty for all the records where nodes aren't added (and there may be many such records).
在这里,我获取了所有添加的子节点的 NodeList。对于所有未添加节点的记录(并且可能有很多这样的记录),这将是空的。
From there on, I loop through the added nodes and find any that are <select>
elements.
从那时起,我遍历添加的节点并找到任何<select>
元素。
Nothing really complex here.
这里没有什么真正复杂的。
jQuery
jQuery
...but you asked for jQuery. Fine.
...但您要求使用 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);
This creates a new event called domNodeInserted
, using the jQuery special events API. You can use it like so:
这将domNodeInserted
使用jQuery 特殊事件 API创建一个名为 的新事件。你可以像这样使用它:
$(document).on("domNodeInserted", "select", function () {
$(this).combobox();
});
I would personally suggest looking for a class because some libraries will create select
elements for testing purposes.
我个人建议寻找一个类,因为有些库会创建select
用于测试目的的元素。
Naturally, you can also use .off("domNodeInserted", ...)
or fine-tune the watching by passing in data like this:
当然,您也可以.off("domNodeInserted", ...)
通过传入这样的数据来使用或微调观看:
$(document.body).on("domNodeInserted", "select.test", {
attributes: true,
subtree: false
}, function () {
$(this).combobox();
});
This would trigger checking for the appearance of a select.test
element whenever attributes changed for elements directly inside the body.
select.test
每当直接在正文中更改元素的属性时,这将触发对元素外观的检查。
You can see it live below or on jsFiddle.
你可以在下面或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
笔记
This jQuery code is a fairly basic implementation. It does not trigger in cases where modifications elsewhere make your selector valid.
这个 jQuery 代码是一个相当基本的实现。在其他地方的修改使您的选择器有效的情况下,它不会触发。
For example, suppose your selector is .test select
and the document already has a <select>
. Adding the class test
to <body>
will make the selector valid, but because I only check record.target
and record.addedNodes
, the event would not fire. The change has to happen to the element you wish to select itself.
例如,假设您的选择器是.test select
并且文档已经有一个<select>
. 添加类test
to<body>
将使选择器有效,但因为我只检查record.target
and record.addedNodes
,事件不会触发。更改必须发生在您希望选择的元素上。
This could be avoided by querying for the selector whenever mutations happen. I chose not to do that to avoid causing duplicate events for elements that had already been handled. Properly dealing with adjacentor general sibling combinatorswould make things even trickier.
这可以通过在发生突变时查询选择器来避免。我选择不这样做是为了避免对已经处理过的元素造成重复事件。正确处理相邻的或一般的兄弟组合会使事情变得更加棘手。
For a more comprehensive solution, see https://github.com/pie6k/jquery.initialize, as mentioned in Damien ó Ceallaigh's answer. However, the author of that library has announced that the library is old and suggests that you shouldn't use jQuery for this.
如需更全面的解决方案,请参阅https://github.com/pie6k/jquery.initialize,如Damien ó Ceallaigh的回答中所述。但是,该库的作者宣布该库已经过时并建议您不要为此使用 jQuery。
回答by Dieter Gribnitz
Just came up with this solution that seems to solve all my ajax problems.
刚刚想出了这个似乎解决了我所有 ajax 问题的解决方案。
For on ready events I now use this:
对于准备好的事件,我现在使用这个:
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');
});
And for normal trigger events I now use this:
对于正常的触发事件,我现在使用这个:
$(document).on('click', '.foo', function () {
//some action
$(this).css('background', 'pink');
});
回答by andyb
This could be done with DOM4 MutationObservers
but will only work in Firefox 14+/Chrome 18+ (for now).
这可以完成,DOM4 MutationObservers
但仅适用于 Firefox 14+/Chrome 18+(目前)。
However there is an "epic hack" (author's words not mine!) that works in all browsers that support CSS3 animations which are: IE10, Firefox 5+, Chrome 3+, Opera 12, Android 2.0+, Safari 4+. See the demofrom the blog. The hack is to use a CSS3 animation event with a given name that is observed and acted upon in JavaScript.
然而,有一个“史诗般的黑客”(作者的话不是我的话!)适用于所有支持 CSS3 动画的浏览器:IE10、Firefox 5+、Chrome 3+、Opera 12、Android 2.0+、Safari 4+。请参阅博客中的演示。hack 是使用具有给定名称的 CSS3 动画事件,该事件在 JavaScript 中被观察和执行。
回答by David says reinstate Monica
One way, which seemsreliable (though tested only in Firefox and Chrome) is to use JavaScript to listen for the animationend
(or its camelCased, and prefixed, sibling animationEnd
) event, and apply a short-lived (in the demo 0.01 second) animation to the element-type you plan to add. This, of course, is not an onCreate
event, but approximates (in compliantbrowsers) an onInsertion
type of event; the following is a proof-of-concept:
一种看起来可靠的方法(尽管仅在 Firefox 和 Chrome 中测试过)是使用 JavaScript 来侦听animationend
(或它的驼峰命名和前缀,同级animationEnd
)事件,并将短暂的(在演示中为 0.01 秒)动画应用到您计划添加的元素类型。当然,这不是onCreate
事件,而是近似于(在兼容浏览器中)一种onInsertion
类型的事件;以下是概念验证:
$(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
});
With the following HTML:
使用以下 HTML:
<div class="wrapper">
<button class="add">add a div element</button>
</div>
And (abbreviated, prefixed-versions-removed though present in the Fiddle, below) CSS:
和(缩写,prefixed-versions-removed 虽然出现在下面的小提琴中)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;
}
Obviously the CSS can be adjusted to suit the placement of the relevant elements, as well as the selector used in the jQuery (it should really be as close to the point of insertion as possible).
显然可以调整 CSS 以适应相关元素的放置,以及 jQuery 中使用的选择器(它应该尽可能靠近插入点)。
Documentation of the event-names:
事件名称的文档:
Mozilla | animationend
Microsoft | MSAnimationEnd
Opera | oanimationend
Webkit | webkitAnimationEnd
W3C | animationend
References:
参考:
回答by Milan Simek
For me binding to the body does not work. Binding to the document using jQuery.bind() does.
对我来说,绑定到身体是行不通的。使用 jQuery.bind() 绑定到文档。
$(document).bind('DOMNodeInserted',function(e){
var target = e.target;
});
回答by Damien ó Ceallaigh
There is a plugin, adampietrasiak/jquery.initialize, which is based on MutationObserver
that achieves this simply.
有一个插件,adampietrasiak/jquery.initialize,它基于MutationObserver
它简单地实现了这一点。
$.initialize(".some-element", function() {
$(this).css("color", "blue");
});
回答by user315338
I Think it's worth mentioning that in some cases, this would work:
我认为值得一提的是,在某些情况下,这会起作用:
$( document ).ajaxComplete(function() {
// Do Stuff
});
回答by bipen
create a <select>
with id , append it to document.. and call .combobox
创建一个<select>
with id ,将其附加到 document.. 并调用.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();
this should do the trick.. you can hide the select if you want and after .combobox
show it..or else use find..
这应该可以解决问题..如果需要,您可以隐藏选择并在.combobox
显示后隐藏它..或者使用查找..
$(document).find('select').combobox() //though this is not good performancewise