CodingBison

This section covers JavaScript's implementation of those DOM APIs that allow us to retrieve/locate elements of an HTML page. Retrieving these elements is useful since once we get a handle of the required DOM node or nodes, then we can modify their attributes to add value to the page; note that different DOM nodes may have different attributes.

We start by providing an outline of these APIs below. The first API, getElementById() takes an ID and returns a handle of that node. The second API, getElementsByName() takes a name and returns all elements that have the specified name. The next API, getElementsByClass() takes a class and returns all elements that belong to that class. The fourth API, getElementsByTagName() takes an HTML tag and returns all elements that are within the specified tags. Lastly, the API querySelectorAll() allows us to get a handle of all elements identified by the provided selectors; selectors can be HTML tags or CSS elements, or a combination of both.

 var element  = getElementById(stringElementId);
 var elements = getElementsByName(stringElementName);
 var elements = getElementsByClassName(stringElementClass);
 var elements = getElementsByTagName(stringElementTag);
 var elements = querySelectorAll(stringSelectors);

Note that one of the APIs in the above list, getElementById() returns a single node where as the remaining three APIs return an array of elements. This is because by design, nodes of an HTML page must have unique id. Thus, when we pass an id, there can only be one element with that id. However, since name, class, and tags can be shared by multiple nodes, the remaining API returns more than one elements.

An easy way to remember the difference between CSS class and CSS id is to think of a class room full of students. One class can have many students, but each student needs to have a unique (student) id!

DOM recommends that we use a distinct id to identify each element; since, html, head, and body elements are unique, we do not need to use an id for them! However, for other elements, whenever needed, we should add an id tag.

We begin this section with a simple HTML page that contains a mix of various types of HTML elements (DOM nodes). To better understand the usage of the above APIs to retrieve HTML nodes, we provide a simple HTML page:

 <!doctype html>
 <html>
 <body>

 <!-- This provides lightweight CSS design -->
 <style type="text/css"> 
 #idForm {border: 2px solid black; border-radius: 12px; width: 17em}
 </style>

 <!-- This is an image -->
 <img src="./gandalfLOTR.png" id="idImage"></img>

 <!-- This is a simple HTML Form -->
 <form name="nameForm" id="idForm"> 
     <p><b> Form: To-Do List </b> <p> 
     Check mark completed tasks: <br>
     <input type="checkbox" name="checkboxSpells" id="idSpell" checked="true"> 
           Practice Spells <br>
     <input type="checkbox" name="checkboxDragon" id="idDragon"> Feed the dragon  <br>
     <input type="checkbox" name="checkboxWand" id="idWand"> Repair broken wand  <br>
     <p> Notes: <br> 
     <input type="text" name="textNotes" id="idInputNotes" value="Add Notes"> </p>
     <input type="submit" name="submitButton" id="idSubmit" value="Click to Update">
 </form>

 <!-- A sample anchor tag --> 
 <a href="http://www.twitter.com"> Twitter: Go tweet something magical! </a>

 <!-- Dummy Element to write text on the page -->
 <div id="div_id"></div>

 <!-- Insert the JavaScript file here -->i
 <script type="text/javascript"  src="./wizard.js"> </script>

 <body>
 </html>

Even though, we can use the name to identify HTML form elements and we do so to demonstrate usage of various APIs, as per W3C recommendation, applications should use the id attribute to identify elements.

Let us go through each of the elements of the above HTML page.

The first element of the page is a CSS-type style element that identifies the "idForm" id of the form. We use this style to encase the HTML form in a box. Note that if the the style is for a id, then the CSS style-sheet uses a "#", as in "#idForm". On the other hand, if the the style needs to be for a class, then the CSS style-sheet uses a ".", as in ".classForm".

The second element of this page is an image element; this is embedded within the <img> tag. This tag uses a "src" descriptor to specify the path of the image. Next, we also assign it an ID using the "id" descriptor.

The next element of this page is an HTML form; this is embedded within the <form> tag. Simply put, an HTML form is used for accepting input from users. In this aspect, an HTML form is rather similar to other types of (non-HTML) forms (e.g. a Tax Return form, School Admission form, etc) that also accept some form of user input (e.g. user identification, yearly income, past school grades, etc).

An HTML form can have several elements of its own. A combination of these elements allows an HTML form to accept input from a user and once submitted, send it to the server. The form element also has an ID. We also assign it a name using the name descriptor; it is allowed for an element to have both ID and name. Typically, the name descriptor is used with form and its elements. This element contains various nodes of input types like checkbox, text, and submit. Each of these elements have a name. The checkbox elements appears as checkbox on the form, where the user can mark his selection. The text element appears as field where the user can input text.

The form also has a submit element; this element appears as a button that the user can click once he has provided all the information. We can also specify the submit button as button type instead of input: "<button type="submit" name="submitButton" value="Click to Update">".

The next element of the above page is an anchor element; this is embedded within the <a> tag. This tag uses an "href" descriptor that we can use to specify a URL. The task of an anchor element is simple -- when the user clicks the link, the user is directed to the specified URL.

The page also has a simple div element; this is embedded within the <div> tag. This element specifies a division or a section for the HTML page and thus provides with a way to separate the HTML page into various divisions. We will use this division tag to add text to this page dynamically.

The last element of this page is a script element; this is embedded with the <script> tag and we have already seen it umpteen times before! We can use this tag to specify JavaScript code and let it run under JavaScript context. Like the img tag, this tag also provides a "src" attribute -- we can use this attribute to provide a file that contains JavaScript code.

Let us first look at the plain HTML page. For this, we begin by keeping the "./wizard.js" page empty. In later examples, we will add JavaScript code to this file to retrieve various elements.

With "./wizard.js" file being blank, here is how the form looks:



Figure: Elements of an HTML form

Note that in the above HTML page, the first checkbox (with name as "checkboxSpells" appears to be checked -- this is because in the form we set the "checked" attribute to be true. By default, this attribute is turned off and this is why the other checkboxes do not appear to be checked.

Next, let us add JavaScript code to "./wizard.js" file so that it can retrieve various elements. We provide the contents of the new file below.

 // Some of the common attributes of interest for HTML node elements.
 var attrList = ["id", "src", "tagName", "name", "checked", "nodeName", 
     "nodeType", "value", "type", "length", "textContent", "textLength", 
     "href", "baseURI", "protocol", "hostname"];

 // Get a handle for the div element.
 var elem = document.getElementById('div_id');

 function printAttributesOfNode(newElem, nameNode) {
     var str = "<br><br>Printing attributes for " + nameNode + ": <br>";
     var strUndefined = "Undefined attributes: ";

     for (var item in attrList) {
         if (newElem[attrList[item]]) {
             str += attrList[item] + ": " + newElem[attrList[item]] + " <br>";
         } else {
             strUndefined += attrList[item] + ", ";
         }   
     }   

     elem.innerHTML += str;
     // Get rid of the last ", " from strUndefined string.
     elem.innerHTML += strUndefined.substring(0, (strUndefined.length-2));
 }

 window.onload = ( function () {
     // Accessing a DOM node using getElementById()
     var imageElem = document.getElementById("idImage");
     printAttributesOfNode(imageElem, "Image");

     // Accessing a DOM node using getElementsByTagName(). This API returns 
     // an array of nodes since multiple nodes can have the same name. Since 
     // the HTML page has only one anchor, we can use allAnchorElems[0].
     var allAnchorElems = document.getElementsByTagName("a");
     printAttributesOfNode(allAnchorElems[0], "Anchor");
 });

In the above file, uses window.onload to wait for the page to load and once the page loads, it calls an anonymous function to update the page. This anonymous function focuses on two elements of the above HTML page: image and anchor. It retrieves a handle for the image element using getElementById() by passing the "idImage" ID of the image node. After that, it uses getElementsByTagName() to retrieve a handle for the anchor element by passing "a" anchor.

Once we have a handle for these elements, it calls printAttributesOfNode() function to print attributes of these elements. Now, attributes of DOM nodes are very specific to each node. To demonstrate this, we collect some of the common attributes and keep them in an array and the printAttributesOfNode() function goes through each of these attributes and if it is defined for an element, then it prints its value, otherwise it declares that this attribute is not defined for this element.

The motivation behind keeping a set of common node attributes in the "attributes" array is that we want to demonstrate that not all attributes are applicable to all nodes.

When we reload the above HTML page with the new "./wizard.js" file, we find the following output in addition to the original form:

 Printing attributes for Image: 
 id: idImage 
 src: file://localhost/home/codingbison/gandalfLOTR.png 
 tagName: IMG 
 nodeName: IMG 
 nodeType: 1 
 baseURI: file://localhost/home/codingbison/wizard_form.html 
 Undefined attributes: name, checked, value, type, length, textContent, textLength, 
         href, protocol, hostname

 Printing attributes for Anchor: 
 tagName: A 
 nodeName: A 
 nodeType: 1 
 textContent: Twitter: Go tweet something magical! 
 href: http://www.twitter.com/ 
 baseURI: file://localhost/home/codingbison/wizard_form.html 
 protocol: http: 
 hostname: www.twitter.com 
 Undefined attributes: id, src, name, checked, value, type, length, textLength

It is very interesting to note that from the set of attributes defined in the "attributes" array, the image and anchor elements do not define all the attributes; this behavior is true for most of the HTML elements. As an example, the image element has a "src" attribute but the anchor element does not. Similarly, the anchor tag has an "href" attribute, but the image element does not.

We can use these attributes to change behavior of nodes. Thus, if we were to set the to a different one, we can simply reset the src attribute above as 'imageElem.src = "./gandalfHobbit.png";'.

Now, let us turn our focus to the form and its elements. For this we modify the updatePage() function of "./wizard.js" file to this time retrieve form and its elements and to also print their attributes. For this, we use getElementsByName() and pass "nameForm" to get handle for the HTML form. In fact, given the tree-like organization of DOM, we can directly access the form using document.forms.nameForm since all the forms in an HTML page are part of the document.forms. As noted above, since there could be multiple elements with the same name, getElementsByName() returns an array. Since there is one element with this name, we refer to the form as the first element of this array.

Next, we retrieve two of the form elements directly using documents.forms.nameForm.checkboxSpells and documents.forms.nameForm.submitButton. DOM allows us to directly access elements of a form. Thus, if we were to look at the properties of the form (allNameElems[0] or document.forms.nameForm in the above example), then we would find that the following nodes are automatically added to this object: checkboxSpells, checkboxDragon, checkboxWand, textNotes, and submitButton. It is due to this reason that we can access these elements directly!

 // Some of the common attributes of interest for HTML node elements.
 var attrList = ["id", "src", "tagName", "name", "checked", "nodeName", 
     "nodeType", "value", "type", "length", "textContent", "textLength", 
     "href", "baseURI", "protocol", "hostname"];

 // Get a handle for the div element.
 var elem = document.getElementById('div_id');

 function printAttributesOfNode(newElem, nameNode) {
     var str = "<br><br>Printing attributes for " + nameNode + ": <br>";
     var strUndefined = "Undefined attributes: ";

     for (var item in attrList) {
         if (newElem[attrList[item]]) {
             str += attrList[item] + ": " + newElem[attrList[item]] + " <br>";
         } else {
             strUndefined += attrList[item] + ", ";
         }   
     }   

     elem.innerHTML += str;
     // Get rid of the last ", " from strUndefined string.
     elem.innerHTML += strUndefined.substring(0, (strUndefined.length-2));
 }

 window.onload = (function () {
     // Accessing a DOM node using getElementsByName(). This API returns 
     // an array of nodes since multiple nodes can have the same name. Since 
     // the HTML page has only one anchor, we should use allNameElems[0].
     allNameElems = document.getElementsByName("nameForm");
     printAttributesOfNode(allNameElems[0], "Form");

     // Accessing a DOM node directly 
     newElem = document.forms.nameForm.checkboxSpells; 
     printAttributesOfNode(newElem, "Checkbox");

     // Accessing a DOM node directly 
     newElem = document.forms.nameForm.submitButton; 
     printAttributesOfNode(newElem, "Submit Button");
 });

We provide the output below.

 Printing attributes for Form:
 id: idForm
 tagName: FORM
 name: nameForm
 nodeName: FORM
 nodeType: 1
 length: 5
 textContent: Form: To-Do List Check mark completed tasks: Practice Spells Visit 
 Bilbo Baggins Repair broken staff
 baseURI: file:///home/codingbison/wizard_form.html
 Undefined attributes: src, checked, value, type, textLength, href, protocol, hostname

 Printing attributes for Checkbox:
 tagName: INPUT
 name: checkboxSpells
 checked: true
 nodeName: INPUT
 nodeType: 1
 value: on
 type: checkbox
 textLength: 2
 baseURI: file:///home/codingbison/wizard_form.html
 Undefined attributes: id, src, length, textContent, href, protocol, hostname

 Printing attributes for Submit Button:
 tagName: INPUT
 name: submitButton
 nodeName: INPUT
 nodeType: 1
 value: Click to Update
 type: submit
 textLength: 20
 baseURI: file:///home/codingbison/wizard_form.html
 Undefined attributes: id, src, checked, length, textContent, href, protocol, hostname

The above output shows that the above selected elements also have their own specific attributes. For example, none of these elements define "src" and "href" attributes. It is worth noting that the form element has a unique attribute "length". This attribute refers to total number of elements present in a form -- in this case, it is five (checkboxSpells, checkboxDragon, checkboxWand, textNotes, and submitButton). The other two elements share three new fields from the attribute array: value, type, and textLength. The value for the checkbox is "on" since we have switched them on my default. Next, the type attribute specifies "checkbox" and "submit" for these two elements. These also have textLength field that specifies the text value of these elements.

For all of the above elements, the nodeType is equal to 1. As seen earlier, this is the value of the nodeType as defined by DOM as ELEMENT_NODE.





comments powered by Disqus