TL;DR: How do I get an action like find(), but block traversal (not full stop, just skip) for a certain selector?
ANSWERS: $(Any).fin
Maybe something like this would work:
$.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;
}
http://jsfiddle.net/JCA23/
Chooses those elements that are either not in mask parent or their closest mask parent is same as root or their closest mask parent is a parent of root.
Why not taking the problem upside down?
Select all $(.target) elements and then discard them from further treatment if their .$parents(.group) is empty, that would give sonething like:
$('.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
}
});
Note that don't answer the title but literally gives you "Selector B, inside of a result from Selector A"
understanding your needs better and applying the specific classes you need, I think this is the syntax will work:
var targetsOfTopGroups = $('.InterfaceGroup .Interface:not(.Interface .Interface):not(.Interface .InterfaceGroup)')
This Fiddle is an attempt to reproduce your scenario. Feel free to play around with it.
I think I found the problem. You were not including the buttons in your not
selector
I changed the binding to be
var Controls = $('.InterfaceGroup .Interface :button:not(.Interface .Interface :button):not(.Interface .InterfaceGroup :button)');
If your .interface classes had some kind of identifier this would seem to be rather easy. Perhabs you already have such an identifier for other reasons or choose to include one.
http://jsfiddle.net/Dc4dz/
<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" );
Edit: And now Im wondering if you're tackling this problem from the wrong angle.
So I am trying to $('.Interface').each( bind to any .controls not inside a deeper .Interface )
http://jsfiddle.net/Dc4dz/1/
$(".interface").on("click", ".control", function (event) {
alert($(this).text());
event.stopPropagation();
});
The event would be triggered on a .control; it would then bubble up to its .closest( ".interface" ) where it would be processed and further propagation be stopped. Isn't that what you described?
If you want to exclude element in you find, you can use a not filter. As for example, I've taken you function that exclude element and made it way shorter :
$.fn.findExclude = function( Selector, Mask,){
return this.find(Selector).not(this.find(Mask).find(Selector))
}
Now, ill be honest with you, I did not fully understand what you want. But, when i took a look at your function, I saw what you were trying to do.
Anyway, take a look at this fiddle, the result is the same as your : http://jsfiddle.net/KX65p/8/
I think that this is the closest the findExclude
can be optimized:
$.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;
}
Also, see its fiddle with added logs with ellapsed time in milliseconds.
I see that you are worried with the performances. So, I've run some tests, and this implementation takes no longer than 2 milliseconds, while your implementation (as the answer you have posted) sometimes takes around 4~7 millisecods.