修改尚未创build的元素的首选方式(除了事件)

关于将未来的操作绑定到不存在的元素上 ,有很多问题最终都以现场 /代表来回答。 我想知道如何运行一个任意的callback(例如添加一个类或触发一个插件)所有匹配select器的现有元素,以及所有未来的元素匹配相同的select器尚未创build。

看起来,livequery插件的主要function使它成为核心,但另一方面,随意的callback在某种程度上迷失了方向。

另一个常见的答案是事件委托,但如果没有访问所有的供应商代码来创build元素来触发事件呢?


这是一些真实世界的代码:

// 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) 

一种方法是监视何时添加/删除新节点,并重新触发我们的select器。 感谢@arnorhs,我们了解了DOMNodeInserted事件,我忽略了跨浏览器的问题,希望那些小的IE补丁有朝一日能够上游到jQuery或知道jQuery DOM函数可以被包装。

即使我们可以确保DOMNodeInserted已经开启了跨浏览器,但是用多个select器来绑定它将是荒谬的。 可以随时创build数百个元素,每个元素上可能会有数十个select器调用。

我迄今为止最好的主意

监视DOMNodeInserted / Deleted和/或挂钩到jQuery的DOM操作例程中,只设置一个标志“re-init”应该发生? 然后可能会有一个计时器每隔x秒检查一次该标志,只有在DOM实际更改时才运行所有select器/callback。

如果您以很快的速度添加/删除大量元素(如使用animation或____),那仍然可能非常糟糕。 如果x很低,必须每隔x秒为每个保存的select器重新parsingDOM一次,如果x很低,则界面会显得很慢。

还有其他新颖的解决方

当它让我时,我会添加一个赏金。 我添加了最新解决scheme的赏金!

基本上我得到的是更多的面向方面来操纵DOM。 一个可以允许将来创build新元素的元素 ,并且应该使用最初的document.ready修改来创build它们

JS最近能够做出如此多的魔术,我希望这将是显而易见的。

在我看来,DOM级别3事件DOMNodeInserted 帮助 (仅针对节点触发)和DOMSubtreeModified 帮助 (实际上触发任何修改,如属性更改)是完成该任务的最佳select。

当然,这些事件的巨大缺点是,这个世界的互联网探险家不支持他们
IE9呢 )。

对于这个问题的其他合理的解决scheme是钩入任何可以修改DOM的方法。 但是,我们不得不问,我们在这里的范围是什么?

处理像jQuery这样的特定库中的DOM修改方法就足够了吗? 如果由于某种原因,另一个库正在修改DOM甚至本地方法呢?

如果只是针对jQuery,我们完全不需要.sub() 。 我们可以用下面的forms编写钩子:

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')); 

上面的代码的一个生动的例子可以在这里find: http : //www.jsfiddle.net/RRfTZ/1/

这当然需要DOMmanip方法的完整列表。 我不确定是否可以用这种方法覆盖像.appendChild()这样的本机方法。 .appendChild位于Element.prototype.appendChild中,可能值得一试。

更新

我在Chrome,Safari和Firefox(官方最新版本)中testing覆盖Element.prototype.appendChild等。 适用于Chrome和Safari,但不适用于Firefox!


可能还有其他方法来解决这个要求。 但是我想不出一个真正令人满意的方法,比如计算/观察一个节点的所有后代(这需要intervaltimeouts ,eeek)。

结论

DOM级别3事件的混合,其中支持和挂钩DOMmanip方法可能是最好的,你可以在这里做。

我正在阅读1.5版本的jQuery的新版本,我立即想到了这个问题。

使用jQuery 1.5,你可以使用jQuery.sub()来创build你自己的jQuery版本。

这样,你可以实际上覆盖jQuery中的默认的.append(),insert(),.html(),..函数,并创build自己的自定义事件,称为“mydomchange”,而不影响所有其他脚本。

所以你可以做一些这样的事情(从.sub()文件复制小mod。):

 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/

伟大的问题

似乎有一个可以绑定的自定义事件: http : //javascript.gakaa.com/domnodeinserted-description.aspx

所以我想你可以做这样的事情:

 $(document).bind('DOMNodeInserted',function(){ /* do stuff */ }); 

但是我还没有尝试,所以我没有线索..

顺便说一句:相关的问题: JavaScript可以听每个Dom元素的“onDomChange”吗?

没有简单明显的方法来做到这一点。 唯一可靠的方法是主动轮询,这会导致在创build新元素和轮询发现之间出现打嗝。 这也可能使您的网页花费很多资源,取决于您轮询页面的频率。 正如你所观察到的,你也可以将它与几个特定于浏览器的事件绑定,至less在这些浏览器中使事情变得更好。

你可以重写jQuery的DOM修改函数来触发一个自定义的更改事件(并使用$ .live来捕获这些事件进行操作),但是当我过去尝试过这个时,它会微妙地破坏各种jQuery插件(我的猜测是这些插件做类似的事情)。 最后,我放弃了这么做,因为我不想放弃performance,让投票活跃起来,而且没有其他全面的方法去做。 相反,我有一个初始化事件,我确定要触发每个DOM更改,并将我的操作事件绑定到这些事件上。

要小心,如果你不考虑事情,很容易陷入无限的事件循环中,这也可能是微妙而难以追查的。 更糟糕的是可能会发生你的unit testing不允许的情况(所以你的用户体验它而不是你)。 自定义手动触发的初始化事件从这个意义上来说更容易诊断,因为您始终知道何时触发它。

那么,首先,如果你担心性能,你甚至不应该使用这样的select器。

 $('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及以下版本)的浏览器都可以轻松地在一个大网站上花费几秒钟的时间咀嚼,所以投票当然是完全不可能的如果你想这个广泛的。