Is there an agreed set of "best practices" for use of the DOM Level 0 collections in modern JavaScript applications? (document.forms
, document.images
, etc.)
In applications using jQuery, I've noticed a tendency to, say, change the contents of a drop-down using $(...).html()
to switch out the underlying nodes, rather than using element.options[]
. Is this because the DOM 0 collections are best avoided, or because jQuery ma开发者_JS百科kes it much easier to change the underlying DOM structure?
Edit: I guess part of the question includes if the older features are reliable cross-browser. I remember that, once upon a time, IE would add <tbody>
tags automatically to your table, while firefox would not. That made walking the dom tree painful cross-browser. Similarly, element.options[]
had problems when changing the options in the collection. Are these guys reliable cross-browser?
First off, great question.
DOM Level 0-1 functionality is my favorite toolbox to work with. The support is tremendous. I'll highlight the pros and cons for each subset of DOM Level 0 below:
DOM Level 0 Events
Since these are added as properties of an ElementNode, you can only have one handler. For some instances (eg: event delegation), this is burdensome. However, I feel that the DOM is diverse enough (especially as your project gets bigger) to have enough room for DOM level 0 handlers. DOM Level 2 listeners are painful to implement without a library/framework to smooth over older browsers. Even as a Flash developer (listeners are used everywhere), the DOM 0 events are far easier to me. One of the highlights is the fact that the this
value is set for you (without IE hacking and coughing like the other model). For example, consider this markup:
<div id="foo">Cick Me!</div>
Now, all that needs to be done is selection and attachment of a DOM Level 0 handler.
var foo = document.getElementById("foo");
function bar()
{
console.log(this); //<div id="foo">
}
foo.onclick = bar;
That's a very easy way of selecting elements and an alternative to Event.currentTarget;
There's a great discussion on DOM Level 0 events vs. DOM Level 2 events here: [link]
DOM Level 0 HTMLCollections
Bar none, HTMLCollections are my favorite feature of the DOM. Because nodes are selected for you, there's no need to run queries. In my opinion, they're the most overlooked DOM feature today. Name traversal such as: collection["name"]
is very handy and certainly helps when traversing forms. For example, consider this bit of markup:
<form action="./" id="foo" name="foo" method="post" onsubmit="return false;">
<fieldset>
<legend>Dummy Form</legend>
<input type="text" name="bar">
<select name="baz">
<option selected value="1">1</option>
</select>
</fieldset>
</form>
There are many DOM Level 0 ways to tackle this.
var foo = document.forms.foo; //<form id="foo" onsubmit="return false;" method="post" name="foo" action="./">
Demo: http://jsbin.com/udozoz/0edit#preview
var foo = document.forms[0]; //<form id="foo" onsubmit="return false;" method="post" name="foo" action="./">
Demo: http://jsbin.com/udozoz/2/edit#preview
var foo = document.getElementById("foo"); //<form id="foo" onsubmit="return false;" method="post" name="foo" action="./">
Demo: http://jsbin.com/udozoz/3/edit#preview
Certainly, method 3 is more preferable. It's DOM Level 1, and not DOM Level 0. However, name traversal fits naturally for the HTMLFormElement.elements HTMLCollection. Since you're supposed to use name
attributes on form elements, you can easily access them without an id
attribute.
Ex: var baz = foo.elements.baz;
When using radio buttons that share the same name (to make only one selectable at a time), you can use the HTMLFormElement.elements HTMLCollection to select all of the radio buttons. That's very powerful. Consider this markup:
<form action="./" id="foo" name="foo" method="post" onsubmit="return false;">
<fieldset>
<legend>Radio Buttons</legend>
<label for="select_1">1</label>
<input id="select_1" type="radio" name="selectable" value="a">
<label for="select_2">2</label>
<input id="select_2" type="radio" name="selectable" value="b">
<label for="select_3">3</label>
<input id="select_3" type="radio" name="selectable" value="c">
</fieldset>
</form>
You could use this simple code and have every radio button with the name
attribute value of "selectable":
var foo = document.forms.foo; var selectables = foo.elements.selectable; console.log(selectables); //[input#select_1 a, input#select_2 b, input#select_3 c]
Demo: http://jsbin.com/upiyom/edit#preview
var foo = document.forms.foo; var selectables = foo.selectable; console.log(selectables); //[input#select_1 a, input#select_2 b, input#select_3 c]
Demo: http://jsbin.com/upiyom/2/edit#preview
Option 2 enables you to bypass the elements
HTMLCollection completely. While certainly not as clear as Option 1, it's still used today.
HTMLCollections have become more populous and far more diverse since the introduction of DOM Level 0. For example, take a look at the HTMLCollections available for a table. It's staggering. There's HTMLTableElement.rows, HTMLTableElement.tBodies, HTMLTableSectionElement (thead, tbody, tfoot).rows, and HTMLTableRowElement.cells. Those collections are very powerful, and have made DOM traversal far simpler with tables (permitting you use them).
DOM Level 0 Properties
While the properties on ElementNodes weren't as diverse in DOM Level 0 as they are now, there's still a couple of gems to note:
HTMLInputElement.defaultChecked
defaultChecked
enables you to bypass searching through an HTMLInputElement's checked
attribute completely because it stores a boolean based on that attribute's value. This means you don't have to kludge through sloppy solutions for IE builds related to get/set/removeAttribute. Later on, the defaultValue
property would also be added to fill a similar need.
document.lastModified [non-standard]
lastModified
will store the last time the document was changed. It's a cool little feature that has limited use.
HTMLDocument.title
title
will snag the document's title for you. Its usage is a small niche at best.
In regards to your tbody
question, browsers today do add an HTMLTableSectionElement (tbody) if you don't to promote proper DOM structure. You should get to know proper table markup so this won't be a problem in the future :).
Example markup:
Wrong:
<table>
<!-- tbody will be inserted here and will wrap the tr -->
<tr>
<td>Hello, World!</tr>
</tr>
</table>
Right:
<table>
<tbody>
<tr>
<td>Hello, World!</td>
</tr>
</tbody>
</table>
Demo: http://jsbin.com/exomub/edit#preview
Summary
The main point that needs to be driven home is that most of DOM Level 0 is standardized in DOM Levels 1 and 2. This means the browser support is extensive (since it's really old). There shouldn't be too much apprehension to using it—aside from some edge cases in older browser builds. At the end of the day, it's your choice.
I'd like to add that I've only very briefly—in the past—been employed to develop with HTML/JavaScript. I do it as a hobby, so I'm not privy to "horror stories" about browsers/projects gone wrong.
I hope this cleared a few things up.
-Matt
ElementNode - Node with nodeType == 1
HTMLCollection - Live array-like NodeList collected by the browser
That's a very interesting question. Here are my two cents.
First, it might go without saying, but it depends on whose code you're working on. Professional programmers aim to follow company-wide (or, for larger companies, team-wide) best practices, and if DOM Level 0 is discouraged or forbidden by these guidelines, then you should not use it. If it's either allowed or not mentioned at all, then the decision resolves to a personal choice, like for your own code.
Now, there's no obvious technical drawback that prevents you from using Level 0 if you wish to do so. For instance, I would be surprised if iterating over element.options
was any slower than over element.getElementsByTagName("option")
or $("option", element)
. The first syntax is also arguably more readable than the alternatives.
Browser support is not a problem either. Level 0 is older than dirt and has been supported for more than a decade by every script-aware browser under the sun.
However, the above is about choice, not efficiency, which is what the second half of your question is concerned with. Indeed, you can iterate over element.options
or $("option", element)
or $(element).children("option")
with more or less the same efficiency, but if you have to do heavy work (for instance, wipe out the existing <option>
elements and add new ones), then using element.innerHTML
or $(element).html()
will definitely be faster.
That's because the innerHTML Level 0 property and the html() jQuery method (which uses innerHTML
internally) both delegate all the markup parsing and DOM manipulation to the browser, which is usually written in a lower-level language and heavily optimized for these tasks. Removing the <option>
elements one by one in a Javascript loop will always be slower in comparison, and in this situation, using DOM Level 0 or all the bells and whistles of jQuery makes absolutely no difference.
I'll start by saying that I don't know of any "official" statements like "DOM Level 0 considered harmful". This is just my own opinion.
I think it depends on the case.
The issue I have is that it is problematic, especially with dynamically generated/modified documents, to target specific elements using the collections like documents.forms
or documents.images
. Every time I see someone write document.forms[0]
, I cringe.
Current, and even not-so-current browsers provide the ability via functions like getElementsByTagName
to simulate the same behaviour, but in a more generic and reusable way. Of course, when you add jQuery into the mix, then there's no reason at all to use those collections.
The exceptions I have though, are when you are working in a non-jQuery environment and you're accessing things like selectElement.options
or tbodyElement.rows
. In those cases, it really is simpler when you're doing basic stuff like adding or removing items.
In jQuery-using applications, I've noticed a tendency to, say, change the contents of a drop-down using $(...).html() to switch out the underlying nodes, rather than using element.options[]. Is this because the dom0 collections are best avoided, or because jQuery makes it much easier to change the underlying DOM structure?
That can be explained quite easily. 95% of jQuery developers are ignorant of the fact the DOM API exists or can be used.
The fact that jQuery is abused for things the DOM can do more easily is simply caused by people not learning the DOM.
Personally I'd say use DOM 0, but then again, personally I would say use a DOM shim and don't use jQuery. It's all a matter of choice.
Do whatever is the most maintainable
For better or for worse, I use pure DOM level 0 features mainly for debugging. It is sometimes faster to inspect document.forms[0].elements for inputs.
I also use such features when I'm inspecting some page which use some esoteric (or simply unknown to me) framework. I have no time to dig into those abstractions and just inspecting collections directly.
I do believe you'd better know all this DOM zero arsenal, but it's even better if you know about all this modern pure DOM don't-know-which-level-it-is-exaclty level APIs. ClassList collections are nice, for example. I mean, if you are using a framework, you should always know, why do you exactly need it.
精彩评论