University of Washington CSE 190M

Section 7: DOM

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 : Bouncing Ball

Create a page which contains an animated bouncing ball. You can view an example of this page here. You are given ball.html and ball.css, and you will write ball.js.

Exercise Solution

var ballY = 0;
var ballVelocity = 0;

window.onload = function() {
	$("ball").style.top = ballY + "px";
	$("ball").style.left = (window.innerWidth / 2) + "px";
	setInterval(update, 20);
};

function update() {
	$("ball").style.top = ballY + "px";
	ballY += ballVelocity;
	ballVelocity += 1;
	if (ballY > window.innerHeight) {
		ballVelocity *= -.9;
	}
}

Exercise : Cheerleader

Given cheerleader.html, write the JavaScript code cheerleader.js to echo typed characters to the screen as in this working example.

Inject the pressed keys as li elements inside #cheers. Each character should be in upper case, followed by an exclamation point. The lecture slide on key events will probably be useful to help you determine which key was pressed and convert it from a code to a character.

After you have made the pressed keys appear, modify your code so that each cheer removes itself from the page after two seconds (i.e., 2000 milliseconds).

Exercise Solution

window.onload = function() {
	document.onkeypress = cheer;
};

function cheer(e) {
	var cheer = document.createElement("li");
	cheer.innerHTML = String.fromCharCode(e.charCode).toUpperCase() + "!";
	$("cheers").appendChild(cheer);
	setTimeout(removeCheer, 2000);
}

function removeCheer() {
	var letters = $$("#cheers li");
	if (letters.length > 0) {
		letters[0].remove();
	}
}

Exercise : Turtles All the Way Down

A well-known scientist (some say it was Bertrand Russell) once gave a public lecture on astronomy. He described how the earth orbits around the sun and how the sun, in turn, orbits around the center of a vast collection of stars called our galaxy. At the end of the lecture, a little old lady at the back of the room got up and said: What you have told us is rubbish. The world is really a flat plate supported on the back of a giant tortoise. The scientist gave a superior smile before replying, What is the tortoise standing on? You're very clever, young man, very clever, said the old lady. But it's turtles all the way down!

Stephen Hawking, A Brief History of Time

(see also: Turtles all the way down on Wikipedia)

Exercise : Turtles All the Way Down

We will implement a version of the "turtles all the way down" model (commonly but falsely attributed to Hindu mythology) in which the earth rests on the back of an elephant, which in turn rests on the back of infinite tortoises.

Given turtles.html (containing the earth, elephant, and a single tortoise), write the necessary JavaScript code turtles.js to give the page infinitely-scrolling turtles as in this working example.

Exercise : Turtles All the Way Down

To solve this problem you will need to add a turtle to the bottom of the page every time the user scrolls to the bottom. You can attach an onscroll event handler to the document, so that every time the page is scrolled that event handler will be executed. You may also find these useful:

window.scrollY
The portion of the document that has scrolled off-screen above the currently-visible portion.
window.innerHeight
The height of the visible area of the document on-screen.
document.body.getHeight()
The height of the body tag (i.e., the height of the entire page, on- and off-screen). This is a Prototype function.

Exercise : Turtles All the Way Down

When the user has scrolled to the very bottom of the page, the sum of scrollY (the off-screen portion above) and innerHeight (the on-screen portion) will be equal to the height of the body. That's when you'll want to add another turtle — which will grow the body and result in more scrolling.

To add a turtle, simply append to document.body a new div with the class of turtle.

Can you fix the case where the height of the window is greater than the initial height of the animals?

Exercise Solution

window.onload = function() {
	document.onscroll = turtles;
	turtles(); // in case window height is initially taller than animals
};

function turtles() {
	while (window.scrollY + window.innerHeight >= document.body.getHeight()) {
		var div = document.createElement("div");
		div.className = "turtle";
		document.body.appendChild(div);
	}
}

Exercise : Raptor

A raptor is on the loose. Rawr! He wants to stomp the townspeople. Write JavaScript code to allow the raptor to eat them. The HTML and CSS are already completely written; start from this skeleton of attack.html. (Click the image to run the sample solution.)

Exercise : Raptor

Make it so that when the page first appears, 5 boys are visible in the town. There are already 5 persons in the HTML, but they have no gender. These are stored in the div with id of people as divs with the class of person. Assign them the additional class boy when the page loads (while retaining the class person).

<div id="people">
	<!-- give these 5 divs the class 'boy' -->
  <div class="person"></div>  
  <div class="person"></div>
  <div class="person"></div>
  <div class="person"></div>
  <div class="person"></div>
</div>

Exercise Solution

window.onload = function() {
    prepopulate();
};
function prepopulate() {
	var people = $$("#people .person");
    for (var i = 0; i < people.length; i++) {
        people[i].addClassName("boy");
    }
}

Exercise : Raptor - Add

Add! Adds 5 more people of the currently selected gender to the page. A person is a div with the classes of person and either boy or girl.

<button id="add">Add!</button>

Exercise Solution

window.onload = function() {
    prepopulate();
    $("add").onclick = populate;
};

// Add! button event handler; adds 5 people of current gender
function populate() {
    for (var i = 0; i < 5; i++) {
        var newPerson = document.createElement("div");
        newPerson.addClassName("person");
        newPerson.addClassName("boy");
        $("people").appendChild(newPerson);
    }
}

Exercise : Raptor - Kill

Kill! Randomly "kills" 1/5 of the people of the currently selected gender. Kill them by giving them a class of splat (in addition to their existing person class, but in place of their gender class such as boy or girl).

<button id="kill">Kill!</button>

Exercise Solution

window.onload = function() {
    prepopulate();
    $("kill").onclick = kill;
};
// Get all guys or girls and splat one fifth of them
function kill() {
    var peeps = $$("#people .boy");
    for (var i = 0; i < peeps.length / 5; i++) {
        var randomIndex = Math.floor(Math.random() * peeps.length);
        peeps[randomIndex].removeClassName("boy");
        peeps[randomIndex].addClassName("splat");
    }   
}

Exercise : Raptor - Boys

Boys / Girls: Selects which gender to add or kill.

<label><input id="boys" type="radio"
		name="gender" /> Boys</label>
<label><input id="girls" type="radio"
		name="gender" checked="checked" /> Girls</label>

Exercise Solution

// Helper function to get which gender is currently selected.
// Use the return from this to parameterize populate() and kill().
function getGender() {
    if ($("boys").checked) {
        return "boy";
    } else {
        return "girl";
    }
}

Exercise : Raptor - Clean Up

Clean Up! Removes any dead splatted people from the page (any divs with class splat).

<button id="cleanup">Clean up!</button>

Exercise Solution

window.onload = function() {
    prepopulate();
    $("cleanup").onclick = clearDead;
};

// Clean up the dead! button event handler
function clearDead() {
    var dead = $$("#people .splat");
    for (var i = 0; i < dead.length; i++) {
        dead[i].remove();
    }   
}

Exercise : Raptor - Stomp

Stomp! Makes the raptor move up or down by 75px and also kills 1/5 of both genders. The raptor is an img tag with an id of raptor.

<button id="stomp">Stomp!</button>

Exercise Solution

window.onload = function() {
    prepopulate();
    $("stomp").onclick = stomp;
};

// Stomp! button event handler
function stomp() {
    var pxtop = parseInt($("raptor").getStyle("top"));
    $("raptor").style.top = ((pxtop + 75) % 150) + "px";
    splat("boy");
    splat("girl");
}

Exercise : Raptor - Enrage

Enrage! Applies the CSS class of enrage to the raptor and the page's top h1 heading. In addition, the raptor should be made to be 50px wider than his current width. Clicking the button again removes the class from both elements and returns the width to its previous value. The h1 has an existing CSS class that should not be removed. You are guaranteed that there is exactly one h1 element on the page.

<button id="enrage">Enrage!</button>

Exercise Solution

// Enrage! button event handler
function enrageRaptor() {
    // If enraged -- go back to normal, else get ENRAGED
    if ($("raptor").hasClassName("enrage")) {
        $("raptor").removeClassName("enrage");
        $$("h1")[0].removeClassName("enrage");
        $("raptor").style.width =
        		parseInt($("raptor").getStyle("width")) -
        		50 + "px";
    } else {
        $("raptor").addClassName("enrage");
        $$("h1")[0].addClassName("enrage");
        $("raptor").style.width =
        		parseInt($("raptor").getStyle("width")) +
        		50 + "px";
    }
}

Exercise : Raptor - Patrol

Patrol! (advanced) Makes the raptor animate. He should move right by 4px every 20ms until his left position style is at least 300px, he should change directions and start patrolling to the left until his left position is 10px or less, at which point he stops patrolling.

<button id="patrol">Patrol!</button>

Exercise Solution

// Patrol! event handler code (advanced)
var timer;

function patrol() {
    clearInterval(timer);
    timer = setInterval(patrolRight, 20);
}

function patrolRight() {
    var pxleft = parseInt($("raptor").getStyle("left"));
    pxleft += 4;
    $("raptor").style.left = pxleft + "px";
    if (pxleft >= 300) {
        clearInterval(timer);
        timer = setInterval(patrolLeft, 20);
    }       
}

Exercise Solution

function patrolLeft() {
    var pxleft = parseInt($("raptor").getStyle("left"));
    pxleft -= 4;
    $("raptor").style.left = pxleft + "px";
    if (pxleft <= 10) {
        clearInterval(timer);
        $("raptor").style.top = "5px";
        $("raptor").style.left = "10px";
    }       
}