Comments for Fast DOM Queries in Today's Browsers
Regards
No really, what we have here is a lack of support in the underlying technology, ASP.NET is actually using the IDs as they were meant, it is us that are not. I mean, I barely use any of the ASP.NET framework (viewstate and all that giberish), I use it for the C# language, which I happen to like.
I originally attempted this with multi class names and tried to get all elements with a class name in the class string, this is OK but again, not its intended use. We need a group name which can allow an element to be part of one or more groups and we need a fast way of selecting those groups. The stuff I could do with that!!!! Oh man!!
Regards
https://bugzilla.mozilla.org/show_bug.cgi?id=311681
It looks like multiple elements with the same ID working is not guaranteed (though based on the resolution, will continue to for now).
getElementsByName
:
http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/item.asp
Regards
var byName = element.all.item("some-id");
or:
var byName = document.all.item("some-id");
That's pretty much what you are looking for isn't it?
Alex,
One solution that I've only tested on Internet Explorer 6 is to stream down a custom (unrecognized) element with some pre-determined position in relationship to the desired element class in question. Then you query for that custom element by it's tag name, then use the predetermined position to get the actual element to which you want to attach a behavior.
HTML:
<Behavior:MyBehavior />
<div id="SomeBehavingElement"></div>
Behavior Code:
(function()
{
(function()
{
var oBehaviorNodes = document.getElementsByTagName("Behavior:MyBehavior");
for (var nIndex = 0; nIndex
Alex,
One solution that I've only tested on Internet Explorer 6 is to stream down a custom (unrecognized) element with some pre-determined position in relationship to the desired element class in question. Then you query for that custom element by it's tag name, then use the predetermined position to get the actual element to which you want to attach a behavior.
HTML:
Behavior Code:
(function() {
(function() { var oBehaviorNodes = document.getElementsByTagName("Behavior:MyBehavior"); for (var nIndex = 0; nIndex < oBehaviorNodes.length; nIndex++) { var oElement = oBehaviorNodes[nIndex].nextSibling; addBehavior(oElement.id); } })();
function addBehavior(strID) { // ... } })();
Alex,
One solution that I've only tested on Internet Explorer 6 is to stream down a custom (unrecognized) element with some pre-determined position in relationship to the desired element class in question. Then you query for that custom element by it's tag name, then use the predetermined position to get the actual element to which you want to attach a behavior.
HTML:
<Behavior:MyBehavior />
<div id="SomeBehavingElement"></div>
Behavior Code:
(function()
{
(function() { var oBehaviorNodes = document.getElementsByTagName("Behavior:MyBehavior"); for (var nIndex = 0; nIndex < oBehaviorNodes.length; nIndex++) { var oElement = oBehaviorNodes[nIndex].nextSibling; addBehavior(oElement.id); } })();
function addBehavior(strID) { // ... } })();
"IE doesn’t (...) provide client-side XSLT."
I'm not sure what you meant by this because, actually, IE does provide client-side XSLT by the interfaces:
.selectSingleNode(XPathExpr) .selectNodes(XPathExpr) .transform()
which perfectly implement all relevant portions of XSLT and XPath.
Alex,
âIE doesnât (â¦) provide client-side XSLT.â?
Iâm not sure what you meant by this because, actually, IE does provide client-side XSLT by the interfaces:
<node>.selectSingleNode(XPathExpr) <node>.selectNodes(XPathExpr) <processor>.transform()
which perfectly implement all relevant portions of XSLT and XPath.
Yes, it's horrible. Go ahead defend it all you want. Hack up your pages with a hundred elements that have the same ID, great idea.
Even if you hate the elementsById() hack, you're not making a rational case for why the DOM should represent a collision-free hash (which it clearly doesn't anyway since elementsById() works!).
Look, I'm all for speeding things up, but this is just rubbish. Instead of advocating unclean, non-standard hacks, create an architecture based on autonomous controls that handle setting up their event handlers as they are needed, incorporating lazy loading where applicable. I create applications which are used in hospitals, and I can all but gaurantee they are more complicated than the UIs you are dealing with, with hundreds of widgets active (with event handlers) on a page at a time, and I've never had problems with speed that would make me considering resorting to poor practices like this. As each widget is loaded, it handles it's own DOM event handler wiring, and each DOM element is uniquely IDed and keyed to the widget which it belongs to (so many obviuos benefits here, and covered so many times by so many people I won't even bother going into all that here). ID = identifier. 1 element, 1 ID. I'd hate to have to even look at your markup, let alone try to extract any semantic meaning from it if I was a developer on your team.
This method is not scalable, not maintanable, not standard, not good.
And I'm not outraged, just appalled. If you did something like this as a member of any team I was involved with and I had to clean up your mess, then I would be outraged.
You knew all this before you posted the article, which is why you had to dedicate your first paragraph to defending your method Why not just present it as a hack that some people could try and leave it at that?, Rather than trying to say "here's a hack, but I made it so it's a GOOD hack... a hack that the industry should look at as something great.. and because the W3C's standards don't support this, they are a bunch of bone-heads"... your ego is way too big man.
It's just a hack.
What I'm suggesting is that the W3C decided to go the abstract (but slow) route instead of concrete (but fast). Instead of providng a getElementsByClassName (which I'd be much in favor of!) they just said "oh, people can implement that in script". Ever wonder why there's no node.prependChild() method? Because the working group (in it's infinite wisdom) decided that you could just determine a.) whether or not a node had children, b.) choose insertBefore() or appendChild() as necessaray and c.) wrap it up in your own interface if you liked. Working groups fuck up. I'm suggesting that the omission of a fast arbitrary-group primitive from the core DOM spec (and I don't care what it is) is one such fuck-up.
To understand why all the alternatives to XPath and/or elementsById() are slow, you have to know a little bit about how browsers themselves optimize things. Renderers like Mozilla create the JS/DOM bindings for a node only when it is accessed from script the first time. Let that sink in a bit.
There is object creation/allocation cost for every chaff node in an interative search. That cost goes up (linearly, we hope) with the number of chaff nodes. Scripters are at a structural disadvantage from the C/C++ implementation of the DOM that the JS engine overlays. What I'm presenting here is an algorithm that appears to be O(N) and not O(M) (where N is the nodes of interest and M is total nodes in the tree). It is an interesting result regardless of whether or not you find it elegant.
As for whether or not I'm suggesting that this hack is somehow "good", please see the paragraph of deprecating remarks that clearly denotes it as something for people with hard, real-world problems to solve. You seem to think I'm somehow "defending" it as more elegant than it is (I'm not). Weirdly, you just skipped all that and just went straight on to the flaming.
FWIW, I'm working on a permeutation of Dean's excellent HTC+XPath approach for dojo.behavior, but it will still support this method for those who choose to use it. I'm not trying to break standards compliance left and right. I'd like a better solution, but applications are different than pages and we draw the lines differently in the kinds of apps I work on than perhaps the ones you develop.
Regards
Cool thing but I am a validatorian and I use Prototype for a similar thing ( I am so married to $()) ... so it's useless for me and I even think that's not very recommendable to use
Greets, .mario
I really don’t think doing that (Alex’s) hack will make accessing elements with same id faster. The faster way is to use document.all. Most of the browsers implemented document.all now. Please check the fastness by yourself with the following link: http://www.geocities.com/keelypavan/DOMFasterMethods.html . Please click on the second button first as Alex’s method is resetting ids. I tested in IE 6, Firefox 1.5, opera 8.54 and in all these browsers document.all method took almost 0ms. I know it’s not a W3C standard but when all the browsers implemented that, I don;t see any other reason why we should stop using that.
Pavan Keely
That's a great result! It works on nightly Safaris and Konq 3.5.1 as well.
Outstanding.
Meanwhile why do people feel the need to become so outraged? Alex put up the disclaimer that this was not a standards friendly manouver and even called it a 'hack'.
On the upside flaming does prompt some very informative justification posts. From Alex at least.
The problem of not showing up Divs in Firefox is because I am using innerText which is not supported in Firefox, in place of that if you try using document.createTextNode, it displays the Divs and Firefox does support appendChild on document.body. But I have to agree with you that Forefox is not supporting Firefox. I think I somehow missed it because my plan was to test in 3 diff browsers.
Pavan Keely
Pavan Keely
Example 1
Example 2
document.write(document.getElementsByName("example").length)
<B ID="example" NAME="example">Example 1</B> <B ID="example" NAME="example">Example 2</B> <SCRIPT> document.write(document.getElementsByName("example").length) </SCRIPT>
I think the need for speed and appropriate collections to apply behaviors to is worthy, but this is surely not a reasonable way to accomplish it.
Your ids are all still unique and you get the performance boost of getElementById().
My problem was that I had to resize almost all the rows of a table dynamically (it doesn't matter why, we needed this). The strategy was to iterate through all the rows setting the heights to the apropiate values.
This thing takes a lot of time. About 70 ms for a 600 rows table (= 42 seconds).
After some fight with it, I managed to solve it hidding (style.display='none') the div containing the table before doing the resizing and showing it again afterwards (style.display='inline'). This only little thing made the time drop to 2 ms per row (about 1.2 seconds). As you can see, a big improvement!
Aparently, Internet Explorer defers the DOM drawing/update until it is shown.
I hope this helps somebody!
The first query will fill the cache, from there on queries will be immediate.-
The full example is at: my demo place if you want to see the timings.
Hope the cut & paste works, enjoy and comment please.
Here goes the snippet:
/*
-
ElementsById
-
Author: Diego Perini
-
Updated: 06/11/2006
-
Version: 0.0 (from parent)
-
Extracted from latest versions of IPORT/STYLER engines.
-
Returns an array of elements with specified ID. */ function ElementsById($id) { var c=0,i=0,j=0,k=0; var nodes=[],storage=arguments.callee.storage; var childrens=document.body.childNodes,len=childrens.length;
// only BODY elements are checked, no HTML or HEAD nodes/subnodes if (storage && storage.length && storage.length != 0) { k = $id; while (storage[k]) { nodes[nodes.length] = storage[k]; k = $id + '' + (++i); } } else { storage = { length: 0 }; while (len > i) { c = childrens[i]; if ((k = c.id) == $id) { nodes[nodes.length] = c; if(storage[k]) { k = c.id + '' + (++j); } } i++; storage[k] = c; storage.length++; } arguments.callee.storage = storage; } return nodes; }
document.body.childNodes
should be:
document.getElementsByTagName('*')
by cut & paste I took it from the wrong example, but it works, have a look in my site I am updating it.
Diego