CSE 190M Web Programming

Lecture TBD: DOM, jQuery

Reading: Chapter 9

Except where otherwise noted, the contents of this document are Copyright 2012 Marty Stepp, Jessica Miller, Victoria Kirst and Roy McElmurry IV. 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

for each loop

var things = ['a', 'b', 'c', 'd', 'e'];

for (idx in things) {
	console.log(idx + ', ' + things[idx]);
}
0, a
1, b
2, c
3, d
4, e
		

functions as first-class citizens

javascript scope

function getAlert(str) {
	var prefix = "Roy: ";
	return function() {
		alert(prefix + str);
	}
}
var myAlertFunc = getAlert("What's up!");
myAlertFunc();						// "Roy: What's up!"

objects literals

var x = {
	'a': 97,
	'b': 98,
	'c': 99,
	'd': 199,
	'mult': function(a, b) {
		return a * b;
	}
};

for each over object literal

var things = {'a': 97, 'b': 98, 'c': 99 };
for (key in things) {
	console.log(key + ', ' + things[key]);
}
a, 97
b, 98
c, 99
		

JSLint

JSLint

The jQuery Library

There is no chapter about jQuery in our course book. Learning jQuery will be an exercise in your ability to navigate online APIs and documentation:

Problems with JavaScript

JavaScript is a powerful language, but it has many flaws:

jQuery framework

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"
 type="text/javascript"></script>

jQuery Design Principles

jQuery is so powerful in part because of these design principles

window.onload

We cannot use the DOM before the page has been constructed. jQuery gives us a more compatibile way to do this.

Exercise: jQuery boilerplate

We will be working on a bouncing images example.

  • Use jQuery to alert some text when the square is clicked. (Use regular DOM syntax when needed)

Aspects of the DOM and jQuery

The DOM tree

DOM tree

Selecting groups of DOM objects

name description
getElementById returns array of descendents with the given tag, such as "div"
getElementsByTagName returns array of descendents with the given tag, such as "div"
getElementsByName returns array of descendents with the given name attribute (mostly useful for accessing form controls)
querySelector * returns the first element that would be matched by the given CSS selector string
querySelectorAll * returns an array of all elements that would be matched by the given CSS selector string

Getting all elements of a certain type

highlight all paragraphs in the document:

var allParas = document.querySelectorAll("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>

Complex selectors

highlight all paragraphs inside of the section with ID "address":

var addrParas = document.querySelectorAll("#address p");
for (var i = 0; i < addrParas.length; i++) {
	addrParas[i].style.backgroundColor = "yellow";
}
<p>This won't be returned!</p>
<div id="address">
	<p>1234 Street</p>
	<p>Atlanta, GA</p>
</div>

Ploblems with DOM API

// identifying a single elemnt
var list = document.getElementById("mylist");

// identifying a group of elements
var specials = document.querySelectorAll("li.special");

jQuery node identification

The $ aka jQuery function selects elements from the DOM using most any CSS selector.

jQuery Selectors

jQuery / DOM comparison

DOM method jQuery equivalent
getElementById("id") $("#id")
getElementsByTagName("tag") $("tag")
getElementsByName("somename") $("[name='somename']")
querySelector("selector") $("selector")
querySelectorAll("selector") $("selector")

Exercise: square identification

  • Use jQuery selectors to identify elements with these properties in a hypothetical page:
    • All p tags that have no children, but only if they don't have a class of ignore
    • Any element with the text "REPLACE_ME" in it.
    • All div tags with a child that has a class of special
    • All heading elements (h1, h2, h3, h4, h5, h6)
    • Every other visible li.
  • Use the DOM API to target the #square and periodically change it's position in a random direction.
  • Use jQuery selectors instead of the DOM API.

jQuery Terminology

the jQuery function
refers to the global jQuery object or the $ function depending on the context
a jQuery object
the object returned by the jQuery function that often represents a group of elements
selected elements
the DOM elements that you have selected for, most likely by some CSS selector passed to the jQuery function and possibly later filtered further

The jQuery object

// false
document.getElementById("id") == $("#myid");
document.querySelectorAll("p") == $("p");

// true
document.getElementById("id") == $("#myid")[0];
document.getElementById("id") == $("#myid").get(0);

document.querySelectorAll("p")[0] == $("p")[0];

Using $ as a wrapper

// convert regular DOM objects to a jQuery object
var elem = document.getElementById("myelem");
elem = $(elem);

var elems = document.querySelectorAll(".special");
elems = $(elems);

DOM context identification

DOM tree
var list = document.getElementsByTagName("ul")[0];
var specials = list.querySelectorAll('li.special');

find / context parameter

jQuery gives two identical ways to do contextual element identification

var elem = $("#myid");

// These are identical
var specials = $("li.special", elem);
var specials = elem.find("li.special");

Types of DOM nodes

<p>
	This is a paragraph of text with a
	<a href="/path/page.html">link in it</a>.
</p>
DOM Tree

Traversing the DOM tree

every node's DOM object has the following properties:

name(s) description
firstChild, lastChild start/end of this node's list of children
childNodes array of all this node's children
nextSibling, previousSibling neighboring nodes with the same parent
parentNode the element that contains this node

DOM tree traversal example

<p id="foo">This is a paragraph of text with a
	<a href="/path/to/another/page.html">link</a>.</p>
navigate tree

Element vs. text nodes

<div>
	<p>
		This is a paragraph of text with a
		<a href="page.html">link</a>.
	</p>
</div>

jQuery traversal methods

Exercise: #box children

  • Use the DOM API to target the children of the box element and change their position.
  • Use the jQuery library to do the same.

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

  • Update the bouncing images javascript code to use better jQuery style.
  • Use javascript to initially position the element with id square 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

  • 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