Web Programming Step by Step, 2nd Edition

Lecture TBD: DOM, jQuery

Reading: Chapter 9

Except where otherwise noted, the contents of this document are Copyright 2012 Marty Stepp, Jessica Miller, and Victoria Kirst. All rights reserved. Any redistribution, reproduction, transmission, or storage of part or all of the contents in any form is prohibited without the author's expressed written permission.

Valid HTML5 Valid CSS

Exercise: square positioning

  • Examine the positioning of the #square and #box elements.
  • Initialize the position of the #square element to the upper left corner of it's container.
  • Use the DOM API to target the #square and periodically change it's position in a random direction.
  • Use jQuery instead of the DOM API.

Looping over the DOM

Inside the jQuery each loop

$("li").each(function(idx, e) {
	// do stuff with e
});
$("li").each(function(idx, e) {
	e = $(e);
	// do stuff with e
});
$("li").each(function() {
	// do stuff with this
});

Modifying DOM nodes

DOM nodes have fields that correspond to the attributes in HTML tags. There are a few exceptions

HTML attributes DOM fields
title .title
id .id
class .className
style="prop: value" .style.prop = value

Getting/setting CSS classes

function highlightField() {
	// turn text yellow and make it bigger
	var elem = document.getElementById("id");
	if (!elem.className) {
		elem.className = "highlight";
	} else if (elem.className.indexOf("invalid") < 0) {
		elem.className += " highlight";
	}
}

Getting/setting CSS classes in jQuery

function highlightField() {
	// turn text yellow and make it bigger
	if (!$("#myid").hasClass("invalid")) {
		$("#myid").addClass("highlight");
	}
}

Adjusting styles with the DOM

<button id="clickme">Color Me</button>
window.onload = function() {
	document.getElementById("clickme").onclick = changeColor;
};
function changeColor() {
	var clickMe = document.getElementById("clickme");
	clickMe.style.color = "red";
}
Property Description
style lets you set any CSS style property for an element

Common DOM styling errors

Problems with reading/changing styles

<button id="clickme">Click Me</button>
window.onload = function() {
	document.getElementById("#clickme").onclick = biggerFont;
};
function biggerFont() {
	var size = parseInt(document.getElementById("#clickme").style.fontSize);
	size += 4;
	document.getElementById("#clickMe").style.fontSize = size + "pt";
}

Accessing styles in jQuery

function biggerFont() {
	// turn text yellow and make it bigger
	var size = parseInt($("#clickme").css("font-size"));
	$("#clickme").css("font-size", size + 4 + "pt");
}

Common bug: incorrect usage of existing styles

// bad!
$("#main").css("top", $("#main").css("top") + 100 + "px");

Exercise: style update, positioning

  • Use javascript to initially position the #square element directly in the center of it's parent.
  • Add code to make the #square change direction when it reaches the boundary of it's parent.

Unobtrusive styling

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

jQuery method behavior

jQuery method parameters

Many jQuery object methods are overloaded

getter syntax:
$("#myid").css(propertyName);
setter syntax:
$("#myid").css(propertyName, value);
multi-setter syntax:
$("#myid").css({
	'propertyName1': value1,
	'propertyName2': value2,
	...
	});
modifier syntax:
$("#myid").css(propertyName, function(idx, oldValue) {
	return newValue;
});

What do you think the multi-modifier syntax is?

common jQuery mistake

// bad jQuery
$("#main").css("top", parseInt($("#main").css("top")) + 100 + "px");

jQuery method returns

When there is no other return to make, jQuery methods return the same jQuery object back to you

method return type
$("#myid"); jQuery object
$("#myid").children(); jQuery object
$("#myid").css("margin-left"); String
$("#myid").css("margin-left", "10px"); jQuery object
$("#myid").addClass("special"); jQuery object

jQuery chaining


$("#main").css("color", "red");
$("#main").attr("id", "themainarea");
$("#main").addClass("special");

The implictly returned jQuery object allows for chaining of method calls.

$("img")
	.css("color", "red")
	.addClass("special")
	.src = "foo.png";

Expression return value at each line:

// [<img />, ...]
// [<img style="color: red" />, ...]
// [<img class="special" style="color: red" />, ...]
// cannot chain further because this is an assignment :(

Exercise: another style update

  • Update the bouncing images javascript code to use the better setter jQuery style.
  • Add several divs with a class of square to the html.
  • Make sure that our code still makes all of them move around the page randomly.
  • Clean up the code to take full advantage of jQuery syntax.
  • Change the background-color of each square when it hits a wall.

jQuery attr() function

$("img")				// poor jQuery style
	.css("color", "red")
	.addClass("special")
	.src = "foo.png";
$("img")				// good jQuery style
	.css("color", "red")
	.addClass("special")
	.attr("src", "foo.png");
	// we could chain further right here

More node manipulation with jQuery

Suppose you already have a jQuery object, e.g. $("#myid")

jQuery method functionality
.hide() toggle CSS display: none on
.show() toggle CSS display: none off
.empty() remove everything inside the element, innerHTML = ""
.html() get/set the innerHTML without escaping html tags
.text() get/set the innerHTML, HTML escapes the text first
.val() get/set the value of a form input, select, textarea, ...
.height() get/set the height in pixels, returns a Number
.width() get/set the width in pixels, return a Number

DOM innerHTML hacking

Why not just code the previous example this way?

document.getElementById("myid").innerHTML += "<p>A paragraph!</p>";
  • Imagine that the new node is more complex:
    • ugly: bad style on many levels (e.g. JS code embedded within HTML)
    • error-prone: must carefully distinguish " and '
    • can only add at beginning or end, not in middle of child list
document.getElementById("myid").innerHTML += "<p style='color: red; " +
		"margin-left: 50px;' " +
		"onclick='myOnClick();'>" +
		"A paragraph!</p>";

Creating new nodes

name description
document.createElement("tag") creates and returns a new empty DOM node representing an element of that type
document.createTextNode("text") creates and returns a text node containing given text
// create a new <h2> node
var newHeading = document.createElement("h2");
newHeading.innerHTML = "This is a heading";
newHeading.style.color = "green";

Modifying the DOM tree

Every DOM element object has these methods:

name description
appendChild(node) places given node at end of this node's child list
insertBefore(newold) places the given new node in this node's child list just before old child
removeChild(node) removes given node from this node's child list
replaceChild(newold) replaces given child with new node
var p = document.createElement("p");
p.innerHTML = "A paragraph!";
document.getElementById("myid").appendChild(p);

Removing a node from the page

var bullets = document.getElementsByTagName("li");
for (var i = 0; i < bullets.length; i++) {
	if (bullets[i].innerHTML.indexOf("child") >= 0) {
		bullets[i].parentNode.removeChild(bullets[i]);
	}
}

jQuery manipulation methods

Create nodes in jQuery

The $ function to the rescue again

var newElement = $("<div>");
$("#myid").append(newElement);

jQuery programmers typically 1 line it

$("#myid").append($("<div>"));

The previous example becomes this with jQuery


var bullets = document.getElementsByTagName("li");
for (var i = 0; i < bullets.length; i++) {
	if (bullets[i].innerHTML.indexOf("children") >= 0) {
		bullets[i].parentNode.removeChild(bullets[i]);
	}
}
$("li:contains('child')").remove();

Exercise: lots of squares

  • Add some number of .square divs to the page using jQuery.
  • Change the divs to be img tags with a src of laughing_man.jpg.

Creating complex nodes in jQuery

The terrible way, this is no better than innerHTML hacking
$("<p id='myid' class='special'>My paragraph is awesome!</p>")
The bad way, decent jQuery, but we can do better
$("<p>")
	.attr("id", "myid")
	.addClass("special")
	.text("My paragraph is awesome!");
The good way
$("<p>", {
	"id": "myid",
	"class": "special",
	"text": "My paragraph is awesome!"
});

jQuery $ function signatures

Responding to the page ready event
$(function);
Identifying elements
$("selector", [context]);
Upgrading DOM elements
$(elements);
Creating new elements
$("<html>", [properties]);

DOM example

Here is what it might look like if you tried to insert an image before each special span tag in a div using the DOM's API.

var spans = document.querySelectorAll("#ex1 span.special");
for (var i = 0; i < spans.length; i++) {
	var img = document.createElement("img");
	img.src = "images/laughing_man.jpg";
	img.alt = "laughing man";
	img.style.verticalAlign = "middle";
	img.style.border = "2px solid black";
	img.onclick = function() {
		alert("clicked");
	}
	spans[i].insertBefore(img, spans[i].firstChild);
}
Special Special Not Special Special Not Special

Poor jQuery example

$("#ex2 span.special").each(function(i, elem) {
	var img = $("<img>")
		.attr("src", "images/laughing_man.jpg")
		.attr("alt", "laughing man")
		.css("vertical-align", "middle")
		.css("border", "2px solid black")
		.click(function() {
			alert("clicked");
		});
	$(elem).prepend(img);
});
Special Special Not Special Special Not Special

Good jQuery example

$("#ex3 span.special").prepend($("<img>", {
	"src": "images/laughing_man.jpg",
	"alt": "laughing man",
	"css": {
		"vertical-align": "middle",
		"border": "2px solid black"
	},
	"click": function() {
		alert("clicked");
	}
}));
Special Special Not Special Special Not Special