Descendant Testing
Descendant Testing
(Split from the discussion at http://groups.google.com/group/jquery-dev/browse_thread/thread/5e9d78af4ea552ed)
Stripped down to the basics, what we're discussing here is simply a
new type of filter identifying descendancy that can be applied
directly to any jQuery object and its contained set. I'm defining this
so strictly because in our previous discussion I felt like we were
hitting all over the map.
So, with that in mind, all this needs to do is apply a .filter() with
a fancy helper function. That helper function returns true or false,
passed two DOMElements (tocheck, context) if the element is not
contained. I've elected to use lazy function definition to keep branch
testing to a minimum. The downside of this is that as far as I can
tell we'll not be able to avoid a named helper function to short-
circuit it (correct me if I'm wrong).
I've gone and created preliminary versions these two functions
(untested), stealing liberally from all the ideas tossed around in the
other thread.
Presuming this:
<body>
<div id="foo">
Test 1
<p id="bar">Test 2
</div>
Test 3
Test 4
</body>
// Discussed as many different names (within, contain descendantOf),
but same function.
jQuery.fn.inContext = function ( context ) {
// Returns jQuery.
}
var myelem = document.getElementById('bar');
var mycontext = document.getElementById('foo');
All of these should be equivalent:
$('#bar', mycontext) == $('#bar').inContext(mycontext);
$('p', mycontext) == $('p').inContext(mycontext);
And ignoring that the first function call doesn't exist:
$(myelem, mycontext) == $(myelem).inContext(mycontext);
(For consistency I like implementing this ability in both places, but
that isn't the point right now.)
jQuery.inContext = function ( element, context ) {
if ('compareDocumentPosition' in document.documentElement) {
arguments.callee = function ( element, context ) {
return !!(context.compareDocumentPosition(element) & 16);
}
} else if ('contains' in document.documentElement) {
arguments.callee = function ( element, context ) {
return (element != context && context.contains(element));
}
} else {
arguments.callee = function ( element, context ) {
for ( var parent = element; parent != context; parent =
parent.parentNode )
if ( !parent ) return false;
return true;
}
}
return arguments.callee.apply(jQuery.fn, arguments);
};
jQuery.fn.inContext = function ( context ) {
return this.filter( jQuery.inContext( element, context ) );
};
Additionally, the patch to the $() function would look like this
(resulting in one additional condition check per call on the fastest
of all possible selector types):
if ( selector.nodeType ) {
// Handle $(DOMElement, context)
if ( context && !jQuery.inContext( selector, context ) ) {
return jQuery( [] );
}
// Handle $(DOMElement)
this[0] = selector;
this.length = 1;
return this;
}
Let's continue this discussion here with a blank slate and this as a
starting point. I'm also taking votes on which name it goes by in the
plugin.
Nathan Hammond