RU beehive logo ITEC dept promo banner
ITEC 325
2017fall
ibarland

homelectshws
D2Lbreeze (snow day)

js-dom-surgery
case study: refactoring javascript

We'll look at using javascript to modify the DOM in response to events. Also, we'll consider an extended example of refactoring some javascript (in the process, illustrating some standard ways of modifying the DOM).

HTML vs. DOM

You have already talked about javascript code and the DOM in WebI; we'll review those concepts in the context of improving an actual web page.

We think of HTML as being a tree. (Or, a way of representing a document's tree-structure as a flat string full of angle-brackets.) The HTML gives content-information associated with nodes of the tree; CSS further provides presentation-information associated with nodes. And the browser even tracks more information with each node: all its default-styles and spacing, and more we'll see below.

Barland's definition:DOM (“Document Object Model”): The tree, as it exists in-memory, representing the web-page; it can be modified after the page is loaded.

Do not attempt to write any significant javascript without a decent debugging tool like Firebug or Chrome Tools' javascript console window help. At least, not if you value your sanity. In particular:

Javascript in a nutshell:

Note that all HTML attributes become DOM corresponding attributes: if you have “<table lang="en" id='sample' contentEditable='true'>…<p>” then the corresponding DOM node will have those attributes:

    var someNode = document.getElementById("sample");
    var isBarlandSpeakingTruth = (someNode.lang==="en" && someNode.innerText==="magic-blue");
    someNode.style.color = (isBarlandSpeakingTruth ? "blue" : "orange");
  
TODO: Use your browser-tools to check the following paragraph (search for "magic-blue"). Then, (a) edit the paragraph's text, and (b) copy/paste the above three javascript lines from within Tools » Console, and see the text change color.

not green!

Weirdnesses/inconsistencies:

A Case Study

We'll show-source, and discuss evolution of the javascript program. For each version: discuss how much work it would be to add another category (animal)? (And: what is the least conceivable amount of work/info required?)

  1. js-hiding-parts-v0.html (An initial version, chock full of gross repeated code; I found this the web, and modified to be about animals. C-)
    youtube (14m11s):
  2. js-hiding-parts-v1.html (A small tweak, greatly reduces length of code. B)
    (This version v1 discussed in the video for v2 below.)
  3. js-hiding-parts-v2.html (Generalize -- much nicer. A)
    youtube (11m01s):
  4. js-hiding-parts-v2-oops.html: Debugging your javascript.
    youtube (10m10s):
    Take note that javascript's for-in construct loops over indices, not contents, so you still need to use square-brackets inside the loop. (This is different from java's foreach and php's foreach).
  5. js-hiding-parts-v3a.html Generalize the entire navbar. We do tree surgery: create new nodes which we splice onto the DOM entirely at run-time. A+.
    youtube (14m31s):

    As seen in the video, the javascript methods-of-interest are createElement (and its friend createTextNode) to create a element(tag), appendChild to add that node into the existing DOM, and setAttribute to add attributes to the element(tag).

  6. Pitfall: js-hiding-parts-v3b.html As before, but placing the paragraph in a different place suddenly breaks the code?!
    youtube (09m33s):
    It breaks because the processing happens ASAP, before the named id has been parsed by the browser. An error message like “no such id” would have been very nice!2 Had my first attempt suffered from this bug, I would have given up assuming my whole approach didn't work.
    See also: attribute defer="defer" for tag script, which waits for the entire page to load before running.
    (Or, less helpfully, the onload attribute which can be placed in body and just a few other tags).
  7. js-hiding-parts-v3c.html, Minor, final touch: Pulling the javascript into its own file, so that it can now be re-used between many pages.
    Reminder (see preceding video): When writing a script tag, never use the self-closing notation (“<script src="" />”), since the browser might ignore it entirely. Grrr!

alternate video: If you don't like the above videos, here is a long (less wieldy) version of the same info, from 2017-Mar-14 distance section (57m05s): introduction to the DOM and javascript to modify it.
In-class example: Applying the above: How can/should we write javascript for a Bingo card? (initial and final versions).

Note: here's an on-line javascript interpreter. We'll use it to note that: arrays are objects; any object can have properties accessed via square-brackets; but you should still iterate over arrays by indexing over [0,data.count). You can also use “forin” to iterate over a general object's properties. For example, .childnodes returns a list, which can be iterated over via “forin”.

arrays vs. objects, in javascript

Arrays in javascript are not associative -- the indices are only numeric.
However: any object (array or string or DOM-node or whatever) can have properties; properties are key/value pairs. So I guess every object could be used as an asssociative array incidentally. This is discouraged, because other code might add properties to your object.

Just to muddle things thoroughly, properties can be assigned/retrieve either through dot-notation (“a.color = "true"”) or through array-brackets (“a['color'] = "true"”) even though they are not arrays.

Arrays have a property named “length”. To demo, paste the below into this on-line javascript interpreter.

var a;
a = [71,72,73];


a.foo = "huh";

writeln( "The length of a: " + a.length );
writeln( "The length of a: " + a['length'] );



writeln( "Using a regular for-loop, to loop over numeric indices:" );
for (var i = 0;   i < a.length;  ++i ) {
  writeln("at index i=" + i + ", a[i]=" + a[i] );
}


writeln( "Using for-in, to loop over all *enumerable* properties:" );
for (var i in a) {
  writeln("at index i=" + i + ", a[i]=" + a[i] );
}
writeln( "Note: the for-in loop will also include properties from the object's "
       + "parent object, and it's parent, etc." );
writeln( "Note: `length` is a property of the object, but the `for` loop doesn't "
       + "enumerate over it.  That's because some properties can be tagged as "
       + "'non-enumerable' -- a concept invented just for for-each loops?" );


Tips for minimizing the difference between php and javascript code:


1To add to the confusion: As we just said, if you want to set a DOM node's html-attribute “class”, you should assign to the DOM-field “className”. However, if you are using the method setAttribute, pass it the html-version: someNode.setAttribute("class","important-info"). Sigh — this inconsistency definitely violates the principle of least surprise (though there may not be any better solution, given the html/DOM mismatch at the heart of the issue).      
2Javascript error tools like Firebug or Chrome Tools' javascript console window help.      
3In fact, jQuery is a javascript library which has a variable named “$”, with its apply-a-function operator “()” overloaded — thus $("a").click(…) is taking the $ object, calling its () operator passing it "a", which apparently returns an object with a method named “click”. …More specifically, “$” is a property that jQuery adds to the window object; it can also be accessed through the name “jQuery”. See more.      

homelectshws
D2Lbreeze (snow day)


©2017, Ian Barland, Radford University
Last modified 2017.Oct.23 (Mon)
Please mail any suggestions
(incl. typos, broken links)
to ibarlandradford.edu
Rendered by Racket.