Code as a Liberal Art, Spring 2025

Unit 3, Lesson 4 — Thursday, May 1

More interactive techniques with JavaScript

We did not have time to cover this lesson in class, so consider this a bonus lesson that is available to you for self-directed learning if you are interested. This lesson discusses some techniques for going further with JavaScript and jQuery.

No particular relevance to this image except that perhaps by the end of this lesson you'll feel like you've learned some JavaScript techniques you can use to make your pages explode with joyful psychedelia ;)

Table of contents

  1. Getting started with a new page
  2. The this keyword
  3. The infamous hamburger menu
  4. Emphasizing multimedia content

I. Getting started with a new page

Last time we worked on JavaScript we left off with a folder that looked like this:

Today let's continue with that same folder. We didn't have a chance to work with the article.html file, and today we'll work with that.

To get started, download the following zip file: unit3-lesson4-multimedia.zip

Double-click on that to unzip it, which should create a folder called unit3-lesson4-multimedia, which itself should contain two subfolders: images and video.

To get started, drag the image files themselves into the image folder from last week, and then drag the entire video folder into the folder from last week. So you should have something like this:

Let's get started. Open article.html in VS Code. Let's start by adding a little header. Modify the first <div> like so:

  <div class="header">
    <h1>An article</h1>
  </div>

Now let's add some "content". Hopefully you have made some progress on the textual material that you will be working with for your site, but I don't have that. So I will proceed by working with some placeholder text. This will be useful to you if your project work is a little bit ahead of your paper progress and you want to work on the site without having your own content yet.

Visit the canonical Lorem ipsum generator: lipsum.com. Scroll down a bit and let's generate 6 paragraphs: enter "6", select "paragraphs" and hit "Generate".

Inside the second <div>, add a <p> tag (which should autocomplete a closing </p> tag), and inside it, copy/paste the first paragraph. Repeat this for each paragraph.

The text is randomly generated, so yours will look a little different from mine, but here is a code snippet:

  <div class="header">
    <h1>An article</h1>
  </div>

  <div>
    <p>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit ...
    </p>

    <p>
      Curabitur at imperdiet magna. Vestibulum non imperdiet ...
    </p>

    <p>
      Duis luctus dui nec mauris pellentesque ultricies ...
    </p>
  </div>

Now let's add some multimedia in the form of images. Starting with the second paragraph, let's add an <img> tag, one for each image in our folder — i.e. 4 total. The order doesn't matter too much, but let's do:

(By the way, these are all images of a well-known data center named One Wilshire in Los Angeles, which is discussed in the optional text by Paul Dourish.)

  <div class="header">
    <h1>An article</h1>
  </div>

  <div>
    <p>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit ...
    </p>

    <p>
      <img src="images/one-wilshire-exterior.jpg" />
      Curabitur at imperdiet magna. Vestibulum non imperdiet ...
    </p>

    <p>
      <img src="images/one-wilshire-meet-me-room.jpg"/>
      Duis luctus dui nec mauris pellentesque ultricies ...
    </p>
  </div>

If you double-click on article.html at this point, it looks pretty basic in your browser:

Let's add some CSS for this. Make a new file in your css folder called article-style.css and add a link to that in article.html:

  <link rel="stylesheet" href="css/article-style.css">

Let's start by adding this to the CSS file:

body {
    width: 600px;
    margin: auto;
}

div {
/* uncomment this to see all box borders */
/* border: solid 1px red; */
}

.header {
    width: 100%;
    height: 100px;
    margin-top: 50px;
}
    

Nothing too special. Now let's add some styles for the images, making them a little smaller, and adding two classes, one to move some images to the left, and another to the right:

body {
    width: 600px;
    margin: auto;
}

div {
/* uncomment this to see all box borders */
/* border: solid 1px red; */
}

.header {
    width: 100%;
    height: 100px;
    margin-top: 50px;
}

img {
    width: 200px;
    border: solid 1px #000;
    margin: 20px;
}
img.left {
    float: left;
}
img.right {
    float: right;
}

Now let's go through article.html and apply these new left and right classes. I will alternate mine, starting with right:

  <img class="right" src="images/one-wilshire-exterior.jpg" />

Now your page should look like this:

Finally let's get to some JavaScript work for today ...

(jump back up to table of contents)

II. The this keyword

Last class I left off with an example that used this without explanation and I promised to go into that today.

this is a keyword in JavaScript and in many (if not most) other programming languages that refers in some sense to the context in which your code is running. In object-oriented programming, it refers to the object "within" which your code is currently running. It refers to a collection of state variables that you're able to access in the current code.

More practically speaking in our current context, in functional programming like JavaScript, when doing event handling, the keyword this refers to the HTML element that triggered the event that you are currently handling.

Let's look at an example that illustrates this ...

(jump back up to table of contents)

III. The infamous hamburger menu

This is a common request. Let's learn how to use CSS and JavaScript to add the infamous three bar "hamburger icon"-style navigation menu () to this page. It's really quite simple!

There are a few different ways you can approach this. One is to use a Unicode text character for the three bars icon. This is what we'll do here. I've adapted this from an example of this technique that I found at theintercept.com. If you're curious, you can right-click on their hamburger menu in the upper-left corner and click "inspect" to poke around and see how this is implemented.

To get started with this, let's first add the clickable icon in a navigation menu area. Add the following HTML code into article.html:

<body>

  <div class="main-navigation">
    <span class="hamburger-icon"></span>
    <nav>
      <a href="">Home</a>
      <a href="">About</a>
      <a href="">Contact</a>
    </nav>
  </div>

  <div class="header">
The .main-navigation <div> will extend across the width of the page, and be pinned to the top of the window and the <span> is where we'll put the hamburger icon. <nav> is rather like a <div> tag: it does not do much formatting on its own, but it has semantic significance, it tells the browser that this is navigation text, which might help text-to-speech readers or web crawlers or etc. To do anything with this we have to style it. Add the following into article-style.css:
.main-navigation {
    position: fixed;
    top: 0;
    width: 600px;
    height: 35px;
    background-color: #ff88ff;
    z-index: 10;
}
.hamburger-icon {
    display: block;
    font-size: 30px;
    line-height: 30px;
    margin: 0 0 0 10px;
}
.hamburger-icon:before {
    content: "\2261";
}
.main-navigation nav {
    background-color: #ffccff;
    width: 100px;
    margin-top: 5px;
    padding: 5px 0 5px 10px;
}
.main-navigation a {
    display: block;
    color: #ffffff;
    font-family: sans-serif;
    text-decoration: none;
    text-transform: uppercase;
    margin: 8px 0 8px 0;
    padding-left: 10px;
}
.main-navigation a:hover {
    text-decoration: underline;
}
You can put this anywhere that you like in the file, but I would recommend in between lines 9 and 11, before the .header rule.

There's a lot going on here. Most of it is basic styling stuff to make this look nice: setting layout things like widths and margins, background colors, etc. Probably the most new/interesting is position: fixed. This tells the browser to position this item in relation to the browser window, meaning that as the user scrolls, this item will not move. It is positioned in accordance with the top property, which I've set here to 0 meaning there are zero pixels between the top of this item and the top of the browser window.

Also interesting is .hamburger-icon:before. The colon is used to indicate what is called a pseudo-class. It is not a class specified by you in HTML, but rather an implicit class that usually refers to the state of the element. For example, you could use :hoverto control display properties when the user hovers over an element. In this case, this style will insert content before the element(s) being targeted by .hamburger-icon. (You can read more here.) The "\2261" indicates a special character, in this case the Unicode character code for .

One other thing worth noting: the .main-navigation a selector is a new technique. This targets all <a> tags that are inside any elements with the .main-navigation class. In this case it will target all the hyperlinks inside the <nav> element, and it mainly is just specifying layout and display properties.

Your page should now have this element at the top, and it should stay fixed there no matter if you scroll the page down:

If you want to get a little fancier, try modifying .main-navigation by deleting or commenting out the background-color rule, and below it add the following:

    background-image: linear-gradient(to bottom right, #ff88ff, #880088);
Now your page should look like this:
(You can read more about this technique here.)

Now that we have the clickable menu icon, and the actual menu itself, let's hide it and only show it when the user clicks the hamburger. First, to hide the menu, add the following property to the .main-navigation nav rule:

    display: none;
This will hide the menu by default. Now let's see how to display it interactively using JavaScript.

First, to get started with jQuery on this page, we have to include the jQuery library just like we did last class. Then also include a new .js file that we will be working on together that I will call article-interaction.js:

<head>
  <title>Adding interaction</title>
  <link rel="stylesheet" href="css/article-style.css" >
  <script src="https://code.jquery.com/jquery-3.7.1.slim.min.js" integrity="sha256-kmHvs0B+OpCW5GVHUNjv9rOmY0IvSIRcf7zGUDTDQM8=" crossorigin="anonymous" ></script>
  <script src="js/article-interaction.js"></script>
</head>
(Make sure to copy/paste that whole thing. The integrity attribute assures that the file being included is the unaltered file from jquery.com, and has not been intercepted and modified by a hacker. This is important and I neglected to mention it last class.)

Next, let's add our custom JavaScript file with our interaction rules. In VS Code, create a new file called article-interaction.js inside your js folder and add the following boilerplate starting code for a jQuery file:

$(document).ready( function(){


});

Now to add the actual interactive code. There are many ways you can do this. Probably the simplest is the following:

    $(".hamburger-icon").click(function(){
    	$(".main-navigation nav").toggle();
    });
This is saying: target any elements with the hamburger-icon class, and for each one, add a click event handler. This way, whenever the user clicks on the icon it will trigger this event handler, which then will target any <nav> elements inside a .main-navigation element, and for each of those, toggle it's visibility. Thus, when the user clicks, if the <nav> is hidden, it will be made visible, and if the user clicks and it is already visible, it will be hidden.

We could do the same thing but with fancier effects like sliding or fading, simply by changing that middle line:

    $(".hamburger-icon").click(function(){
        $(".main-navigation nav").slideToggle();
    });
or
    $(".hamburger-icon").click(function(){
        $(".main-navigation nav").fadeToggle();
    });
Each of those can also take a numeric argument which would specify the amount of time in milliseconds for the effect to last. For example:
    $(".hamburger-icon").click(function(){
        $(".main-navigation nav").fadeToggle(200);
    });

Now what if instead of the user clicking you wanted them just to hover the mouse over. You could implement that like this:

    $(".hamburger-icon").mouseenter(function(){
     	$(".main-navigation nav").show();
    });
The selectors and the event handler are the same, but the event being handled is now mouseenter, which means when the mouse moves over the element without needing to click.

This gets a little trickier because we can't use the toggle functions, so instead we have to implement a second event handler for when the mouse moves away from the element:

    $(".main-navigation nav").mouseleave(function(){
    	$(".main-navigation nav").hide();
    });
      
Note that now the selector is indicating that the mouseleave event is being handled when the mouse moves away from the actual menu itself. This seems to me to make the most sense as a user interface behavior, but you could experiment with different methods yourself.

IV. Emphasizing multimedia content

Let's move on to some techniques for highlighting or emphasizing multimedia content that you'd like the viewer to focus on, based on where they're at in the article in terms of their scrolling. What we'll do is gray out all the images, and then give them color to indicate focus or attention. Start by graying out all the images with the following rule:

img {
    width: 200px;
    border: solid 1px #000;
    margin: 20px;
    filter: grayscale(1);
}

Now let's add some JavaScript to handle scrolling events, and try to use some math to figure out which elements are in the middle of the window, which we'll consider "active." Probably easiest for me to include a chunk of code here and then explain:

    $(window).on("scroll", function() {
	var halfwayPoint = window.innerHeight / 2;
	
	$("img").each(function() {	
	    var imgY = $(this).get(0).getBoundingClientRect().y;
	    if ( Math.abs(imgY - halfwayPoint) < 50 ) {
		$(this).css("filter","none");
	    } else {
		$(this).css("filter","grayscale(1)");
	    }
        });
    });					 
What this is doing first of all is adding an event handler that is triggered whenever the window (specified in jQuery as $(window)) is scrolled. Now inside this event handler, first the halfway point of the window as measured in pixels is calculated: window.innerHeight / 2 and that number is saved for later use as halfwayPoint.

Next, for every <img> tag on the page ($("img").each(...) the jQuery function .each() loops over every element that matches that selector and applies the following code to it. Inside that code, $(this) refers to the "current" <img> element that matched. This is almost like a for loop that iterates over a list, and each time through, sets some variable to the next item in the list.

So then $(this).get(0).getBoundingClientRect().y accesses the current y position in pixels of the <img> being looked at. imgY - halfwayPoint is the difference between the <img>'s y position as it is scrolling and the middle point of the window. This value could be positive or negative (if the image is above or below the halfway point) so we take Math.abs() to always get a positive value. Now, if that distance is less than 50 (which could be above or below) then we turn off the filter .css("filter","none"), otherwise we apply grayscale with .css("filter","grayscale(1)"

Try this out and see what you get!

We can get a bit fancier. Have a look at this:

    $(window).on("scroll", function() {
        var halfwayPoint = window.innerHeight / 2;
    
	$("img").each(function() {	
	    var imgY = $(this).get(0).getBoundingClientRect().y;
	    var distanceFromCenter = Math.abs(imgY - halfwayPoint);
	    if ( distanceFromCenter < 50 ) {
		var fadeFactor = distanceFromCenter/50;
		$(this).css("filter","grayscale("+fadeFactor+")");
	    } else {
		$(this).css("filter","grayscale(1)");
	    }
	});
    });
Here I'm calculating this variable fadeFactor simply by dividing the distance by 50. The maximum value for that distance is 50, and the minimum value is 0, so dividing by 50 is going to give me a value between 0 and 1. It turns out that is precisely the range of values that the CSS grayscale filter takes, so we can pass that value in to that. Now as an image approaches the middle of the window, it should have a quick fade to color effect.

Triggering multimedia plays

As a last technique, let's look at how we can connect scrolling to triggering the play of a multimedia object like audio or video material.

First let's add a video object to the page. I'll replace the second image, named one-wilshire-meet-me-room.jpg with a >video> tag:

      <video class="left">
	<source src="video/antenna.mp4" type="video/mp4">
	  Your browser does not support the video element.
      </video>
We could specify this as autoplay, using that attribute in the <video> tag itself, but let's leave that off and triffer it by scrolling. We do that by adding another scroll event handler, with some changes:
    $(window).on("scroll", function() {
	var halfwayPoint = window.innerHeight / 2;

	$("video").each(function() {
	    var videoY = $(this).get(0).getBoundingClientRect().y;
	    var distanceFromCenter = Math.abs(videoY - halfwayPoint);
	    if ( distanceFromCenter < 50 ) {
		$(this).trigger("play");
	    }
	});
    });
This time we're calling .each() on all the video elements. And for each one we are again getting the y position in pixels of the video element: $(this).get(0).getBoundingClientRect().y;, and we are again calculating the distance between this element and the middle of the window. But this time, if that distance is less than 50, we're triggering the video to play: $(this).trigger("play").

Try this and see what happens. If you're using Chrome it won't work. Why? Open up the console and try again. You might need to reload. The error message I got included a URL point to this help page. That explained that Google Chrome now disables autoplay unless the user first interacts somehow with the window, and apparently just scrolling does not count as an interaction. They do however allow autoplay if the video is muted. So let's add the muted attribute to our <video> tag:

      <video class="left" muted>
Now I believe it should work!!

Concluding

We have been working on three files during the course of this lesson. I hope you take the time to work through the instructions and discussion above step-by-step to try to code these files yourself. If you would like to see how the resulting three files should probably look after doing all these exercises, you can find them all here:

At the end of this lesson you shouldhave a working folder that looks like this: