TL; DR:我如何获得像find()这样的动作,但阻止特定选择器的遍历(不是完全停止,只是跳过)?
答案:$(Any).find(Selector).not( $(Any).find(Mask).find(Selector) )
有许多真正伟大的答案,我希望我可以更多地分配赏金点数,也许我应该为这些中的一些做出50 pt奖励; p我选择Karl-André Gagnon's因为这个答案设法使findExclude不需要在一个稍长的行中。虽然这使用了三个查找调用和一个重度非过滤器,但在大多数情况下,jQuery可以使用非常快速的实现来跳过大多数find()的遍历。
下面列出了特别好的答案:
falsarella:my solution,findExclude(),在许多情境中表现最佳
Zbyszek:基于过滤器的解决方案类似于falsarella' s,效率也很好
Justin:针对底层问题的完全不同但可管理且功能性的解决方案
每一项都有其独特的优点,值得一提。
我需要完全下降到一个元素并比较选择器,将所有匹配的选择器作为数组返回,但在遇到另一个选择器时跳过下降到树中。
修改:将原始代码示例替换为我网站上的一些
这是一个消息论坛,可能有回复消息组嵌套在任何消息中。
但请注意,我们不能使用消息或内容类,因为该脚本也用于论坛之外的其他组件。只有InterfaceGroup
,Interface
和controls
类可能有用 - 最好只是界面和控件。
Interact with the code and see it in JS Fiddle, thanks Dave A, here在查看JavaScript控制台时单击按钮,以查看每个.Interface嵌套级别的控件类被绑定一个额外的时间。
Visual A,Forum Layout Struture:
<li class="InterfaceGroup">
<ul class="Interface Message" data-role="MessagePost" >
<li class="instance"> ... condensed ... </li>
<li class="InterfaceGroup"> ... condensed ...</li>
</ul>
<ul class="Interface Message" data-role="MessagePost" >
<li class="instance"> ... condensed ... </li>
</ul>
<ul class="Interface Message" data-role="MessagePost" >
<li class="instance"> ... condensed ... </li>
<li class="InterfaceGroup"> ... condensed ...</li>
</ul>
</li>
在每个<li class="InterfaceGroup">
内部,可以有相同结构的任意数量的重复(每个组是消息的线程)和/或更深的嵌套,例如..
<li class="InterfaceGroup">
<ul class="Interface Message" data-role="MessagePost" >
<li class="instance"> ... condensed ... </li>
<li class="InterfaceGroup">
<ul class="Interface Message" data-role="MessagePost" >
<li class="instance"> ... condensed ... </li>
<li class="InterfaceGroup"> ... condensed ...</li>
</ul>
</li>
</ul>
</li>
在每个<li class="instance"> ... </li>
内部,有一个由class="controls"
可能出现的另一个团队决定的任意位置,并且应绑定一个事件监听器。虽然这些包含消息,但其他组件会随意构建其标记,但在.controls
内始终会.Interface
,这些.InterfaceGroup
被收集到<ul class="Interface Message" data-role="MessagePost" >
<li class="instance">
<ul class="profile"> ...condensed, nothing clickable...</ul>
<ul class="contents">
<li class="heading"><h3>Hi there!</h3></li>
<li class="body"><article>TEST Message here</article></li>
<li class="vote controls">
<button class="up" data-role="VoteUp" ><i class="fa fa-caret-up"> </i><br/>1</button>
<button class="down" data-role="VoteDown" >0<br/><i class="fa fa-caret-down"> </i></button>
</li>
<li class="social controls">
<button class="reply-btn" data-role="ReplyButton" >Reply</button>
</li>
</ul>
</li>
<li class="InterfaceGroup" > <!-- NESTING OCCURRED -->
<ul class="Interface Message" data-role="MessagePost" >
<li class="instance">... condensed ... </li>
<li class="InterfaceGroup" >... condensed ... </li>
</ul>
</li>
</ul>
。内容的复杂度降低版本(对于论坛帖子)在下面供参考。
Visual B,带控件类的消息内容:
instance
我们只能绑定到接口类中的控件,.controls
可能存在,也可能不存在,但接口会存在。 事件会向.Interface
元素冒泡,并引用保留它们的$('.Interface').each( bind to any .controls not inside a deeper .Interface )
。。
所以我正在尝试.Interface .controls
这是棘手的部分,因为
.control
会在.each()var Interface=function()
{
$elf=this;
$elf.call=
{
init:function(Markup)
{
$elf.Interface = Markup;
$elf.Controls = $(Markup).find('.controls').not('.Interface .controls');
$elf.Controls.on('click mouseenter mouseleave', function(event){ $elf.call.events(event); });
return $elf;
},
events:function(e)
{
var classlist = e.target.className.split(/\s+/), c=0, L=0;
var role = $(e.target).data('role');
if(e.type == 'click')
{
CurrentControl=$(e.target).closest('[data-role]')[0];
role = $(CurrentControl).data('role');
switch(role)
{
case 'ReplyButton':console.log('Reply clicked'); break;
case 'VoteUp':console.log('Up vote clicked'); break;
case 'VoteDown':console.log('Down vote clicked'); break;
default: break;
}
}
}
}
};
$(document).ready( function()
{
$('.Interface').each(function(instance, Markup)
{
Markup.Interface=new Interface().call.init(Markup);
});
} );
次
我怎样才能使用jQuery.find()或类似的jQuery方法呢?
我一直在考虑,或许,使用不是选择器的孩子可以工作,而可以做与在引擎盖下找到相同的事情,但我&#39 ;我不确定它实际上是否会导致可怕的表现。答案仍然可以接受。儿童有效是可以接受的。
更新:最初我尝试使用psuedo-example来简洁,但希望看到一个论坛结构将有助于澄清问题,因为它们是自然嵌套的结构。下面我还发布了部分javascript供参考,init函数的第二行是最重要的。
缩减JavaScript部分:
{{1}}
答案 0 :(得分:6)
如果要在find中排除元素,可以使用not filter。例如,我已经采取了排除元素的功能并缩短了时间:
$.fn.findExclude = function( Selector, Mask,){
return this.find(Selector).not(this.find(Mask).find(Selector))
}
现在,对你说实话,我并不完全明白你想要什么。但是,当我看看你的功能时,我看到了你想要做的事情。
无论如何,看看这个小提琴,结果与你的相同:http://jsfiddle.net/KX65p/8/
答案 1 :(得分:4)
好吧,我真的不想在赏金上回答我自己的问题,所以如果有人能提供更好的或替代的实施,请做..
然而,为了完成这个项目,我最终在这方面做了很多工作,并提出了一个相当干净的jQuery插件,用于执行jQuery.find()样式搜索,同时从结果中排除子分支
用于处理嵌套视图中的元素集:
// Will not look in nested ul's for inputs
$('ul').findExclude('input','ul');
// Will look in nested ul's for inputs unless it runs into class="potato"
$('ul').findExclude('input','.potato');
在http://jsfiddle.net/KX65p/3/找到更复杂的例子,我将它用于.each()一个嵌套类,并将每个嵌套视图中出现的元素绑定到一个类。这让我让组件服务器端和客户端反映彼此的属性,并有更便宜的嵌套事件处理。
<强>实施强>
// Find-like method which masks any descendant
// branches matching the Mask argument.
$.fn.findExclude = function( Selector, Mask, result){
// Default result to an empty jQuery object if not provided
result = typeof result !== 'undefined' ?
result :
new jQuery();
// Iterate through all children, except those match Mask
this.children().each(function(){
thisObject = jQuery( this );
if( thisObject.is( Selector ) )
result.push( this );
// Recursively seek children without Mask
if( !thisObject.is( Mask ) )
thisObject.findExclude( Selector, Mask, result );
});
return result;
}
(简明版):
$.fn.findExclude = function( selector, mask, result )
{
result = typeof result !== 'undefined' ? result : new jQuery();
this.children().each( function(){
thisObject = jQuery( this );
if( thisObject.is( selector ) )
result.push( this );
if( !thisObject.is( mask ) )
thisObject.findExclude( selector, mask, result );
});
return result;
}
答案 2 :(得分:2)
也许这样的事情会起作用:
$.fn.findExclude = function (Selector, Mask) {
var result = new jQuery();
$(this).each(function () {
var $selected = $(this);
$selected.find(Selector).filter(function (index) {
var $closest = $(this).closest(Mask);
return $closest.length == 0 || $closest[0] == $selected[0] || $.contains($closest, $selected);
}).each(function () {
result.push(this);
});
});
return result;
}
选择那些不在掩码父级中的元素或者它们最接近的掩码父级与root相同或者它们最接近的掩码父级是root的父级。
答案 3 :(得分:2)
根据我的理解,我会绑定到.controls
元素并允许事件冒泡到它们。从那里,如果需要,您可以获得最接近的.Interface
以获得父级。通过这种方式,您可以在兔子洞的下方添加多个处理程序。
虽然我看到你提到它,但我从未见过它。
//Attach the event to the controls to minimize amount of binded events
$('.controls').on('click mouseenter mouseleave', function (event) {
var target = $(event.target),
targetInterface = target.closest('.Interface'),
role = target.data('role');
if (event.type == 'click') {
if (role) {
switch (role) {
case 'ReplyButton':
console.log('Reply clicked');
break;
case 'VoteUp':
console.log('Up vote clicked');
break;
case 'VoteDown':
console.log('Down vote clicked');
break;
default:
break;
}
}
}
});
这是显示我的意思的fiddle。我确实删除了你的js,转而采用简化的显示方式。
看起来我的解决方案似乎过于简化了......
所以here是一个小提琴,它定义了一些有助于实现你所寻找目标的常用功能......我想。 getInterfaces
提供了一个简化的函数来查找接口及其控件,假设所有接口始终都有控件。
虽然可能会出现边缘情况。我也觉得如果你已经冒险走上这条道路并且我只是没有看到/理解,我需要道歉!
好的,好的。我想我明白你想要什么。您希望获得唯一接口,并拥有一组属于它的控件,这些控件现在才有意义。
以this fiddle为例,我们同时选择.Interface
和.Interface .controls
。
var interfacesAndControls = $('.Interface, .Interface .controls');
通过这种方式,我们可以得到一个简洁的接口集合和属于它们的控件,以便它们出现在DOM中。有了这个,我们可以遍历集合并检查当前元素是否与.Interface
相关联。我们还可以保留对我们为其创建的当前接口对象的引用,以便稍后添加控件。
if (el.hasClass('Interface')){
currentInterface = new app.Interface(el, [], eventCallback);
interfaces.push(currentInterface);
//We don't need to do anything further with the interface
return;
};
现在,当我们没有与该元素关联的.Interface
类时,我们得到了控件。因此,让我们首先修改我们的Interface
对象,以支持在控件添加到集合时向控件添加控件和绑定事件。
//The init function was removed and the call to it
self.addControls = function(el){
//Use the mouseover and mouseout events so event bubbling occurs
el.on('click mouseover mouseout', self.eventCallback)
self.controls.push(el);
}
现在我们要做的就是将控件添加到当前的接口控件中。
currentInterface.addControls(el);
毕竟,你应该得到一个包含3个对象(接口)的数组,每个对象有一个包含2个控件的数组。
希望,有你想要的一切!
答案 4 :(得分:2)
我认为这是最接近findExclude
可以优化的:
$.fn.findExclude = function (Selector, Mask) {
var result = $([]);
$(this).each(function (Idx, Elem) {
$(Elem).find(Selector).each(function (Idx2, Elem2) {
if ($(Elem2).closest(Mask)[0] == Elem) {
result = result.add(Elem2);
}
});
});
return result;
}
另外,请查看其fiddle添加的日志,其中包含已用完的时间(以毫秒为单位)。
我看到你对表演很担心。所以,我已经运行了一些测试,这个实现不会超过2毫秒,而你的实现(作为你发布的答案)有时需要大约4~7毫秒。
答案 5 :(得分:0)
为什么不把问题颠倒过来?
选择所有$(。target)元素,然后如果他们的。$ parents(.group)为空,则将其从进一步处理中丢弃,这样可以使声音像:
$('.target').each(function(){
if (! $(this).parents('.group').length){
//the jqueryElem is empy, do or do not
} else {
//not empty do what you wanted to do
}
});
请注意,不要回答标题,但实际上会在选择器A&#34;的结果中给出&#34;选择器B.
答案 6 :(得分:0)
如果你的.interface类有某种标识符,这似乎相当容易。 由于其他原因,您已经拥有这样的标识符,或者选择包含一个标识符。
<div class="interface" name="a">
<div class="control">control</div>
<div class="branch">
<div class="control">control</div>
<div class="interface">
<div class="branch">
<div class="control">control</div>
</div>
</div>
</div>
<div class="interface" name="c">
<div class="branch">
<div class="control">control</div>
</div>
</div> </div>
$( ".interface[name=c] .control:not(.interface[name=c] .interface .control)" ).css( "background-color", "red" );
$( ".interface[name=a] .control:not(.interface[name=a] .interface .control)" ).css( "background-color", "green" );
编辑:现在我想知道你是否从错误的角度解决了这个问题。
所以我想尝试$(&#39; .Interface&#39;)。每个(绑定到任何.controls不 在更深层次。接口)
$(".interface").on("click", ".control", function (event) {
alert($(this).text());
event.stopPropagation();
});
该事件将在.control上触发;然后它将冒泡到它的.closest(&#34; .interface&#34;),在那里它将被处理并进一步传播停止。那不是你所描述的吗?