Out of the way JSON!

If CSS can be un-obtrusive, so can JavaScript

Myles Eftos

MadPilot Productions

JavaScript is OK!

JavaScript has a bad wrap

Remember calling methods like this?

<a href="javascript:alert('Hello World!');">Say Hello</a>

It works really well — until you turn JavaScript off, then your browser doesn't know what to do.

This is the CSS equivalent of

<font color="#FF0000;">Styled text<font>

A slightly better method

Just like the style attribute allowed us to get rid of the font tag, we can use the onclick attribute to move javascript out of the href.

<a href="hello_world.html" onclick="alert('Hello World!'); return false;">Say Hello</a>

It still works... But it's a bit ugly. It's the CSS equivalent of

<span style="color: #FF0000;">Styled text</span>

Not overly practical if you want to change a number of tags.

Thankfully, stealing politely borrowing from CSS we can hide our JavaScript away in other files — remember that we want to separate presentation, content and logic.

CSS has done it for years

HTML allows us to apply IDs and class names to tags. CSS exploits this by allowing us to reference them.

<div id="container"> <div id="header" class="navigation"> <ul class="primary"> <li>Home <ul id="secondary"> <li>About</li> <li>Contact</li> </ul> </li> </ul> </div> <div id="content" class="content"> Content would go here... </div> </div>

CSS has done it for years

We used # to reference ids and . to reference class names.

#container { width: 100%; }
#navigation { height: 5em; }
#navigation ul.primary { display: inline; padding: 1em 0 1em 0; }
#navigation ul#secondary { padding: 5em 0 0 0; }

JavaScript can do it too!

HTMLCSSJavaScript
<div>Content</div> div { color: #00F; } document.getElementsByTagName('div');
<div id="myDiv">Content</div> #myDiv { color: #00F; } document.getElementById('myDiv');
<div class="myDiv">Content</div> div.myDiv { color: #00F; } document.getElementsByClassName('myDiv');*
* This method doesn't exist yet. Be patient!

document.getElementsByClassName()

Courtesy of the Prototype Library

(With a few modifications)

document.getElementsByClassName = function(className, parentElement) { var elements = new Array(); var children = (document.getElementById(parentElement) || document.body).getElementsByTagName('*'); for(i = 0; i < children.length; i++) { if (children[i].className.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) { elements.push(children[i]); } } return elements; }

This code is for brevities sake. Don't worry too much if you don't understand it completely — It's not on the test at the end of the talk.

So how do you implement it?

Just as you drop your CSS into an external stylesheet, you can drop your JavaScript into an external JavaScript Library.

  1. Create a file (for the sake of example we will call it myScript.js)
  2. Add this code somewhere in between the <head> tags <script type="text/javascript" src="myScript.js"><script>
  3. The following code (placed in myScript.js will find all of the li elements under the element with the id secondary and will make them alert “Hello world” when clicked: var elements = document.getElementById('secondary').getElementsByTagName('li'); for(i = 0; i < elements.length; i++) { elements[i].onclick = function() { alert('Hello World!'); } }

Back up the truck - we've hit a snag...

If you tried the example on the previous page, your browser will throw and error. The reason? The DOM hasn't loaded at the time the for loop starts running. There are two solutions:

  1. Put the script tag at the bottom on the file (Just above the body end tag).
  2. Bind an event to the window.onload event

The code for the second option is easier than you think:

function bindHelloWorld() { var elements = document.getElementById('secondary').getElementsByTagName('li'); for(i = 0; i < elements.length; i++) { elements[i].onclick = function() { alert('Hello World!'); } } } window.onload = function() { bindHelloWorld(); }

Seems like a lot of work — Why bother?

Separation!

What are the downsides?

* This isn't true — designers ALWAYS find a way of breaking stuff
† This isn't true either — See the next slide for an explanation

document.unload = wrapUp;

Where to go now?

Wherever you want — as with any coding trick, the sky's the limit.

As with every technique: try, learn it, understand it — once you get the hang of it, you'll cry everytime you see an inline JavaScript call!

Other reading

References

  1. Behavioural Separation — Jeremy Keith
  2. Executing JavaScript on page load — Simon Willison
  3. Prototype JavaScript library — Sam Stephenson
  4. Lightbox JS v2.0 — Lokesh Dhakar
  5. The window.onload Problem - Solved! — Dean Edwards