Section B. Hooks


B. Hooks

Many scripts don't work on an entire HTML page, but on specific HTML elements within the page. For instance, Dropdown Menu shouldn't convert every single <ul> in a page to a dropdown menu, but only the <ul>s that you indicate. Usable Forms should not hide all form fields in the document, but only the ones that you have marked as such.

In order to tell the script which HTML elements it should work on, you need hooks. A hook can be any kind of HTML structure, although an attribute, such as id, is the most obvious choice.

When the script starts up, it goes through the document in search of these hooks, and when it finds one it initializes the script for the HTML elements that contain the hook. Thus Dropdown Menu looks for <ul >, and converts those elements to dropdown menus. Usable Forms searches through all <tr>s in the document and takes those with a rel attribute out of the document. Textarea Maxlength searches through all <textarea>s in the document and initializes those that have a maxlength attribute.

Let's discuss a few common hooks.

ID

The best known and most popular hook is id.

var x = document.getElementById('hook'); // initialize x 


The script initiates the behavior on the element with .

This is the simplest way of creating hooks, because the getElementById method does all the dirty work for you. Unfortunately, you can use a certain id only once per document. Therefore id is only useful if a script needs a single hook.

Although that may seem restrictive, sometimes a bit of creativity and good HTML structuring will help you make the most of this single hook. Sandwich Picker does so when it has to find all sandwiches in the page. I could have added an attribute to every sandwich, but since all sandwiches reside in the same table anyway (good HTML structuring!), I decided to give this table an id (well, actually the <tbody> for reasons we'll discuss in 8E).

<table >    <tbody >    <tr price="1.75">           <th colspan="5" >&euro; 1,75</th>    </tr>    <tr>           <td ><input /></td>           <td >Ham</td>           <td ></td>           <td ></td>    </tr>    <tr>           <td ><input /></td>           <td >Cheese</td>           <td ></td>           <td ></td> </tr> // etc. 


[Sandwich Picker, lines 32-37, condensed]

var containers = document.getElementById('startTable').getElementsByTagName('tr'); for (var i=0;i<containers.length;i++) {     var y = containers[i].getElementsByTagName('td');     if (y.length != 4) continue;     // initialize trs } 


The script takes all <tr>s that are descendants of the element with . Any <tr> that does not contain 4 <td>s (i.e., that is a price header) is filtered out, and the script initializes all other <tr>s, since it's now sure they contain sandwiches. In this way, the hook is enough to initialize the script.

class

If you want to initiate the same behavior on several elements, you need other hooks. Take Dropdown Menus. The site I wrote it for had two separate dropdown menus, and therefore I could not use an id. Besides, although the dropdown menus share an ancestor element, the main content area of the site shares the same ancestor, and of course on some pages this area will contain <ul>s, too, which should not become dropdown menus. Therefore I could not use the descendant-of-id trick from Sandwich Picker.

Instead, I opted for a . I convert only <ul>s with this class to dropdown menus.

[Dropdown Menu, lines 7-9]

var lists = document.getElementsByTagName('ul'); for (var i=0;i<lists.length;i++) {     if (lists[i].className != 'menutree') continue;     // initialize dropdown menu on ul } 


If a certain <ul> does not have a docText">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 presentation and behavior separated, and that cannot but lead to clearer Web sites where every attribute serves a clear goal.

Nonetheless, there's a lot to say in favor of using the class attribute. It already has a certain meaning, namely "Deploy styles here." It could just as well say, "Deploy behavior here." Essentially, the class can be both a CSS and a JavaScript hook.

Dropdown Menu's <ul > is the best example. It serves both as a CSS and as a JavaScript hook. Thus you can initiate the dropdown menu behavior and also give the <ul> and its children the special styles any dropdown menu needs in order to be recognizable. As we'll see in a moment, Form Validation could also have benefited from a class hook.

Custom attributes

Textarea Maxlength, Form Validation, and Usable Forms all use custom attributes for hooks, i.e., attributes that are not part of any (X)HTML specification.

<textarea maxlength="300"> <input name="phone" validation="required numeric" /> <tr rel="othercountry"> 


The maxlength custom attribute tells Textarea Maxlength that the <textarea> has a maximum length, and also what that maximum length is. The validation custom attribute tells Form Validation which checks should be performed on this form field. The rel custom attribute tells Usable Forms that the <tr> should be removed from the document, and also that it should be reinserted if the user checks/selects the form field with a matching rel attribute.

I call them custom attributes because I custom-created them for the job. As a result, they are not valid HTML; the validation attribute is not a part of the HTML specification at all. rel is, but only on <a> and <link> tags. maxlength is valid, too, but only on an <input> tag.

This does not matter to JavaScript. The W3C DOM method getAttribute(), which we'll discuss in 8G, allows you to get the value of any attribute, whether it's part of the HTML spec or not.

Nonetheless, it might matter to you as a Web developer. If you have decided your pages should be perfectly valid (X)HTML, you cannot use custom attributes since the validator would choke on them.

What's more important? Valid (X)HTML or a custom attribute that closely cooperates with your script? This important decision is one you should make for yourself. To aid you in this process, let's discuss possible alternatives to the custom attributes of the three scripts.

Form Validation does not need a custom attribute. The validation attribute can easily be rewritten as a class attribute:

<input name="phone"  /> 


This approach has clear advantages, although I didn't realize that when I wrote the script back in 2003. Once you have a you can easily add an extra style to the form fieldfor instance, an icon that communicates that the form field expects numeric input. You now have available a series of class names that contain useful, semantic information about the form fields, such as "This field is required" and "This field is numeric."

Therefore, if you move the values of validation to class, and then change .getAttribute('validation') to .className in Form Validation, the script works better than before.

name/value pairs

Unfortunately, Textarea Maxlength's and Usable Forms' custom attributes are less easy to convert to classes, because I conceived these custom attributes as name/value pairs. Let's discuss Textarea Maxlength first:

<textarea maxlength="300"> 


The maxlength custom attribute contains two bits of data:

  • The fact that this textarea has a maximum length.

  • The value of this maximum length.

The script uses the first bit of data during initialization: it adds event handlers to the textarea, so that its user input can be checked. At this point, the exact value doesn't matter.

[Textarea Maxlength, lines 2-11, condensed]

for (var i=0;i<textareas.length;i++) {     if (textareas [i].getAttribute('maxlength')) {     // administration     textareas[i].onkeyup = textareas[i].onchange = checkMaxLength;         } } 


When the checkMaxLength() function is executed, it needs to know how many characters the textarea may contain. That's no problem; the value of the maxlength attribute contains this information.

[Textarea Maxlength, lines 17-18]

function checkMaxLength() {     var maxLength = this.getAttribute('maxlength');     // compare } 


Let's port this to a class.

<textarea > 


Now the CSS selector for this textarea becomes not textarea.maxlength, but textarea.maxlength=300, which does not work.

Nonetheless, the first job is easy: in order to find out if the script should be initialized for a textarea, find out if its class contains "maxlength":

[Textarea Maxlength, lines 2-11, condensed and changed]

for (var i=0;i< textareas.length;i++) {     if (textareas[i].className.indexOf('maxlength') != -1) {     // administration     textareas[i].onkeyup = textareas[i].onchange = checkMaxLength;         } } 


But what about the value of maxlength? This value is present in the class attribute, but it's not easy to find. If we can be absolutely certain that the value "maxlength=300" is the last one in the class, we do this:

[Textarea Maxlength, lines 17-18, changed]

function checkMaxLength() {     var maxLength = this.className.substring(this.className.indexOf('maxlength')+10);     // compare } 


This is not an example of clear and concise JavaScript, but neither is it extremely complicated. However, suppose now that "maxlength=300" is not last in the class attribute. Our script grows more complex:

[Textarea Maxlength, lines 17-18, changed]

function checkMaxLength() {     var index = this.className.indexOf('maxlength');     var maxLength = this.className.substring(index+10,this.className.indexOf(' ',index));     // compare } 


This, in turn, does not work if "maxlength=300" is the last part of the class attribute after all. I could go on creating code examples, but you get the point: the value of a name/value hook cannot be easily read from the class attribute. Creating the hook as a separate attribute makes much more sense.

Usable Forms has the same problem. The hook <tr rel="othercountry"> would have to be rewritten as <tr >, and reading 'othercountry' requires similar complicated substring statements.

All in all I feel that Textarea Maxlength and Usable Forms are better served by a custom attribute hook than by a class hook. Feel free to disagree with me and use class, but be aware of the difficulties of reading out the value of a name/value pair in the class.



ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
ISBN: N/A
EAN: N/A
Year: 2005
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net