Wanting to get text node from an element using some 'readable' method, i came to this strange behaviour i cannot explain.
Using following HTML markup:
<div>text node <b>and b element</b></div>
A text node isn't a DOM element, so i was wondering if this works:
var textnode = $('div').contents().filter(':not(*)'); //returns jq empty set
It fails even if using .filter('*')
and doesn't return the text node but other element (b
).
So testing it, i used not() method, which i wasn't sure it works or not for text node:
var textnode = $('div').contents().not('*'); //returns jq empty set
Let admit it. But this one returns the text node:
var textnode = $('div').contents().not(':not(:not(*))'); //returns text node in set
So why .not('*')
is failing while .not(':not(:not(*))')
works ?
I cannot explain it.
PS: .filter(':not(:not(:not(*)))')
fails while i was expecting it to work exactly as .not(':not(:not(*))')
Because the .not(':not(:not(*))')
is not a simple selector (basically, it has more than one "part"), it is treated differently.
Simple selectors (like the .filter(':not(*)')
and .not('*')
) get passed to the jQuery.filter()
function with a boolean indicator named not
.
This indicator which wraps an additional :not(...)
around the selector for .not(selector)
.
jQuery.filter()
returns nodes that filter through the selector. It has an additional check so it only ever returns nodes of nodeType 1 (elements).
For complex selectors (like .not(':not(:not(*))')
), the mechanism works a bit differently; the selector gets passed to jQuery.filter()
but the not
boolean indicator is not set.
Instead, the nodes returned from jQuery.filter()
are compared to the original list of nodes and filtered in or out depending on whether you used .not()
or .filter()
.
Since jQuery.filter()
only ever returns elements, when comparing to the original list of nodes it can't find any match for the text element.
In the case of your .not(':not(:not(*))')
, this means that according to this logic, the text node should not be filtered out, because it's not in the list of nodes to be filtered out that was returned by jQuery.filter()
.
The reason for these different mechanism is probably that you can't just wrap :not(...)
around any complex selector; it could change the intended meaning.
Though they do state in the .contents()
documentation that text nodes are not supported by most of the rest of jQuery.
It could be solved by explicitely only ever returning nodeType 1 nodes from the winnow
function.
Original jQuery 1.11.3 code:
function winnow( elements, qualifier, not ) {
//...
return jQuery.grep( elements, function( elem ) {
return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not;
});
}
Changed code:
function winnow( elements, qualifier, not ) {
//...
return jQuery.grep( elements, function( elem ) {
return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not && elem.nodeType === 1;
});
}
I don't know if this qualifies as a bug, but it is atleast a solvable inconsistency in jQuery. You might want to report it to jQuery.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With