This section is about using JavaScript's Document Object Model (DOM).
The following links may be helpful:
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 write ball.js
.
ball.html
contains the following HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />
<title>Bouncing Ball</title>
<link href="ball.css" type="text/css" rel="stylesheet" />
<script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js"
type="text/javascript"></script>
<script src="ball.js" type="text/javascript"></script>
</head>
<body>
<div>
<img src="redball.png" alt="red ball" id="ball" />
</div>
</body>
</html>
ball.css
contains the following styles:
#ball {
position: fixed;
top: 0px;
}
This problem involves using the setInterval
JavaScript function to update the ball's position every 20
milliseconds. You can position the ball using the CSS properties top
and left
. The ball has position: fixed
, so the ball will be positioned from the top left corner of the page.
You will need to store two pieces of information about the ball: it's current y position and its current y velocity. Every 20 milliseconds, the ball's position should be updated by adding the current velocity into it:
ballY += ballVelocity;
Every 20 milliseconds, the ball's velocity should also be updated by a certain value representing gravity:
ballVelocity += gravity;
You can choose a value for gravity that feels right. Finally, to make the ball bounce, you must test to see if the ball's Y position is greater than the height of the window, and then flip the sign of the ball's velocity. You can access the width and height of the window by using window.innerWidth
and window.innerHeight
.
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; } }
Given the following HTML file, write the JavaScript code cheerleader.js to echo typed characters to the screen as in this working example.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Cheerleader!</title>
<link rel="stylesheet" type="text/css" href="cheerleader.css" />
<script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js"
type="text/javascript"></script>
<script type="text/javascript" src="cheerleader.js"></script>
</head>
<body>
<h1>CSE 190 M Cheerleader!</h1>
<div id="cheerleader"><img src="cheerleader.jpg" alt="Cheerleader!" /></div>
<ul id="cheers"></ul>
<p>Type a letter to cheer!</p>
</body>
</html>
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).
document.observe('dom:loaded', function() { document.observe('keypress', cheer); }); function cheer(event) { var cheer = $(document.createElement('li')); cheer.innerHTML = String.fromCharCode(event.charCode).toUpperCase() + "!"; setTimeout(function() { unCheer(cheer); }, 2000); $('cheers').appendChild(cheer); } function unCheer(cheer) { $(cheer).remove(); }
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)
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 the following HTML file (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.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>turtles all the way down</title>
<link rel="stylesheet" type="text/css" href="turtles.css" />
<script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js"
type="text/javascript"></script>
<script type="text/javascript" src="turtles.js"></script>
</head>
<body>
<div id="earth"></div>
<div id="elephant"></div>
<div class="turtle"></div>
</body>
</html>
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. Adding a single tortoise to the bottom of the page will result in having to scroll the new turtle into view — which in turn will cause a new tortoise to be created, and so on. In this way the page can scroll indefinitely.
You can attach a scroll
event handler to the document
, so that every time the page is scrolled that event handler will be executed. In order to determine when the user has scrolled to the bottom of the page there are three useful page measurements we can use:
window.scrollY
window.innerHeight
document.body.getHeight()
body
tag (i.e., the height of the entire page, on- and off-screen). This is a Prototype function.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
.
Once you've made the page grow indefinitely, you can fix one small special case: what if the height of the window is greater than the initial height of the animals? (You can test this by zooming out a lot and then refreshing.) In this case the initial body height will be less than the window height, and the off-screen portion will be zero. How would you fix this?
problem adapted from original implementation by Kevin Wallace
document.observe('dom:loaded', function() { document.observe('scroll', 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.addClassName('turtle'); document.body.appendChild(div); } }
(Click the image below to run the sample solution.) (solution JS code attack.js
)
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.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<!-- CSE 190M, Lab 6: Raptor (Rarrrr) -->
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Raptor</title>
<link href="http://www.cs.washington.edu/education/courses/cse190m/09sp/labs/section6-raptor/solution/attack.css" type="text/css" rel="stylesheet" />
<script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js" type="text/javascript"></script>
<!-- you write this -->
<script src="attack.js" type="text/javascript"></script>
</head>
<body>
<h1 class="normal">RANDY THE RAPTOR IS HUNGRY</h1>
<div id="city">
<img src="http://www.cs.washington.edu/education/courses/cse190m/09sp/labs/section6-raptor/solution/raptor.png" id="raptor" alt="Raptor" />
<div id="people">
<!-- 5 initial people to play with -->
<div class="person"></div>
<div class="person"></div>
<div class="person"></div>
<div class="person"></div>
<div class="person"></div>
</div>
</div>
<fieldset>
<legend>Attack!</legend>
<p>
<button id="add">Add!</button>
<button id="kill">Kill!</button>
<label><input id="boys" type="radio" name="gender" /> Boys</label>
<label><input id="girls" type="radio" name="gender" checked="checked" /> Girls</label>
</p>
<p>
<button id="cleanup">Clean up!</button>
<button id="stomp">Stomp!</button>
<button id="enrage">Enrage!</button>
<button id="patrol">Patrol!</button>
</p>
</fieldset>
<fieldset id="legend">
<legend>Legend</legend>
<div><div class="person boy"></div> boy</div>
<div><div class="person girl"></div> girl</div>
<div><div class="person splat"></div> raptor splat!</div>
</fieldset>
</body>
</html>
Here are the behaviors to add:
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"> <div class="person"></div> <!-- give these 5 divs the class 'boy' --> <div class="person"></div> <div class="person"></div> <div class="person"></div> <div class="person"></div> </div>
addClassName
function.
div
with the classes of person
and either boy
or girl
.
<button id="add">Add!</button>
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>
<label><input id="boys" type="radio" name="gender" /> Boys</label> <label><input id="girls" type="radio" name="gender" checked="checked" /> Girls</label>
div
s with class splat
).
<button id="cleanup">Clean up!</button>
remove
method.
img
tag with an id
of raptor
.
<button id="stomp">Stomp!</button>
top
style attribute to be either 10px
or 85px
.
getStyle
method.
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>
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>
setInterval
method.
problem by Stefanie Hatcher
window.onload = function() { $("add").onclick = populate; $("kill").onclick = kill; $("stomp").onclick = stomp; $("patrol").onclick = patrol; $("enrage").onclick = enrageRaptor; $("cleanup").onclick = clearDead; // Make it a city of N Men...Initially var people = $$("#people .person"); for (var i = 0; i < people.length; i++) { people[i].addClassName("boy"); } }; // Helper function to get which gender is currently selected. function getGender() { if ($("boys").checked) { return "boy"; } else { return "girl"; } } // Add! button event handler; adds 5 people of current gender function populate() { var gender = getGender(); for (var i = 0; i < 5; i++) { var newPerson = document.createElement("div"); newPerson.addClassName("person"); newPerson.addClassName(gender); $("people").appendChild(newPerson); } } // Kill! button event handler // tells raptor to randomly kill 1/5 of selected gender function kill() { var gender = getGender(); splat(gender); } // Get all guys or girls and splat one fifth of them // Random targets, could over lap splats, // allowing for more randomness double death or not! function splat(gender) { var peeps = $$("#people ." + gender); for (var i = 0; i < peeps.length / 5; i++) { var randomIndex = Math.floor(Math.random() * peeps.length); peeps[randomIndex].removeClassName(gender); peeps[randomIndex].addClassName("splat"); // so future kills won't choose splat victims } } // Clean up the dead! button event handler function clearDead() { var dead = $$("#people .splat"); for (var i = 0; i < dead.length; i++) { dead[i].remove(); } } // Stomp! button event handler function stomp() { var pxtop = parseInt($("raptor").getStyle("top")); $("raptor").style.top = ((pxtop + 75) % 150) + "px"; splat("boy"); splat("girl"); } // 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"; } } // 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); } } function patrolLeft() { var pxleft = parseInt($("raptor").getStyle("left")); pxleft -= 4; $("raptor").style.left = pxleft + "px"; if (pxleft <= 10) { clearInterval(timer); $("raptor").style.top = "5px"; // Reset the Raptor $("raptor").style.left = "10px"; } }
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; } }
document.observe('dom:loaded', function() { document.observe('keypress', cheer); }); function cheer(event) { var cheer = $(document.createElement('li')); cheer.innerHTML = String.fromCharCode(event.charCode).toUpperCase() + "!"; setTimeout(function() { unCheer(cheer); }, 2000); $('cheers').appendChild(cheer); } function unCheer(cheer) { $(cheer).remove(); }
document.observe('dom:loaded', function() { document.observe('scroll', 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.addClassName('turtle'); document.body.appendChild(div); } }
window.onload = function() { $("add").onclick = populate; $("kill").onclick = kill; $("stomp").onclick = stomp; $("patrol").onclick = patrol; $("enrage").onclick = enrageRaptor; $("cleanup").onclick = clearDead; // Make it a city of N Men...Initially var people = $$("#people .person"); for (var i = 0; i < people.length; i++) { people[i].addClassName("boy"); } }; // Helper function to get which gender is currently selected. function getGender() { if ($("boys").checked) { return "boy"; } else { return "girl"; } } // Add! button event handler; adds 5 people of current gender function populate() { var gender = getGender(); for (var i = 0; i < 5; i++) { var newPerson = document.createElement("div"); newPerson.addClassName("person"); newPerson.addClassName(gender); $("people").appendChild(newPerson); } } // Kill! button event handler // tells raptor to randomly kill 1/5 of selected gender function kill() { var gender = getGender(); splat(gender); } // Get all guys or girls and splat one fifth of them // Random targets, could over lap splats, // allowing for more randomness double death or not! function splat(gender) { var peeps = $$("#people ." + gender); for (var i = 0; i < peeps.length / 5; i++) { var randomIndex = Math.floor(Math.random() * peeps.length); peeps[randomIndex].removeClassName(gender); peeps[randomIndex].addClassName("splat"); // so future kills won't choose splat victims } } // Clean up the dead! button event handler function clearDead() { var dead = $$("#people .splat"); for (var i = 0; i < dead.length; i++) { dead[i].remove(); } } // Stomp! button event handler function stomp() { var pxtop = parseInt($("raptor").getStyle("top")); $("raptor").style.top = ((pxtop + 75) % 150) + "px"; splat("boy"); splat("girl"); } // 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"; } } // 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); } } function patrolLeft() { var pxleft = parseInt($("raptor").getStyle("left")); pxleft -= 4; $("raptor").style.left = pxleft + "px"; if (pxleft <= 10) { clearInterval(timer); $("raptor").style.top = "5px"; // Reset the Raptor $("raptor").style.left = "10px"; } }