# 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.

# 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`.

• Every frame of animation, apply a "gravity" to the ball and increase its downward speed by 1.
• If the ball hits the ground, make it "bounce" up at 90% the velocity it previously had.

# Exercise Solution

```var ballY = 0;
var ballVelocity = 0;

\$("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;
}
}
```

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

# 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 `div`s 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>
```
• HINT 1 (hover)
• HINT 2 (hover)
• HINT 3: Add/remove CSS classes from an element with Prototype's `addClassName` function.

# Exercise Solution

```window.onload = function() {
prepopulate();
};
function prepopulate() {
var people = \$\$("#people .person");
for (var i = 0; i < people.length; i++) {
}
}
```

# 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>
```
• HINT 1
• HINT 2

# Exercise Solution

```window.onload = function() {
prepopulate();
};

function populate() {
for (var i = 0; i < 5; i++) {
var newPerson = document.createElement("div");
\$("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");
}
}
```

# Exercise : Raptor - Boys

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

```<label><input id="boys" type="radio"
name="gender" /> Boys</label>
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 `div`s with class `splat`).

```<button id="cleanup">Clean up!</button>
```
• HINT: You can remove an element from the page by calling its DOM object's `remove` method.

# Exercise Solution

```window.onload = function() {
prepopulate();
};

// Clean up the dead! button event handler
for (var i = 0; i < dead.length; i++) {
}
}
```

# 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>
```
• HINT 1: Move the raptor by setting his `top` style attribute to be either `10px` or `85px`.
• HINT 2: You can find out an object's existing style properties by calling Prototype's `getStyle` method.

# 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").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";
}
}
```