I find that the DOM approach works best when creating HTML programmatically— when the structure or content of the inserted markup isn’t the same every time—because building long strings
Trang 1I find that the DOM approach works best when creating HTML programmatically— when the structure or content of the inserted markup isn’t the same every time—because building long strings in JavaScript isn’t my idea of a fun afternoon For simpler cases, innerHTMLis easier and faster Figure out the balance you’re comfortable with
The first three methods we’ll deal with—update,replace, and insert—don’t force you
to pick one approach or the other All three can accept either a markup string or a node.
Using update
Element#updatechanges the contents of an element Think of it as a thin wrapper around
innerHTML—just as assigning to the innerHTMLproperty will erase whatever was there before, updatewill discard the original contents of the element, replacing it with what you’ve given
// HTML (before):
<p id="foo"><b>narf</b></p>
// JavaScript:
$('foo').update('<span>thud</span>');
// HTML (after):
<p id="foo"><span>thud</span></p>
It boasts several advantages over innerHTML:
• As explained, it can take a DOM node or a string
• The convenient “automatic script evaluation” you were introduced to in Chapter 4 also applies to Element#update Any scriptelements in the inserted markup will be removed; the code inside them will be extracted and evaluated after the element has been updated
• It gracefully handles some special cases where Internet Explorer tends to choke For instance, most table elements have read-only innerHTMLproperties, as do odd-balls like coland select
Let’s try a DOM node instead of an HTML string:
var span = document.createElement('span');
span.appendChild(document.createTextNode('thud'));
$('foo').update(span);
$('foo').innerHTML; //-> "<span>thud</span>"
Trang 2Using replace
Element#replaceis nearly identical to its brother update, but can be used to replace an
ele-ment (and all its descendants) instead of just changing its contents
CHAINING
Prototype’s augmentation of DOM node instance methods opens the door to method chaining: a
syntac-tic shortcut that makes lines of code read like sentences
Many of the methods in this chapter—specifically those that do not need to return other values—
will return the elements themselves Consider this code:
$('foo').addClassName('inactive');
$('foo').hide();
Because both addClassNameand updatereturn the element itself, this code can be simplified:
$('foo').addClassName('active').hide();
Chaining method calls like this—joining them in a line, each acting upon the return value of the
last—can increase code clarity when used judiciously In this example, we’ve also optimized the code,
removing a redundant call to $to re-fetch the element
Look out for methods that do not return the original element Consider Element#wrap, which
returns the new created parent node:
$('foo').wrap('div');
$('foo').addClassName('moved');
// wrong:
$('foo').wrap('div').addClassName('moved');
We’ve changed the meaning of the code by accident: instead of adding a class name to the
ele-ment with an ID of foo, we’re now adding it to the divthat was created and returned by wrap
Reversing the order of the method calls preserves our intent:
// right:
$('foo').addClassName('moved').wrap('div');
Similarly, note that Element#replacewill return the original element, but that element has been
replaced and is no longer a part of the DOM tree If you want to work with the content that has replaced
it, you’ll need to obtain that reference some other way
Trang 3// HTML (before):
<div id="foo"><span>thud</span></div>
// JavaScript:
$('foo').replace('<p id='foo'><b>narf</b></p>');
// HTML (after):
<p id="foo"><b>narf</b></p>
The new content occupies the same position in the document as its predecessor So replaceis a way to remove an element, keep a finger on its spot in the DOM tree, and then insert something else at that spot
Using insert
Element#insertappends content to an element without removing what was there before
We flirted with insertin Chapter 4, so the syntax will be familiar to you:
// HTML (before):
<div id="foo"><span>thud</span></div>
// JavaScript:
$('foo').insert("<span>honk</span>", 'top');
// HTML (after):
<div id="foo"><span>honk</span><span>thud</span></div>
The second argument is the position of insertion—either before,after,top, or bottom This argument will default to bottomif omitted
// equivalent in meaning:
$('foo').insert("<span>honk</span>", 'bottom');
$('foo').insert("<span>honk</span>");
A more robust syntax can be used to insert several things at once Instead of a string, pass an object as the first argument—the keys are insertion positions and the values are HTML strings
Trang 4// HTML (before):
<div id="foo"><span>thud</span></div>
// JavaScript:
$('foo').insert({ top: "<span>honk</span>", bottom: "<span>narf</span>" });
// HTML (after):
<div id="foo"><span>honk</span><span>thud</span><span>narf</span></div>
The positions beforeand afterare similar to topand bottom, respectively, but you
insert the new elements outside the boundaries of the given element—as siblings, rather
than children
// HTML (before):
<div id="foo"><span>thud</span></div>
// JavaScript:
$('foo').insert({ before: "<span>honk</span>", after: "<span>narf</span>" });
// HTML (after):
<span>honk</span><div id="foo"><span>thud</span></div><span>narf</span>
Using remove
In the DOM API, you must remove an element by calling the removeChildmethod on its
parent:
// to remove "foo"
$('foo').parentNode.removeChild($('foo'));
Prototype adds Element#removein order to circumvent this annoyance:
$('foo').remove();
Note that removing an element from the document doesn’t make it vanish; it can
be reappended somewhere else, or even modified while detached But a detached node
won’t respond to calls to $or document.getElementById(since the node is no longer in
the document), so make sure you preserve a reference to the node by assigning it to a
variable
Trang 5// to remove "foo" and append it somewhere else
var foo = $('foo');
foo.remove();
$('new_container').appendChild(foo);
The readAttribute and writeAttribute Methods
Prototype’s readAttributeand writeAttributemethods are used to get and set attributes
on elements
“Aren’t these superfluous?” you ask “Doesn’t the DOM give us getAttributeand setAttribute?” Yes, it does, and browsers also expose attributes as properties of their object representations—a holdover from the pre-DOM days So, for instance, the href attribute of a link can be fetched with $('foo').getAttribute('href')or even
$('foo').href
But these approaches have compatibility problems Internet Explorer, in particular, exhibits a host of bugs in this area, thwarting our attempts to get identical behavior from all major browsers Element#readAttributeand Element#writeAttributeare wrappers that ensure identical behavior
Using readAttribute
Let’s look at some examples of surprising getAttributebehavior in Internet Explorer: // HTML:
<label id="username_label" class="required" for="username">
<input type="text" id="username" name="username"
disabled="disabled" />
</label>
<a id="guidelines" href="/guidelines.html">Username guidelines</a>
// JavaScript:
var label = $('username_label');
label.getAttribute('class'); //-> null
label.getAttribute('for'); //-> null
var input = $('username');
input.getAttribute('disabled'); //-> true
var link = $('guidelines');
link.getAttribute('href'); //-> "http://www.example.com/guidelines.html"
Trang 6The labelelement has classand forattributes set, but Internet Explorer returns null
for both The inputtag has a disabledattribute with a value of “disabled” (in accordance
with the XHTML spec), but Internet Explorer doesn’t return the literal value—it returns a
Boolean And the aelement points to an absolute URL on the same server, but Internet
Explorer gives us the “resolved” version when we ask for its href
In all three cases, we’re expecting the literal value that was set in the markup—that’s
how getAttributeis supposed to work, and that’s how the other browsers do it When it
was released, Internet Explorer 6 had the best DOM support of any browser, incomplete
as it was; now, six years later, bugs like these make Internet Explorer the slowpoke of the
bunch
In nearly all cases, the value we want is hidden somewhere—we’ve just got to find it
Prototype does the heavy lifting for you
var label = $('username_label');
label.readAttribute('class'); //-> "required"
label.readAttribute('for'); //-> "username"
var input = $('username');
input.readAttribute('disabled'); //-> "disabled"
var link = $('guidelines');
link.readAttribute('href'); //-> "/guidelines.html";
Use readAttributeanywhere you’d use getAttribute It’s safer
Using writeAttribute
As you may expect, writeAttributelets you set attribute values safely in all browsers,
suc-ceeding where Internet Explorer’s setAttributefails:
label.setAttribute('class', 'optional'); // fails
label.writeAttribute('class', 'optional'); // succeeds
But that’s not all It adds a major syntactic time-saver for writing multiple attributes
at once—simply pass an object literal full of attribute names and values
input.writeAttribute('id', 'user_name');
label.writeAttribute({
title: 'Please choose a username.',
'class': 'optional',
'for': 'user_name'
});