#13265 closed bug (fixed)
parent method fails with text nodes in IE10
Reported by: | markrendle | Owned by: | dmethvin |
---|---|---|---|
Priority: | high | Milestone: | 1.10/2.0 |
Component: | traversing | Version: | 2.0b1 |
Keywords: | Cc: | orkel | |
Blocked by: | Blocking: |
Description
In earlier versions of jQuery, the parent method looked like this:
parent: function (elem) {
var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null;
}
In 2.0.0b1, it looks like this:
parent: function (elem) {
return elem.parentElement;
}
In IE10, it seems text nodes are not elements, and do not have the parentElement property, so parent() returns undefined.
Change History (16)
comment:1 Changed 11 years ago by
Cc: | markelog added |
---|---|
Owner: | set to markrendle |
Status: | new → pending |
comment:2 Changed 11 years ago by
Component: | unfiled → traversing |
---|---|
Priority: | undecided → high |
Status: | pending → open |
I'm inclined to make the same argument here as elsewhere; that text nodes should generally be considered valid input for traversing methods.
comment:3 Changed 11 years ago by
It turns out that satisfying my position would require reverting much of https://github.com/jquery/jquery/commit/65bdfbf07f911d5014e32ab126ef0c568c22bf49 (even Chrome doesn't define *ElementSibling
on text nodes). Looks like it's judgment time for official support/non-support.
comment:4 Changed 11 years ago by
I did encounter this issue in real code; it happens in AngularJS's compile phase, which seems to include the newlines between divs as text nodes with value "\n". It works fine with jQuery 1.9 and earlier, and in Chrome and Firefox with 2.0.
comment:5 Changed 11 years ago by
Man that's just sad. I guess we'll have to back out a lot of those 2.0 optimizations because our users can't easily avoid having text nodes in the set and it doesn't make sense to fork the logic.
comment:6 Changed 11 years ago by
Let's look at documentation for jQuery#parent –
Get the parent of each element in the current set of matched elements, optionally filtered by a selector.
and
Given a jQuery object that represents a set of DOM elements, the .parent() method allows us to search through the parents of these elements...
If you look at documentation for others traversal methods you can find the same quotes. Text nodes, comments, document fragments, etc; aren't elements nodes.
even Chrome doesn't define *ElementSibling on text nodes
This is logical, how can, say, nextElementSibling
property, on text node, can return element node, if text node is not an element?
How can it be "next element" if current node is not an element? Same logic applies to jQuery traversal methods – if, say, jQuery#nextAll
, return only element nodes, current node should be an element, because we search the next elements relative to the current element.
Otherwise jQuery#nextAll
called on text node should return next text nodes, but not an element nodes.
This is why, say, parentElement
prop on element, doesn't return document fragment node (if it's immediate parent), jQuery#parent
, doesn't return it too.
What is bad, that behavior in 2.0 differ from 1.9, but differ or not, jQuery does not, generally, support them, nor data module, nor events and etc; are not fully (or entirely) support them too, same goes for other node types,
i think we don't have tests for them, for that reason, 1.9 traversal methods called on text node situation is just a side effect of jQuery#contents
.
Given by limited use cases, and hit on size (although not big) and perfomance, i say we should stick to W3C specification and our documentation.
That being said, we might make an exception for jQuery#parent
, this is more or less in line with the existing logic, although contradicts our documentation, but i would not hurry with such decision, we might just wait for other examples, if it really widespreaded, others tickets should pop up.
comment:7 Changed 11 years ago by
We've always suffered a backhanded treatment of text nodes, but methods like .contents
, parseHTML
, and even jQuery
itself are explicit about putting them in collections. I took the position last year that we should stop carving out exceptions piecemeal for things like .has
and .parent
, and accept them as valid input (though generally not output) for traversing methods (specifically excluding other modules). As it turns out, we were already pretty solid on this point, but unfortunately it wasn't secured by unit tests—which is how we got here.
But either way, I think the time has come for a clear stance, preferably expressible in a single line of copypasta.
comment:8 Changed 11 years ago by
Traversal module is basically representation of Element Traversal API, it always has been, documentation is explicit about what input and output should be, just like W3C-specification about definition of what element is, it's pretty clear stance i would say.
There are some exceptions, but i think not in this case, for text node, it's logical that it might have parent element. But it's not logical to ask for previous element from a text node if user does not have current element node, but only text node, this is not semantically correct.
If we excluding other modules and we are and we should, leaving only traversal module for that equation it would look as exception to me.
comment:9 Changed 11 years ago by
@markrendle, Can you point to the code that was failing? We're trying to determine whether just ignoring the text nodes during traversal would give us the correct results.
comment:10 Changed 11 years ago by
https://github.com/angular/angular.js/blob/master/src/ng/compile.js#L313
What happens (in my app, at least) is, the call on line 316 to jqLite (which is jQuery if it's present) passes a <div>, a text node "\n" and another <div>. The return is [element, undefined, element], which then causes an error on line 321, "no property nodeType on undefined".
It's possible that splicing the text node out of the array, either at Angular's end or in the jQuery function, would fix the issue; I haven't tried it though.
comment:11 Changed 11 years ago by
Milestone: | None → 1.9.1 |
---|
Thanks Mark! This is funny, their code is basically faced with the same dilemma we have about text nodes and their workaround is what breaks:
https://github.com/angular/angular.js/blob/master/src/ng/compile.js#L318
This could easily be rewritten to avoid the traversal method, and is depending on undocumented behavior as it is right now.
During a meeting discussion we talked about changing the behavior of 1.9.1 traversal methods so I'll mark this for action there.
comment:12 Changed 11 years ago by
Owner: | changed from markrendle to dmethvin |
---|---|
Status: | open → assigned |
I'll touch base with the Angular guys and convince them to rearrange that code slightly, so that they start with an element and append the text node, which is the kosher way to do it.
comment:13 Changed 11 years ago by
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Fix #13265 #13332: Allow .parent/.closest with text nodes. Close gh-1146.
Changeset: 5e29ff7e59a0c97bae02b7512b40486df0f44022
comment:14 Changed 11 years ago by
Milestone: | 1.9.1 → 1.9.2 |
---|
comment:15 Changed 11 years ago by
Fix #13265 #13332: traversing methods with text nodes. Close gh-1145.
Changeset: b734666f4d2e9a92b8ebb99db5b05bd4c82e71f2
Do you have a test case for this? Did you encounter it in real code? I can kind of see how it might happen if you used something like
.contents()
although we generally don't provide full support for text nodes in a jQuery set.