Blog of Jeff A blog about programming and random other things.

22Jul/09link

“Brief” Guide to JavaScript – Part 7

If you haven’t already, check out the previous parts of this series:

Utility Functions

Various functions that are useful when you start doing more than simple JavaScript/DOM manipulation code.

Typeof

Sometimes you'll want to determine what type of data a variable contains, you can use the typeof keyword which returns a string of the type (parenthesizes are optional, but recommended):

typeof('hello world') // => 'string'
typeof 123 // => 'number'
typeof(true) // => 'boolean'
typeof(function(){}) // => 'function'

It's worth mentioning a quirk in typeof with arrays and objects:

typeof([1 2 3]) // => 'object'
typeof({hello: 'world'}) // => 'object'

We'll talk about why when we cover objects in depth, but arrays are actually objects.

Math

As the name suggests, the Math object contains mathematical functions and constants. For convenience, I've copied over the documentation listed at W3Schools. I wish there is more to say about these functions but they're mostly self-explanatory. They come in handy every now and then.

Properties:

  • Math.E - Euler's Constant (~2.718)
  • Math.LN2 - Value of the natural log of 2 (~0.693)
  • Math.LN10 - Value of the natural log of 10 (~2.302)
  • Math.LOG2E - Value of the base-2 log of E (~1.442)
  • Math.LOG10E - Value of the base-2 log of E (~0.434)
  • Math.PI - Circular Constant 22/7 (~3.141)
  • Math.SQRT1_2 - Value of square root of 1/2 (~0.707)
  • Math.SQRT2 - Value of square root of 2 (~1.414)

Methods:

  • Math.abs(x) - Absolute value of x. Converts negative numbers to positive equivalents.
  • Math.acos(x) - ArcCosine (cos-1) of x.
  • Math.atan(x) - ArcTangent (tan-1) of x. Value returned is between -PI/2 and PI/2 radians.
  • Math.atan2(y, x) - Returns angle theta of an (x, y) as a numeric value between -PI and PI radians.
  • Math.ceil(x) - Returns the value of x rounded upwards to the nearest integer.
  • Math.cos(x) - Returns Cosine of x.
  • Math.exp(x) - Returns Ex.
  • Math.floor(x) - Returns the value of x rounded downwards to the nearest integer.
  • Math.log(x) - Returns the natural logarithm (base E) of x.
  • Math.max(x, y) - Returns the highest value of x and y.
  • Math.min(x, y) - Returns the lowest value of x and y.
  • Math.pow(x, y) - Returns xy.
  • Math.random() - Returns a random decimal between 0 and 1.
  • Math.round(x) - Rounds x to the nearest integer.
  • Math.sin(x) - Returns Sine of x.
  • Math.sqrt(x) - Returns square root of x.
  • Math.tan(x) - Returns tangent of an angle x.

Strings

As with everything else in JavaScript strings are objects. There are some functions you may find useful when operating with Strings.

  • str.anchor(anchorid) - A shortcut for creating an a-anchor tag. "My Title".anchor('mytitle') produces <a name="mytitle">My Title</a>.
  • str.link(url) - A shortcut for creating an a-href tag. "Google".link("http://google.com") produces <a href="http://google.com">Google</a>.
  • str.charAt(index) - Gets the character at the specific 0-base index location. Identical to doing str[index].
  • str.toUpperCase() - Returns an uppercase variant of the string. "aBc".toUpperCase() becomes ABC.
  • str.toLowerCase() - Returns a lowercase variant of the string. "aBc".toLowerCase() become abc.
  • str.indexOf(searchStr[, startIndex]) -  Returns the index where searchStr appears in str or -1 if not found.
  • str.replace(searchStr, replaceStr) - Returns a new string of str that replaces the matches of the regular expression of searchStr with replaceStr. You must use regular expressions to replace multiple occurrences of a string. "abccba".replace('a', 'b') produces "bbcba" while "abccba".replace(/a/g, b) produces "bbcbb".
  • str.split(sepStr[, max]) - Returns an array of strings split by sepStr. If an empty string it used, an array of every character of the string is returned. If max is provided, the remaining string will the maximum times the string will be split. "may,june,july".split(",") produces ["may", "june", 'july"].
  • str.match(rexp) - Returns an array of matches found via the regular expression, or null if no matches.

You can view more methods at W3Schools.

 

Timeouts & Intervals

Timers provide an easy way to execute a function at a specified delay. This is done using setTimeout, clearTimeout, setInterval, and clearInterval. setTimeout accepts the function to call and the delay time in milliseconds and returns a timer object which can be passed to clearTimeout to prevent the function from being called. Intervals are like timeouts except that intervals persist, calling the given function every time after a given delay (where as timeouts die after the function is called).

Besides having delays, timeouts and intervals are vital for asynchronous activities as this is the only method, in JavaScript, to do async operations--including animations.

For example, we can fade an element like so:

// quick way to set the property (for IE differences)function opacity(element, value){
    // CSS method of opacity.
    element.style.opacity = value; // 0-1
    if(element.filters){ // IE method of opacity
        element.filters.alpha.opacity = value * 100; // 0-100
    }
}// animate our opacity effect.
function fade(el, start, end, time){
    var curr = start, interv = 13, t = 0;    // how much we should change per interval
    var amt = (end - start) / (time / speed);
    opacity(el, curr); // initial opacity
    var timer = setInterval(function(){
        curr += amt; // new opacity value
        t += interv; // increase time tracker
        opacity(el, curr); // change opacity        // stop animation when we reached out target opacity
        if(t >= time) clearInterval(timer);    // according to john resig, 13 is the min time most browsers will use.    // Lower number is "smoother"
    }, interv); 
}
// usage: fade(document.getElementById('myel'), 0, 1, 1000);// fades in element in 1 second

Animations in JavaScript frameworks are done similar to this method.

Regular Expressions

Another useful feature for any language are regular expressions. Like the DOM tree, regular expressions could have its very own guide. But, unlike the DOM tree, regular expressions are pretty common is higher-level languages, so I'll do a brief overview. After all, this isn't a guide solely for regular expressions.

What are Regular Expressions?

Regular expressions is a way of quickly parsing through a string to find matches, replacements, or just gathering data. For someone who never seen regular expressions, they may appear cryptic. A good reference for getting started with regular expressions is here. Otherwise, prepare for the intense crash course.

Like regular strings, you can match a specific set of letters.

abcdef

Which would only match the string 'abcdef'. By default, regular expressions are case-sensitive, so 'aBcdef' would not be accepted by the expression.

Maybe we want to match a letter from the alphabet, we can do this:

[a-zA-Z]

The square brackets indicates the character must match one of the characters listed inside it. a-z is shorthand for listing all the alphabetical characters where A-Z includes all captial letters.

If we want to match at least one of a particular character, we can use the plus sign.

a+

Which accepts any number of a's (other than zero): 'aaa', 'a', and 'aaaaaaaaaa' all would be accepted.

Star is identical, except it also matches zero of that character:

a*

Accepts everything a+ accepts and no characters (empty string).

Combining what we know, this regular expression is to match only alphabetical characters, which can be done as follows:

[a-zA-Z]+

The + sign indicates that the expression must match one or more of the previous sub-expression (the square brackets, which indicates any alphabetical character).

Having alphanumeric isn't much different.

[a-zA-Z0-9]+

As you can guess, 0-9 represents numbers from 0 to 9.

Shorthand for [0-9], we can use \d to match single digit numbers. So

\d+

Matches any number.

If you just want to match any character known to man, use the dot.

.ell.

This would match 'hello' and 'mello'. But not 'ellot' (no character infront).

Curly braces are used to indicate a more specific value than + or * would provide.

a{3}

Matches only 3 a's. Likewise:

a{3,}

Matches 3 or more a's.

a{,3}

Matches 3 or less a's.

a{3,9}

Matches 3 to 9 a's.

A question mark after a character match indicates an optional match:

abcd?

Matches 'abc' and 'abcd'. If the question mark is used after a + or *, a minimum number of matches is used. This is used to minimize the match range:

a.*c

Would match 'abcabcabc' in the string 'abcabcabc'. Where as:

a.*?c

Would match 'abc' in the string 'abcabcabc'.

Parenthesises are used to group a set of characters as one. Not too significant unless you use other operators

(abc)+

Matches 'abc', 'abcabc', 'abcabcabc', etc.

abc(def)?

Matches 'abc' and 'abcdef'.

So far, most the expressions I've given can match any string that has a substring that matches the expression. So:

[a-z]+

Would accept 'my name is bob', provided matches for 'my', 'name', 'is', 'bob'.

We can provide restrictions than an expression must be at the start of a string or end of a string using ^ and $.

^[a-z]+$

Matches only if the entire string is alphabetical letters. 'mynameisbob' would work, not, 'my name is bob'.

If you want to escape any regular expression special character, use a backslash:

\[hello\]

matches '[hello]'.

That's as much as I'll cover. There are plenty of general-purpose resources on regular expressions you can find via Google.

Now on to the JavaScript specifics of regular expressions.

Constructors

Creating a new regular expression object can be done in two ways. The easiest is using slashes which surround the expression:

var re = /[a-zA-Z]+/;

Alternatively, the new keyword can be used:

var re = new RegExp('[a-zA-Z]+');

The advantage of the latter is being able to use variables, since the constructor accepts a string as the regular expression. In addition, both accept methods can accept additional regular expression flags. The three most common flags are:

  • g - performs a global match. This is used with replace() to replace all occurrences of the expression in a string instead of just the first one.
  • i - performs a case-insensitive match as opposed to the default case-sensitive match.
  • m - performs a multiline match. This makes ^ and $ match the start and end of a line respectively.

Flags are added simply by appending them together:

var rexp1 = /[a-z]+/gi;
var rexp2 = new RegExp('[a-z]*', 'gim');

Using Regular Expressions

The most common uses for regular expressions are used in conjunction with string methods: search, match, replace, and split. Such as replace:

"normal stuff /* comment */".replace(/\/\*.*?\*\//g, '');

This removes your typical multiline comments from strings.

Regular expressions are most commonly used for form validation. An example to check if the user entered a valid email address:

function submitHandler(evt){
    // we're assuming #email_input is the text input where the user entered the email
    var email = document.getElementById('email_input').value;
    if(!email.match(/^[a-zA-Z0-9+._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z]{2,4}(\.[a-zA-Z]{2,4})?$/)){
        alert('Please enter a valid email address!');
        if(evt && evt.preventDefault)
            evt.preventDefault();
        else
            window.event.returnValue = false;
    }
}
// attach submitHandler to the form submit event.

We've covered most of the things here. We have a function that gets the value of the #email_input element (presumably an input field), check if it matches our regular expression, and stop the form from submitting if it doesn't.

Let's breakdown the regular expression monstrosity:

^[a-zA-Z0-9+._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z]{2,4}(\.[a-zA-Z]{2,4})?$

^ indicates that the expression has to match from the very beginning of the string.

[a-zA-Z0-9+._-] allows any alphanumeric, hypen, period, plus, or underscore character.

+ requires at least one character (that matches the above requirements) but can be more than that. In conjunction with [a-zA-Z0-9+._-], we match the username portion of the email address.

@ is simply the text we're looking for: the @ symbol of the email.

[a-zA-Z0-9._-] allows any alphanumeric, hypen, period or underscore character.

+ again, matches the previous expression at least one time.

\. escapes the normal regular expression behavior of the period. Here we just match a period character.

[a-zA-Z] We match an alphabetical character.

{2,4} two to four times. So 'a' would not work, but 'aa' would. This helps match a domain name extension for us (eg - .com, .net, etc.)

(\.[a-zA-Z]{2,4}) Here we do that again, because there are domains with two dots (eg - .co.uk) and we need to check for those too. In this case, we're creating it as a group because...

? We make the matching requirement optional. Allowing the traditional domain name extensions to still work.

$ Indicates we should be at the end of the string now.

Admittedly, some invalid emails will get through, but the point of validation is to help filter the invalid data from entering our system while not hindering possible valid entries.

Using Regular Expressions (from the Perl/Ruby Camps)

If you're used to a programming language like Perl and Ruby, which have more convenient ways of doing loops, use RegExp.exec(), which returns the text if a match is found and null if otherwise.

But the main advantage of exec is it automatically fills the special variables:

RegExp.$1
RegExp.$2

... all the way to ...

RegExp.$9

These special variables are filled with groups. Groups are either the entire regular expression matching or subexpressions in parenthesizes (), which allow us to "capture" text in certain locations in an expression.

As usual, examples always speak better than documentation:

var re = /^(http:\/\/)?(([^ .]+)\.)?([^ .]+)\.([^ .]{2,4})(\/[^ ]*)?$/;
var input = prompt('Enter some URL:');
if(input){
    if(re.exec(input)){
        alert('Subdomain: '+RegExp.$3 + '\n' +
                 'Domain: ' + RegExp.$4 + '.' + RegExp.$5);
    } else {
        alert('Invalid URL');
    }
} else {
    alert('No Input');
}
//

There are also some other new things. First the regular expressions:

[^ .]

The carrot at the beginning makes the matching exclusive. So this would match any character that isn't a space or period.

prompt(message[, default])

Prompt displays a browser-provided dialog input with your message and a space for the user to enter input. An optional default parameter can be filled to provide default input. I don't usually recommend using this function in production for usability issues. The function returns the text the user entered.

We then call our exec function on the given input string to parse. If there was no match, we execute the else statement.

After calling exec, The special RegExp variables are filled. To figure out which grouping you want, count the number of parenthesizes.

$1 is for the http:// or blank if not existant

$2 is the subdomain, including the period at the end

$3 is the subdomain without the period

$4 is the domain name

$5 is the domain name extension

$6 is everything after the domain name part of the URL.

Remember that this numbering only goes up to $9, so try not to have too many groupings in your expression.

As anything, experimentation will solidify your understanding more than me yapping away.


That wraps it up. Next, we’ll talk about some more advanced topics that are lesser used if programming isn’t your normal cup-of-tea. Read part 8.

  • Share/Bookmark
13Jul/09link

“Brief” Guide to JavaScript – Part 6

Events & Handling

In conjunction with the DOM tree, events are an important aspect of the browser-provided functionality. Think of events as a "reaction" to a particular situation. For example, when the user clicks, you may want to do something. Events provide this capability.

Some events browsers provide:

  • load: When the page is finished loaded. Applies only to the window object.
  • unload: When the user is attempting to leave the page. Applies only to the window object.
  • click: When the object is clicked on by the user.
  • dblclick: When the object receives a double click by the user.
  • keyup: When the user releases a key. Applies only to window, body, input and textarea.
  • keydown: When the user presses down on a key. Applies only to window, body, input and textarea.
  • mouseup: When the user releases a mouse button down on the object.
  • mousedown: When the user presses a mouse button on the object.
  • change: When the input data changed (after the user no longer has the element focused). Applies only to input and textareas.
  • focus: When the user provides input focus. Applies only to input, select, and textareas.
  • blur: When the user leaves the input focus. Applies only to input, select and textareas.
  • resize: When the user resizes the browser window that contains your page. Applies only to window object.
  • submit: When the user submits a form. Applies only to form, not submit button.

The load event is useful to access elements not available to your script (html code after your script tag), but there's a flaw which we'll cover in a bit. Normally you would use load if you script is in the head of your document.

Attaching Code to Events

The simplest way to add your own functionality when an event occurs is to set the property with on<eventname>:

// window is prefixed for clarity--we're attaching an event
// to something versus just setting some random variable.
window.onload = function(){
    alert('page loaded!');
};

So when the page finishes loading, an alert is displayed. Quite similar to those attributes you've seen in html documents with on<eventname>="<javascript>". Like <a href="http://google.com" onclick="alert('you\'re leaving my site!')">Google</a>, although embedding JavaScript isn't part of HTML’s philosophy—to represent only data.

The methods above are not ideal. Mostly because only one function can execute per assignment (every element and its events).

window.onload = function(){
    alert('I never get executed');
};
window.onload = function(){
    alert('hello world!');
};

Now if you only intend to have one function call for a particular element's event, that maybe fine for you, but as you write more JavaScript, or write your own framework, this isn't ideal. The solution is to attach functions to events with browser-provided functions. There's the w3c-compliant way, and the IE way.

  • element.addEventListener(type, function, capturing) [w3c-compliant]: Calls function whenever the given event type (as a string) occurs. Capturing should always be false to utilize event bubbling (which is the only kind of event propagation IE supports.)
  • element.attachEvent(type, function) [IE-specific]: Calls function whenever the passed event type (as a string) occurs. Type should be prefixed with 'on'. Only supports event bubbling.

Capturing & Bubbling

There are two kinds of event propagation styles since parent elements also receive the event. This is the preferred method most of the time, especially since IE doesn't support event capturing. But for those curious, I'll briefly explain the difference. The first is event capturing:

Here we're assuming that we caused an event to occur on Element 2 (eg - we clicked on Element 2). The event handlers would fire be called in Element 1, then Element 2.

Then there's event bubbling:

Here the event handlers are fired in Element 2 before Element 1. To reiterate again, IE only supports this event propagation technique.

Event Object

The functions you use for event has a special event object it can access. As usual, there's the "all non-IE browsers" method and the IE method to get to this event object.

The normal method is the event object is passed to the function as the first parameter, while IE stores the event object in the global variable. Unfortunately, IE's event object is a bit different from other browsers.

For most browsers, the functions defined for events are as follows:

function clickHandler(evt){
    // 'this' is a special variable that contains the HTML element that fired it.
    // This only works when using addEventListener or attachEvent.
    // We'll talk about 'this' in a future advanced topic.
    alert(this.innerHTML);
    // alternatively, you can do this:
    alert(evt.target.innerHTML);
}

Where as IE would be something like:

function clickHandler(evt){
    // contains the targeted element, we want to default to document     // in-case IE wasn't called by through an element.
    var target = evt.srcElement || document;
    alert(target.innerHTML);
}

You may have noticed we utilizes the OR expression oddity to our advantage. This ensures we are acting on an element (srcElement or document if srcElement does not exist).

The event object also provides some addition information, such as which keys were pressed and mouse position, but may not be available for all events:

  • evt.keyCode, evt.which (older browsers): Provides the keyCode which the key was pressed.
  • evt.pageX, evt.pageY (non-IE) and evt.clientX, evt.clientY (IE): mouse x & y positions of the mouse.

There are more, but those are the most common.

Browser Specificity

As you can see, we need to write code specific to various browser differences. We can easily do this by checking if the given function is defined. Any browser that doesn't support that function will simply have undefined (since all functions we want to access are usually through objects), so we can use a simple if statement block:

var elem = document.getElementById('mylink');
function clickHandler(){ alert("You've clicked me"); }
if(elem.addEventListener){ // check for the standard event handler
    elem.addEventListener('click', clickHandler, false);
} else { // do the IE method
    elem.attachEvent('onclick', clickHandler, false);
}

Usually you'll want to abstract this into a function:

function addEvent(elem, type, func){
    if(elem.addEventListener)
        elem.addEventListener(type, func, false);
    else
        elem.attachEvent('on'+type, func);
}
// example:
addEvent(document.getElementById('mylink'), 'click', function(){ alert('hello');});

Of course we can do that for anything else like the different properties of events, but you get the idea.

Note: Using the "feature-detection" is recommended instead of detecting the browser to determine what to do.

Stopping Event Propagation

The browser has default behaviors for certain elements, such as going to new url when a user clicks on a link, or a submit input button passing form information to the given action page. Sometimes we want to stop that from happening.

One way is to stop event propagation.

As per usual, this varies by browser *cough* IE *cough*. For most browsers, you call the stopPropagation method for the event object:

function myEventHandler(evt){
    evt.stopPropagation();
}

For IE, set the cancelBubble property of the event object to true.

function myEventHandler(){
    window.event.cancelBubble = true;
}

If we attached this function as a click handler to a link, the user would no longer be transferred to a new url when clicked on. Pretty handy for having links that do web app functionality or stopping a form from submitting when there are errors (eg - Empty required field).

If you want event propagation to still occur, but stop the browser default, you can utilize preventDefault (and returnValue property for IE):

function myEventHandler(evt){
    var evt = evt || window.event || document;
    if(evt.preventDefault)
        evt.preventDefault();
    evt.returnValue = false;
}

Removing Attached Events

Another benefit of using addEventListener and attachEvent is the possibility of removing previously attached functionality to events. This is utilized by removeEventListener and detachEvent which accept the same kind of parameters as their attaching counterparts.

The only difference is that the function passed should be the exact same function that was used to attached it.

// for simplicity, this code only works for w3c-compliant browsers:
function stopOnce(evt){
    evt.stopPropagation(); // can't do whatever
    evt.target.removeEventListener('click', stopOnce, false);
}
document.getElementById('mylink').addEventListener('click', stopOnce, false);

For the given ID-ed link, the user will have to click twice to get to the href location the link refers to. A nice way to annoy your users.

Alternatively, if you like anonymous functions, you can use the special arguments variable:

document.getElementById('mylink').addEventListener('click', function(evt){
    evt.stopPropagation();
    evt.target.removeEventListener('click', arguments.callee, false);
}, false);

I hate sounding like a broken record, but arguments is an advanced topic we'll cover.


Next time, we’ll cover some utility functions and regular expressions. Read part 7.

  • Share/Bookmark
6Jul/09link

“Brief” Guide to JavaScript – Part 5

DOM Tree

This part of the main reason of using JavaScript in the first place. Browsers give JavaScript access to its DOM, or Document Object Model, which is how the browser internalizes the HTML tree. This is an extremely large topic in its own right. A nice article in progress at Nettuts but otherwise you could probably find other resources on the internet besides this article. I wish I could go in-depth about DOM, but I would consume more than half this series (and JavaScript isn't just about the DOM tree). Experimentation and online resources will help you plenty when wrangling with the DOM.

What is it?

The document object model is a tree-styled data structured which represents the html structure of a page. Browsers use DOM as a way to draw the page. Given this example HTML page:

<html>
<head>
    <title>My title</title>
</head>
<body>
    <h1 id="header">My title</h1>
    <p>My paragraph text</p>
    <p id="secondpara">My second <strong>amazing</strong> paragraph.</p>
</body>
</html>

When the browser loads this page, it generates a DOM tree close to the one shown below:

Before we start, we should quickly review some terminology of typical computer-science trees.

  • Node: In the graph, a node is a box. They make up the tree itself.
  • Leaf Node: Nodes that have no nodes underneath it.
  • Internal Node: Nodes that contains more elements underneath it.
  • Root: All nodes are "under" this node. For DOM trees, that's the document node which is the root node for all html elements.
  • Parent: A parent node is a node that is "above" the particular node we're talking about. For example, the body node is the parent node to the h1 node.
  • Child: A node that is directly underneath the node we're talking about. The h1 node is a child node to the body node but the strong node would not be a child node to the body node.
  • Sibling: Nodes that share the same parent node with the node we're talking about. The h1 node has two sibling nodes, both of which are p nodes.

(As you can start to see, most of the naming schemes of trees are similar to family trees.)

Browsers start at the root node and visit every child, grandchild, great grandchild, etc. node to figure out how to draw the page. Any node that isn't a descendant of the root node isn't drawn.

Now let's talk about the content of the tree itself. Each node represents a particular HTML element in the page, including all its css styling and attributes. Text nodes are simply a special node the browsers uses to represent text. Although text nodes are always created when you enter text, the creation of certain text nodes varies from browser to browser.

For example, if you added spacing when typing your html page (like the html I showed to display this tree), the browser would add text nodes because of the spaces and newlines. The only exception is for certain elements that don't accept text nodes directly (like tr or table elements). But pesky Internet Explorer is the only browser that adds text nodes to those elements.

All html nodes are descendants to the document node. Besides being the root for all html elements, the document node is directly accessible to use through javascript and provides access to its child elements. Since html documents always have head and body elements, we can access them easily with the document node:

document.body; // the body element
document.head; // the head element (only works for firefox, afaik)

head, body, html, and all other html elements provides many capabilities, but we'll only cover some of the most common ones:

  • element.tagName: The html tag that represents this element in all uppercase. document.body.tagName would be set to "BODY". and document.head.tagName would be set to "HEAD". Non-html nodes tagName properties are undefined, such as text/comment nodes.
  • element.nodeType: Like tagName, but gives the type of tag the node represents in numeric form:
    • 1 = Element Node: Your typical html element
    • 2 = Attribute Node: An attribute node, like a text node (see last bullet point here)
    • 3 = Text Node: A node that represents text
    • 8 = Comment: A comment in your html page.
    • 9 = Document: The root node for a DOM tree.
  • element.innerHTML: The most popular feature officially not part of the DOM spec. This contains all the html that this element would contain. Using our html page from above, document.head.innerHTML would contain "<title>My title</h1>". You can also set this property to replace the interior html.
  • element.innerText: Like innerHTML, but text only (no html tags). This is useful if you don't want to bother escaping your string to display text in an element.
  • element.style: This provides access to modifying the element's style. All changes only affect the given element, which is identical to using the style attribute in your html tags (<p style="...">). All properties under style are identical to their CSS variants with the exception that a hyphened word is changed to a capitalized word: background-color becomes backgroundColor. The only exception is the float property, which is styleFloat in IE and cssFloat in other browsers. All CSS property values are stored as strings.
  • element.className: Since class is a reserved word in the JavaScript language (saved for the future), className is used. className contains the string of all classes attached to this element.
  • element.getElementsByTagName(tagNameString): A function that accepts a string for the element tag name to search for that this element contains. Returns all the elements with the matching tag.
  • element.attributes: An array of special text nodes that hold all the attributes of the element. Usually you can access the attributes directly in the element (like document.body.id) or using getAttribute, setAttribute, and removeAttribute.

Let's change the h1 text as soon as the page finishes loading. To do this, we need to get to our h1 element.

Luckily, we gave the element an id attribute. JavaScript provides functionality to quickly access id'ed elements: document.getElementById. To quickly access the h1 tag we do this:

<html>
<head>
    <title>My Site</title>
</head>
<body>
    <h1 id="header">My title</h1>
    <p>My paragraph text</p>
    <p id="secondpara">My second <strong>amazing</strong> paragraph.</p>
    <script type="text/javascript">
    // <![CDATA[
    alert(document.getElementById('header').innerHTML); // displays "My title"
    // ]]>
    </script>
</body>
</html>

Where 'header' is the id attribute value. But we want to change the text, the easiest way is to modify the innerHTML text, so let's do that.

<script type="text/javascript">
// <![CDATA[
document.getElementById('header').innerHTML = 'JavaScript Rulez!';
// ]]>
</script>

Let's say we want to make the h1 element in red and have a background of light-light grey. We can use the style properties:

var h1 = document.getElementById('header');
h1.style.color = 'red'; // as if we're setting css style, color
h1.style.backgroundColor = '#eee'; // rgb, hex, and keywords are accepted.
h1.style['background-color'] = '#eee'; // does same as above if you like your hyphens

Styling without those silly stylesheets. Although stylesheets are better, really.

Manually Navigating the Tree

There comes a point where you want to navigate the DOM tree that doesn't have an an ID. Either because you don't want to clutter you page with a bunch of id's or you're creating your own JavaScript framework/embed script That's where we can use the following properties for elements:

  • element.parentNode: gets the parent element based on the current element.
  • element.previousSibling: gets the previous sibling element from the current element.
  • element.nextSibling: like previousSibling, but gets the next sibling instead of the current element.
  • element.childNodes: an array of all of the child elements for this element.
  • element.firstChild: simply a short-hand to get the first child element. Identical to childNodes[0].
  • element.lastChild: short-hand to get the last child element. Identical to childNodes[childNodes.length - 1].

null is returned where any element doesn't exist (like getting the previousSibling for the first element).

I find it easier to learn with examples than definitions. So let's say our h1 element didn't have an ID (where we can use getElementById), but we wanted to get to that element.

We'll start from the top of the DOM tree (document) an work our way to the h1 element. At first glance, the following line may seem to get what we want.

document.body.firstChild;

But there are text nodes, which I didn't show on my diagram since the diagram would be too large. In between and around each element, has a text node. So the code above retrieves the text node before the h1 node (which probably contains a couple newlines and spaces). We can get to the h1 node in several ways:

document.body.firstChild.nextSibling; // the node after the first (text) node
document.body.childNodes[1]; // second node
// gets all h1 tags on the page (we're looking for the first and only one)
document.body.getElementsByTagName('h1')[0];

From there we can do whatever manipulations we want.

Manipulating the Tree

I said we can manipulate the element once we pick it out, but we haven't really talked how to do that. Our handy element object provides some more functions to handle adding and removing new nodes to the DOM:

  • element.appendChild(newNode): Inserts a passed element, newNode, as the last child node for the current element.
  • element.insertBefore(newNode[, referenceNode]): Like appendChild, but inserts the passed element before the given child referenceNode. If referenceNode is not given, then this function behaves like appendChild.
  • element.replaceChild(newNode, childNode): Takes a new node and existing node. Replaces the existing child node with the new node.
  • element.removeChild(childNode): Removes the given child element.
  • element.cloneNode(deep?): Returns a duplicate node to current node. Accepts a boolean (true/false) value that indicates whether to perform a deep clone. A deep clone copies all child elements. The duplicated node has no parent node and thus, not drawn.

Using the previously provided html page, we can move the h1 element to the end by simple re-appending it to the body element:

document.body.appendChild(document.getElementById('header'));

Or maybe you didn't like that header one iota.

document.body.removeChild(document.getElementById('header'));

And away it goes. Now you want the second paragraph to be repeated, for emphasis or your subliminal messaging scheme:

// copies the strong element too.var cloned_paragraph = document.getElementById('secondpara').cloneNode(true);
document.body.appendChild(cloned_paragraph); // attach cloned node to the DOM tree.
cloned_paragraph.id = "thirdpara"; // change the id since IDs need to be unique.

We also have one more function, provided by document, to complete our element manipulation features. document.createElement creates a new, parent-less, element with a given tag name. Let's dynamically add an image to the page. I'm using a local copy of a photo on Flickr to load since hotlinking is evil.

var img = document.createElement('img');
img.src = 'cat.jpg'; // whatever you named it
document.body.appendChild(img); // attach to tree to be displayed

Result:

Kitten! (photo taken by Tambako)

Feel free to experiment on your own. (That's your homework!)

To reiterate, there are good detailed resources about the DOM tree out there. Google and Mozilla are good starting points.

Window Object

As stated earlier, the window object is the browser's way of storing the global state of the page. Listed here are some browser provided features, exposed via the window object.

  • alert(message): You probably know what this does already. Displays a modal dialog with the message as text.
  • document: The browser's DOM tree exposed to JavaScript. We just talked about it above.
  • location: Browser-provided information on page information as an object.
    • hostname: The subdomain and domain name (eg - finance.google.com)
    • host: alias to hostname.
    • href: The full URL the browser used to reach this page. (eg - http://finance.google.com/finance). Changing this property will make the browser go to another url. Use location.href.replace(newURL) to also replace this page in the browser back history with newURL.
    • pathname: The server path used access the page (eg - finance)
    • protocol: The protocol the browser used to access the page. Usually either "http:" or "https:"
  • console.log(message): This isn't implemented in all browsers (just Firefox + Firebug, Opera, Webkit, and Chrome), but prints out a message to the JavaScript Debugger. This is more flexible than alert since javascript arrays and objects can usually be inspected (instead of "object" being printed).

That wraps it up for this week. Next up: Events. Events are utilized for responding to user actions (such as mouse clicks, key presses, etc.). Read part 6.

  • Share/Bookmark

Recent Posts

Topics

Archives

Following

Links