getElementsByTagName()
It turns out that document fragments do not implement the
getElementsByTagName()
method. But they do implement the Selectors
API methods: querySelector/querySelectorAll
.
Nobody cares about this except for people that write JavaScript selector engines. If you are one of those people then keep reading.
I can code around the absence of a lot of DOM methods but
getElementsByTagName()
seemed impossible to do without. So I had to fake it!
JavaScript performance
JavaScript is a nice enough scripting language but it is still a scripting language.
For high performance loops you should try to avoid the following:
try
/catch
blocks- function/method calls (even on native objects)
- accessing DOM properties (or anything that involves calling a complex getter/setter)
Please note that this is an ordered list.
The code
With all that in mind I wrote this simple implementation of getElementsByTagName()
which I hope is fast enough for those edge cases where users execute DOM queries on document fragments:
function getElementsByTagName(node, tagName) { var elements = [], i = 0, anyTag = tagName === "*", next = node.firstChild; while ((node = next)) { if (anyTag ? node.nodeType === 1 : node.nodeName === tagName) elements[i++] = node; next = node.firstChild || node.nextSibling; while (!next && (node = node.parentNode)) next = node.nextSibling; } return elements; }
Update: I’ve amended the original code based on the comments below.
Some things to consider:
- Document fragments cannot have a parent node.
- This method will only be used to query document fragments, so you can assume that the context node will not be an element.
Comments (24)
Leave a comment
Comment: #1
You can probably optimize a bit more by avoiding double `node.parentNode` access. E.g.:
Comment: #2
NICE!
Comment: #3
I guess this completely overlooks the idea that the NodeList is supposed to be “live”, but what can you expect for a JS shim… I like the way you did the traversal there!
Comment: #4
Note that IE does support getElementsByTagName on document fragments. Assuming that it performs well, I check if the built-in function exists and use it if it does, else use my fallback (which wasn’t nearly as slick as yours).
Comment: #5
Might still need some work:
Comment: #6
There’s a logic error in the line:
It’ll exit the loop when node’s parentNode doesn’t have a nextSibling, though there are plenty of cases where this doesn’t mean all nodes have been visited. E.g. it’ll miss <d> in <a><b><c></c></b></a><d></d>.
To fix it, you’d need to replace that line with something like:
Comment: #7
If you’re focused on optimization, why use equivalence testing (==) when you could use equality testing (===)? A quick test in Firefox 3.5 shows that “*” == “*” takes about 40% longer than “*” === “*”. Might not be much compared to the DOM accessing, but hey, it’s a free optimization, and you’re doing three of them!
Comment: #8
Equally, if performance is the goal, why have an if statement that checks something unchanging out of the loop? Certainly it doubles the size of the code, but it removes a needless comparison.
Comment: #9
@Scott : you may wish to stop in your parent node loop at the context node supplied to the function.
@Jakob/Jim : I have also suggested memoising the the “*”
Comment: #10
@Jakob
Last time I tried testing the performance of ‘==’ vs. ‘===’ I didn’t notice any difference. In theory it should be faster because the interpreter has less to think about, but that would depend on how the interpreter has been coded I guess.
Comment: #11
Forgot the case where the supplied node is empty.
Comment: #12
Final attempt (forgetting that the context node should not be returned):-
Comment: #13
@Jakob and Pete: I suppose in using “===” we would need to be wary of the unlikely case : “a” !== (new String(“a”))
Comment: #14
Thanks for the feedback everybody, particularly Julian. I’ve updated my code based on your suggestions.
Comment: #15
I was doing that this way, but your implementation is faster. Thanks!
Comment: #16
Another thing to avoid for performance is deleting properties (in all the latest engines, that’s something that is really expensive, due to the representation of objects internally).
Comment: #17
It could be a nice NodeList with this method :
Comment: #18
[…] getElementsByTagName() […]
Comment: #19
Hi Dean, any bench against this, which is live as well?
Comment: #20
@Andrea (WebReflection)
The node list doesn’t need to be live. This is an internal method just for use by selector engines.
Your solution is a good idea but unfortunately it does not work because of a behaviour unique to document fragments. Once you append a document fragment to an element, the fragment disappears and the fragment’s children become direct children of the element that the fragment is appended to. I hope I explained that properly.
Comment: #21
still me, I kinda realize how silly was my snippet the instant after I pressed submit
so … what about this? Still talking about performances, I do like your solution.
Comment: #22
I need some rest …
could be probably smaller via while ...
not sure which one will perform better though ... I'll try some bench later
Comment: #23
[…] Quelle hier […]
Comment: #24
Note that IE does support getElementsByTagName on document fragments., and since this post was written in 2010, and its 2015 now, There is a goods chances that its already implemented in all the other frameworks.
Comments are closed.