Today let's talk about going further with JavaScript and jQuery.
Last time we worked on JavaScript we left off with a folder that looked like this:
Today we're going to add some new files and by the end of the day you should have a working folder that looks like this:
To get started, download the following multimedia files, and save or move them into the right place:
Next, let's make a new page that we can use to experiment with the new techniques for today. I've created a page that contains several paragraphs of lorem ipsum text with some images interspersed, which I hope will seem to you like a plausible page of text that you might be working with for your project.
Download this file and save it as article.html
,
overwriting the article.html
file you already have,
or, copy/paste the contents of this file into
your article.html
, deleting all the contents first:
If you double-click on article.html
right now, it
looks pretty basic in your browser:
Let's get started by adding some CSS to clean up that layout a
bit. First, link this file to our CSS file. Add the following to
the <link>
tag
inside <head>
:
<link rel="stylesheet" href="css/article-style.css">
Let's look in that CSS file. Not too much there yet. Let's start
by adding some rules for styling the images. At the end of the
file after the .header {}
rule, add
this:
.header { width: 100%; height: 100px; } img { width: 200px; border: solid 1px #000; margin: 20px; } img.left { float: left; } img.right { float: right; }Here I'm setting the width for all the images, adding a black border, and some margin spacing around them. I'm also adding two rules for images that I've classed as
left
and right
, floating them appropriately.
Now your page should look like this:
Since there was a request for it in class, 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 this really great article from The Intercept related to our class that you might enjoy: "White Supremacists, Conspiracy Theorists Are Targeting Cell Towers, Police Warn". 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 :hover
to 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:
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, like so: (add this inside
the <head>
tag)
<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script> <script src="js/article-interaction.js"></script>(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.)
Next, let's add our custom JavaScript file with our interaction rules. In Atom, create a new file and add the following boilerplate starting code for a jQuery file:
$(document).ready( function(){ });And save this file in your
js
folder
as article-interaction.js
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.
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.
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!!
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: