By the end of today, you'll have had some experience with using loops to process simple lists of numbers. You'll see how functions and event handlers can make for pages with interactive sound playing. We'll also have a fun Halloween game.
First, let's all play with the example that uses a Javascript function to add an audio element on the fly to a page.
Observations:
onDocumentReady
sets up the page, adding
an onclick
event handler to each icon
The playSound
function is pretty complicated. Here's a
simpler version:
function playSound() { console.log(this) // Check that it refers to the <img> element var word = this.previousSibling.innerHTML; var audio = this.nextSibling; audio.setAttribute("type","audio/mpeg"); audio.setAttribute("autoplay","autoplay"); var url = "https://ssl.gstatic.com/dictionary/static/sounds/de/0/" + word + ".mp3"; audio.setAttribute("src",url); }
Below is the structure of each paragraph in the page:
<p><span>apple</span><img src="icons/play.png" alt="sound icon"><audio></audio></p>
What would happen if we rearranged some of the elements in each paragraph, such as putting the audio element before the icon?
this
would have the
wrong value
playSound
would not get invoked.
this
would be different elements.
Let's consider alternative ways to do a page like this
Is it possible to make the words clickable instead of the icons?
span
elements
Do we need four audio elements?
Eni has built, for our chilly delight, a very cool Halloween-themed game.
Let's look at the materials of the game, starting with the game images
Next, a quick look at the important CSS:
div {float: left; width: 100px; height: 100px; border: 1px red solid; margin: 2px; } div img {width: 100px; visibility: hidden;} div.shown img { visibility: visible; }
So, any img
inside a div
is hidden, but we
can make it visible by adding the shown
class to
the div
.
Now it's time to understand the code.
Let's look at some of the functions, in turn. The first prepares the game board by hiding some images in randomly selected cells:
function prepare_game() { var cellsCount = create_grid(); // randomly select the number of total images var filesCount = fileNames.length; var imageLimit = Math.min(filesCount,cellsCount); var totalImages = Math.floor(Math.random()*imageLimit); document.querySelector("#total").innerHTML = totalImages; document.querySelector("#found").innerHTML = foundByUser; // randomly select the image indices var imageIdxArr = getArrayRandomNumbers(totalImages, filesCount); // randomly select the div indices to hold the images var divIdxArr = getArrayRandomNumbers(totalImages, cellsCount); setImages(imageIdxArr, divIdxArr); }
Which of the following is okay?
The create_grid
function creates all the cells (as many as
it can, depending on how big our window is), and tells us (by returning
a value) how many cells there are.
It then chooses a random number (within limits) for the number of
images to hide. This is stored in totalImages
.
Suppose that totalImages
is a number like 3. The last two
lines pick 3 images out of the set of 41 creepy images and pick 3 cells
out of the grid (however many there turn out to be), to put the 3 images
into.
Let's look at that next:
function setImages(imgArr, divArr) { console.log("inside setImages"); if( imgArr.length != divArr.length ) { console.log("something has gone wrong: array lengths don't match"); return; } for (var i=0; i < imgArr.length; i++) { var imgIdx = imgArr[i]; var divId = divArr[i]; setOneImage(imgIdx, divId); } }
This is pretty much our standard loop. All the interesting work is in
the setOneImage
function:
function setOneImage(imageIndex, divId) { console.log("image "+imageIndex+" will go in cell "+divId); var div = document.querySelector("#i_"+divId); var imgEl = document.createElement("img"); var filename = fileNames[imageIndex]; imgEl.setAttribute("src", "spooky/"+filename+".png"); imgEl.setAttribute("alt", filename); div.appendChild(imgEl); div.onclick = showImage; }
This function creates an image element, sets the src
depending on the imageIndex
and puts the image into the div
with the id given by divId
.
By having this as a separate function, we can test it by hand. Let's do that! Open up a console and reload the page a few times until you get a small number of images, so there are lots of empty cells. The JS console will tell you which cells are in use, so you can play a perfect game if you want. But suppose cells 0, 1 and 2 are empty. Do this:
setOneImage(30,0); setOneImage(30,1); setOneImage(30,2);
That puts the same image in all three cells. Now, click on those cells.
Now, let's look at what happens when you click on a cell:
function showImage() { // find div that was clicked to show in console var divId = this.getAttribute("id"); console.log("Found "+divId); // alert("Found you!"); this.classList.add("shown"); // makes the image visible foundByUser += 1; document.querySelector("#found").innerHTML = foundByUser; }
Here's where we use the CSS trick to make the hidden image visible.
How could we cheat and get the page to say "Found 10 out of 9"
document.querySelector("#found").innerHTML = "10 of 9";
document.querySelector("#total").innerHTML = "10 of 9";
document.querySelector("#total").innerHTML = 9; document.querySelector("#found").innerHTML = 10;
document.querySelector("#total").innerHTML = 10; document.querySelector("#found").innerHTML = 9;
We hope that after these activities you have a good understanding of: