User-defined Functions


We will initially complete some of the activities from the previous lecture

Goal

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.

The Sound Playing Example

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:

  • The page initially has four paragraphs, each with a word, an icon and an audio element.
  • The function onDocumentReady sets up the page, adding an onclick event handler to each icon
  • The event handler looks to the siblings of its parent for information and abilities.

Alternative playSound

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);
}
  

Quiz Question Nr. 1

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?

  1. The code would be fine
  2. The code would break because this would have the wrong value
  3. The code would break because playSound would not get invoked.
  4. The code would break because the siblings of this would be different elements.

Let's consider alternative ways to do a page like this

Quiz Question Nr. 2

Is it possible to make the words clickable instead of the icons?

  1. yes, if we can get the right selector to get a list of the span elements
  2. yes, if we can get event handler for them
  3. no, only the image icons can be clickable
  4. no, because it needs to be next to the audio element.

Quiz Question Nr. 3

Do we need four audio elements?

  1. yes, we need a different one for each word
  2. yes, because the audio element needs to be in the same paragraph as the word
  3. no, because we're setting the src to a URL that depends on the word
  4. no, but we do need more than one audio element.

Happy Halloween

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.

Halloween Code

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);
}

Quiz Question Nr. 4

Which of the following is okay?

  1. swapping lines 5 and 6
  2. swapping lines 6 and 7
  3. swapping lines 7 and 8
  4. swapping lines 8 and 9

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.

Quiz Question Nr. 5

How could we cheat and get the page to say "Found 10 out of 9"

  1.     document.querySelector("#found").innerHTML = "10 of 9";
          
  2.     document.querySelector("#total").innerHTML = "10 of 9";
          
  3.     document.querySelector("#total").innerHTML = 9;
        document.querySelector("#found").innerHTML = 10;
          
  4.     document.querySelector("#total").innerHTML = 10;
        document.querySelector("#found").innerHTML = 9;
          

Summary

We hope that after these activities you have a good understanding of:

  • connecting the JavaScript ideas and techniques to the making interactive web pages.
  • how to use the JavaScript console to test pieces of code
  • how to use your knowledge of JavaScript to cheat at games