Unobtrusive JavaScript

CSE 190 M (Web Programming), Spring 2008

University of Washington

Except where otherwise noted, the contents of this presentation are © Copyright 2008 Marty Stepp and Jessica Miller and are licensed under the Creative Commons Attribution 2.5 License.

Valid XHTML 1.1 Valid CSS!

Lecture Outline

Unobtrusive JavaScript

writing web pages where there is no JS code in your XHTML body

Unobtrusive JavaScript idea

Obtrusive event handlers (bad)

<body>
	<button id="ok" onclick="okayClick();">Click me</button>
	...
// called when OK button is clicked
function okayClick() {
	$("ok").style.color = "red";
}

Attaching an event handler in JavaScript code

element.event = functionName;
$("ok").onclick = okayClick;

A failed attempt at being unobtrusive

...
	<head>
		<script type="text/javascript" src="myfile.js"></script>
	</head>
	<body>
		<div><button id="ok">Click Me</button></div>
...
// global code
$("ok").onclick = okayClick;   // error: $("ok") is undefined

Browser/page events


The window.onload event

window.onload = functionName;   // global code

// this will run once the page has finished loading
function functionName() {
	element.event = functionName;
	element.event = functionName;
	...
}

An unobtrusive event handler

<body>
  <button id="ok">Click me</button>
...
window.onload = pageLoad;

// called when page loads; sets up event handlers
function pageLoad() {
	$("ok").onclick = okayClick;
}

function okayClick() {
	$("ok").style.color = "red";
}

Why is unobtrusive JavaScript better?

Common unobtrusive JS errors

The keyword this

window.onload = pageLoad;
function pageLoad() {
	$("ok").onclick = okayClick;   // bound to $("ok") here
}

function okayClick() {           // okayClick knows what DOM object
	this.style.color = "red";      // it was called on
}

Fixing redundant code with this

<fieldset>
	<label><input id="Huey" type="radio" name="ducks" /> Huey</label>
	<label><input id="Dewey" type="radio" name="ducks" /> Dewey</label>
	<label><input id="Louie" type="radio" name="ducks" /> Louie</label>
</fieldset>
...
function processDucks() {
	if ($("huey").checked) {
		alert("Huey is checked!");
	} else if ($("dewey").checked) {
		alert("Dewey is checked!");
	} else {
		alert("Louie is checked!");
	}
	alert(this.id + " is checked!");
}

Anonymous functions

function(parameters) {
	the function's code;
}

Anonymous function example

window.onload = function() {
	$("ok").onclick = okayClick;
};

function okayClick() {
	this.style.color = "red";
}

or, the following is even legal (though harder to read and bad style):

window.onload = function() {
	$("ok").onclick = function() {
		this.style.color = "red";
	};
};

Unobtrusive styling

function okayClick() {
	this.style.color = "red";
	this.addClassName("highlighted");
}
.highlighted { color: red; }

Accessing groups of DOM elements

processing groups of DOM element objects for events or styling purposes

Motivation for grouping DOM elements

How would we do each of the following in our JavaScript code?

Each task involves modifying a group of elements to have a common new feature or style...

Accessing DOM element objects

methods in document object for getting DOM elements (* = Prototype):

Getting all elements of a certain type

highlight all paragraphs in document

var allParas = document.getElementsByTagName("p");
for (var i = 0; i < allParas.length; i++) {
	allParas[i].style.backgroundColor = "yellow";
}

<body>
	<p>This is the first paragraph</p>
	<p>This is the second paragraph</p>
	<p>You get the idea...</p>
</body>

Combining with $

highlight all paragraphs inside of the section with ID "footer"

var footerParas = $("footer").getElementsByTagName("p");
for (var i = 0; i < footerParas.length; i++) {
	footerParas[i].style.backgroundColor = "yellow";
}

<p>This won't be returned!</p>
<div id="footer">
	<p>1234 Street</p>
	<p>Atlanta, GA</p>
</div>

Simplifying things with Prototype

highlight all paragraphs inside of the section with ID "footer"

var footerParas = $$("#footer p");
for (var i = 0; i < footerParas.length; i++) {
	footerParas[i].style.backgroundColor = "yellow";
}

$$ and event handlers

listen to clicks on all buttons with class control directly inside of the section with ID "game"

window.onload = function() {
	var gameButtons = $$("#game > button.control");
	for (var i = 0; i < gameButtons.length; i++) {
		gameButtons[i].onclick = gameButtonClick;
	}
};

function gameButtonClick() {
	...
}

Common $$ errors

Combining with $ : Element.select

select all buttons with class control directly inside of the section with ID "game"

var gameArea = $("game");
var gameButtons = gameArea.select("button.control");
for (var i = 0; i < footerParas.length; i++) {
	gameButtons[i].style.color = "yellow";
}