有很多关于将未来操作绑定到不存在的元素的问题,最终都会以 live / delegate 回答。我想知道如何运行任意回调(例如,添加类或触发插件)到匹配选择器的所有现有元素以及与该选择器匹配的所有未来元素< / strong>尚未创建。
似乎livequery插件的主要功能使其成为核心,但另一部分,附加任意回调在某种程度上丢失了。
另一个常见的答案是事件委派,但是如果一个人无法访问创建元素的所有供应商代码以使其trigger事件呢?
以下是一些现实世界的代码:
// with livequery
$('input[type=text], input[type=password], textarea, .basic_form .block select, .order_form .form_item select, .order_form .form_item input')
.livequery(function(){
$(this)
.focus(function(){
$(this).addClass('active');
})
.blur(function(){
$(this).removeClass('active');
})
.addClass('text');
});
// with live
$('input[type=text], input[type=password], textarea, .basic_form .block select, .order_form .form_item select, .order_form .form_item input')
.live('focus', function(){
$(this).addClass('active');
})
.live('blur', function(){
$(this).removeClass('active');
});
// now how to add the class to future elements?
// (or apply another plugin or whatever arbitrary non-event thing)
一种方法是监视何时添加/删除新节点并重新触发我们的选择器。感谢 @arnorhs ,我们知道 DOMNodeInserted 事件,我会忽略跨浏览器的问题,希望那些小的IE补丁有一天能够上游到jQuery或知道可以包装jQuery DOM函数。
但是,即使我们可以确保DOMNodeInserted触发了跨浏览器,但使用多个选择器绑定它也是荒谬的。可以随时创建数百个元素,并且可能会对每个元素进行数十个选择器调用。
监视DOMNodeInserted / Deleted和/或挂钩到jQuery的DOM操作例程中只是设置一个“重新初始化”应该发生的标志可能会更好吗?然后可能只有一个定时器,每x秒检查一次该标志,只在DOM实际发生变化时运行所有那些选择器/回调。
如果您以快速的速度添加/删除大量元素(如动画或____),那可能仍然非常糟糕。如果x很低,每x秒重新解析一次DOM,每x秒可能会过于激烈,如果x很高,界面会显得迟钝。
我会在它允许的时候添加赏金。我为最新颖的解决方案添加了赏金!
基本上我得到的是一种更强大的,面向方面的方法来操纵DOM。可以允许将来创建新元素,并且应该创建,并将初始document.ready修改应用于它们。
JS最近能够做到这么多魔术,我希望它会很明显。
答案 0 :(得分:10)
在我看来,DOM Level 3事件DOMNodeInserted
help(仅针对节点触发)和DOMSubtreeModified
help(几乎任何修改都会触发,如属性更改)是完成该任务的最佳选择
当然,这些事件的最大缺点是,这个世界的互联网探索者不支持他们 (......好吧, IE9 确实如此。)
此问题的另一个合理解决方案是挂钩任何可以修改DOM的方法。但接下来我们要问,这里的范围是什么?
是否足以处理来自特定库(如jQuery)的DOM修改方法?如果由于某种原因另一个库正在修改DOM或甚至是本机方法会怎样?
如果它仅适用于jQuery,我们根本不需要.sub()
。我们可以用以下形式编写钩子:
<强> HTML 强>
<div id="test">Test DIV</div>
<强> JS 强>
(function(_append, _appendTo, _after, _insertAfter, _before, _insertBefore) {
$.fn.append = function() {
this.trigger({
type: 'DOMChanged',
newnode: arguments[0]
});
return _append.apply(this, arguments);
};
$.fn.appendTo = function() {
this.trigger({
type: 'DOMChanged',
newnode: this
});
return _appendTo.apply(this, arguments);
};
$.fn.after = function() {
this.trigger({
type: 'DOMChanged',
newnode: arguments[0]
});
return _after.apply(this, arguments);
};
// and so forth
}($.fn.append, $.fn.appendTo, $.fn.after, $.fn.insertAfter, $.fn.before, $.fn.insertBefore));
$('#test').bind('DOMChanged', function(e) {
console.log('New node: ', e.newnode);
});
$('#test').after('<span>new span</span>');
$('#test').append('<p>new paragraph</p>');
$('<div>new div</div>').appendTo($('#test'));
可以在此处找到上述代码的实例:http://www.jsfiddle.net/RRfTZ/1/
这当然需要完整的DOMmanip方法列表。我不确定您是否可以使用此方法覆盖.appendChild()
等本机方法。例如.appendChild
位于Element.prototype.appendChild
,可能值得一试。
<强>更新强>
我在Chrome,Safari和Firefox(官方最新版本)中测试了覆盖Element.prototype.appendChild
等。适用于Chrome和Safari,但不适用于Firefox!
可能还有其他方法可以解决这一问题。但我想不出一个真正令人满意的方法,比如计算/观察节点的所有后代(需要interval
或timeouts
,eeek)。
<强>结论强>
DOM Level 3事件的混合,支持和挂钩的DOMmanip方法可能是你在这里做的最好的。
答案 1 :(得分:6)
我正在阅读新版本的jQuery版本1.5,我立即想到了这个问题。
使用jQuery 1.5,您实际上可以使用名为jQuery.sub();
的东西创建自己的jQuery版本这样你就可以在jQuery中覆盖默认的.append(),insert(),. html(),..函数,并创建一个名为“mydomchange”的自定义事件 - 而不会影响所有其他脚本。
所以你可以做这样的事情(从带有小mod的.sub()文档中复制。):
var sub$ = jQuery.sub();
sub$.fn.insert = function() {
// New functionality: Trigger a domchange event
this.trigger("domchange");
// Be sure to call the original jQuery remove method
return jQuery.fn.insert.apply( this, arguments );
};
您必须对所有dom操作方法执行此操作...
jQuery文档中的jQuery.sub(): http://api.jquery.com/jQuery.sub/答案 2 :(得分:2)
很棒的问题
似乎有一个可以绑定的自定义事件: http://javascript.gakaa.com/domnodeinserted-description.aspx
所以我猜你可以这样做:
$(document).bind('DOMNodeInserted',function(){ /* do stuff */ });
但我没有尝试过,所以我没有线索......
btw。:相关问题: Can javascript listen for "onDomChange" on every Dom elements?
答案 3 :(得分:0)
没有简单明显的方法可以做到这一点。唯一可靠的方法是主动轮询,这会导致在创建新元素和轮询通知它之间出现渲染打嗝。这也可能会使您的页面占用大量资源,具体取决于您轮询页面的频率。正如您所观察到的,您还可以将这些事件与几个特定于浏览器的事件绑定在一起,至少可以使这些浏览器的效果更好。
您可以覆盖jQuery的DOM修改函数来触发自定义更改事件(并使用$ .live来捕获这些事件以进行操作),但是当我在过去尝试过这个时,它会巧妙地破坏各种jQuery插件(我的猜测)是其中一些插件做类似的事情)。最后,我已经放弃了这样做,因为我不想放弃性能并为主动轮询提供打嗝,而且没有其他综合方法可以做到这一点。相反,我有一个初始化事件,我确保为每个DOM更改触发,我将操作事件绑定到那些。
要小心,如果你不考虑事情,很容易陷入无限的事件循环中,这也可能是微妙的,难以追踪;更糟糕的是,对于您的单元测试不允许的角落情况可能会发生(因此您的用户体验它而不仅仅是您)。在这种意义上,自定义手动触发的初始化事件更容易诊断,因为您始终确切知道何时触发它。
答案 4 :(得分:-1)
嗯,首先,如果你担心穿孔,你甚至不应该使用像这样的选择器。
$('input[type=text], input[type=password], textarea, .basic_form .block select, .order_form .form_item select, .order_form .form_item input')
任何没有xpath(不仅仅是IE iirc)或getElementsByClassName(IE 7及以下版本)的本机实现的浏览器都可以轻松地在大型网站上花费几秒钟咀嚼它,因此轮询当然会完全消失问题,如果你想要这么广泛。