文章目錄
- B.Hooks
- C.Preparing the page
- Choosing between Single and Double Quotes
- Last value counts
- return false
- Differences between W3C and Microsoft Event-handler registration Models
- Interface events: no bubbling
- this or target?
- HTML attributes and JavaScript properities
- Empty text nodes
- create TextNode() and HTML entities
Do not under any circumstance write a script for one browser first and add support for other browers "later on". This is the fastest way to hell. It's far better to solve nasty incompatibilities at the start of your project than at the end.
B.Hooks
Although the class attribute might seem the ideal hook, I have two problems with it:
- It already serves as the most important CSS hook.
- Sometimes you need a name=value hook, and that's pretty hard to pull off in a class.
The first point is extremely subjective, the second rather less so. In the past I strongly felt that class was a CSS hook, and that any JavaScript hook should use another attribute . It keeps persentation and behavior separated, and that cannot but lead
to clearer Web sites where every attribute serves a clear goal.
C.Preparing the page
The most important action the initialization function takes is registering event handlers. As far as the user is concerned, the script starts when he takes action and the page responds.
The most important thing you must do in this phase is clearly picture what the user should to start up the script.
- Determining visitor status
- Setting up access
- Generating content
- Defining relations
Occasionally, I define relations between one HTML elements and another if an event on the first element should trigger changes in the other element.Thus I don't have to search for the othere element when the event takes place: this bit of data has
already been defined.
- Modifying document structure.
Choosing between Single and Double Quotes
Long ago I decided that I'd always use single quotes for JavaScript and double quotes for HTML, even though both languages allow both types of marks. Therefore I nearly always use single quotes to delimit JavaScript Strings.
I advise you to make such a rule for yourself, since it'll make your code more readable and will occasionally prevent errors.
Last value counts
It is important to realize that the nested function has access only to the final value of a local variable. The next example, which looks a bit like the previous one, doesn't do what you might expect:
function init(){ var message = ' Clicked - '; var x = document.getElementsByTagName('a'); for(var i=0; i<x.length; i++){ x[i].onClick = function(){ x[i].firstChild.nodeValue = message; } }}
Here, too, an event handler is defined within another function, and therefore has access to the local variables of that fuction. In the case ofi, though, it doesn't work as expected.
The function init() defines event handlers on all
<a> tags in the document. Let's say we have 10. During that process, i goes from 0 to 10, and when the function exits, it retains this final value 10.
The event handlers fire long after the function has exited, and by that time i has the value 10. Therefore, the event handler uses this value, and that causes an error. Since the tenth link in the document has index number 9, the event handler gives an error
message: it can't find the eleventh link( with index 10) in the document.
The solution is to use the this keyword instead:
x[i].onclick = function(){ this.firstChild.nodeValue = message;}
return false
Event handlers can become methods of the object you register them on provided you register them correctly.return false works only from a function that is directly assigned as a method of an HTML element.
return false works in this case:
var x = document.getElementsByTagName('a');for(var i=0; i<x.length; i++){ x[i].onclick = askConfirmation;}
This, however, does not work:
var x = document.getElementsByTagName('a');for(var i=0; i<x.length; i++) { x[i].onclick = function(){ askConfirmation(); }}
Now the onclick method of the link calls askConfirmation(), and although the function still returns true or false, this return value is not caught, and disappears without a trace.
A return false in an event handler works in the following cases:
x.onclick = askConfirmation;x.addEventListener('click', askConfirmation, false);x.attachEvent('onclick', askConfirmation);
It does not work in the following cases:
<element onclick="askConfirmation()">x.onclick = function(){ askConfirmation();}x.addEventListener('click', function(){ askConfirmation();},false);
An extra return statement will suffice to get these last examples in line:
<element onclick="return askConfirmation()">x.onclick = function(){ return askConfirmation();}x.addEventListener('click', function(){ return askConfirmation();},false);
Differences between W3C and Microsoft Event-handler registration Models
W3C Model:
x.addEventListener('click', doThis, false);
Microsoft Model:
x.attachEvent('onclick',doThis);
- The Microsoft model does not support event capturing. Since you rarely use capturing anyway, this is not a big problem
- The Microsoft model treats the event-handling function as a global function, not a method of the HTML element it's registered on. That means that thethis keyword refers to the window instead of to the object the event
handler is registered on.
Interface events: no bubbling
You cannot use event bubbling in all situations, because some events are not valid on some HTML elements. The most restrictive rule is that interface events such as change or submit don't work on the document or window.
As a general rule, you can catch mouse and keyboard events on the document level, but not interface events. However ,you should always test this general rule for your specific events and browsers you might get lucky.
this or target?
When do you use the this keyword and when do you use the event target? There are a few general rules, but there's also a lot of overlap, especially when you have registered the event on the same element that will be the event target.
- In general, the this keywords is useful when you register the event handler on a lot of elements and/or when you want to call the event handlers directly, i.e., without an event taking place.
- In general, the event target is useful when you rely on bubbling to take the event upward in the document tree.
HTML attributes and JavaScript properities
if(containers[i].getAttribute('price')) currentPrice = containers[i].getAttribute('price');containers[i].price = currentPrice;
The first two lines read the HTML custom attribute price and then set currentPrice to its value. The third line sets the JavaScript price property of the <tr> to the value of currentPrice.
Even though they share a name, the HTML custom attribute price and the JavaScript property are different things. The HTML attribute is located in the document tree as an attribute node of the <tr>, while the JavaScript property belongs to the object that
represents the <tr> and is not present in the document tree.
Empty text nodes
Normal text nodes are easy to work with. Unfortunately, there are also empty text nodes. They are by far the most useless and annoying feature of the W3C DOM, but you'll encounter them in every HTML document you work with.
Consider this HTML snippet:
<body><h1>Hello World!</h1><p>I am a JavaScript hacker!</p></body>
How many child nodes does the <body> have? Two, right? The<h1> and the <p>?
Wrong.
The <body> has five child nodes. Two of them are element nodes, the other three are empty text nodes. There is text between the tags: a hard return between the<body> and the<h1>,
between the</h1> and the<p>,andbetween the</p> and the
</body>.Since spaces, hard returns, and tabs are text content, the W3C DOM creates text nodes to hold them.(Explorer Windows does not support empty text nodes.)
Their first victims are the previousSibling and nextSibling properties,which are rather useless since they usually refer to empty text nodes. The same happens to childNodes[].
The only situation in which properties like nextSibling are useful is when you construct a document tree, or a portion of one, entirely in JavaScript. Then it contains no empty text nodes, and all properties work as you expect them to work.
create TextNode() and HTML entities
There's one problem with createTextNode(): it cannot create HTML entities like © or —. Instead of the symbol you need, it creates the literal text:
var x = document.createTextNode(' Copyright reserved');document.getElementById('test').appendChild(x);
Use innerHTML instead:
document.getElementById('test').innerHTML = ' Copyrights reserved';