Session 09 - Javascript, Part 2
Harvard Extension School
Fall 2022
Course Web Site: https://cscie12.dce.harvard.edu/
Topics
- Javascript - What can you do?
- Ice Cream Options
- JS Data Structures - array and "object" (key/value pair)
- Loops and Iteration
- Working with Key/Value objects and Arrays
- Expanding and Collapsing Tree Structures
- Responsive Navigation
Presentation contains 17 slides
Javascript - What can you do?
Programmatic access to all aspects of your web page!
- Manipulate classes and style properties
- Manipulate content
- Validate user input
- Communicate with Web Server (XHR, Ajax)
Getting Started with JavaScript
- MDN JavaScript
- JS Reference (DevDocs)
- W3Schools JavaScript
- JavaScript for Web Designers, a short book available online through the Harvard Library.
- Jennifer Robbins, 2018. Learning Web Design: A Beginner's Guide to HTML, CSS, JavaScript, and Web Graphics, 5th ed. O'Reilly Media. 500 p. ISBN 978-1491960202
JavaScript Review
WAIT! for DOM to be loaded
document.addEventListener("DOMContentLoaded", function(){
console.log("DOM has been loaded!");
});
Some examples will show putting the script
before the body end tag </body>
instead of setting a DOMContentLoaded
event listener on the document
Ways of Accessing the DOM
querySelector and querySelectorAll
- Both take "CSS style" selectors.
- querySelector returns only the first match
- querySelectorAll returns a list of items (even if it is a list of zero or one -- it returns a list!).
getElementById
This returns a single element node that we can directly operate on.
Remember that id
values must be unique, so we only get a
single match!
Ice Cream Options
<form method="post" name="ice_cream" id="ice_cream" action="https://cs12.net/form/submit.php">
<fieldset>
<legend>Would you like ice cream? </legend>
<label>
<input type="radio" name="want" id="ic_yes" value="yes"/>
Yes </label>
<label>
<input type="radio" name="want" id="ic_no" value="no"/>
No </label> </fieldset>
<fieldset id="icecream_options">
<legend>Ice Cream Options </legend>
<fieldset>
<legend>Would you like a cup or a cone? </legend>
<label>
<input type="radio" id="container_cup" name="container" value="cup"/>
Cup </label>
<label>
<input type="radio" id="container_cone" name="container" value="cone"/>
Cone </label> </fieldset>
<fieldset>
<legend>What topping do you want? </legend>
<label>
<input type="checkbox" name="toppings" id="toppings_wc" value="whipcream"/>
Whipped cream </label>
<label>
<input type="checkbox" name="toppings" id="toppings_jimmies" value="jimmies"/>
Jimmies </label>
<label>
<input type="checkbox" name="toppings" id="toppings_sprinkles" value="sprinkles"/>
Sprinkles </label>
<label>
<input type="checkbox" name="toppings" id="toppings_nuts" value="nuts"/>
Nuts </label>
<label>
<input type="checkbox" name="toppings" id="toppings_cherry" value="cherry"/>
Cherry </label> </fieldset> </fieldset>
<button type="submit">Submit Order </button>
</form>
In style
element
(<style>
) within head
element:
body {
font-family: helvetica, sans-serif;
margin: 1rem 5%;
line-height: 1.5;
}
form fieldset {
margin-bottom: 1rem;
}
label {
display: block;
}
input[type=submit],
button[type=submit] {
display: block;
}
#icecream_options {
border-style: none;
}
.hidden {
display: none;
}
In
script
element within head
element (<script>
):
console.log("hello!");
document.addEventListener("DOMContentLoaded", function(){
/* do stuff here */
console.log("dom is loaded!");
/*
x hide options when page is loaded
x click events for to yes and no radio
x display or hide the options as appropriate
(hide for 'no', show for 'yes')
*/
let iceCreamOptions = document.querySelector("#icecream_options");
console.log(iceCreamOptions);
iceCreamOptions.classList.add("hidden");
/* get the "want" input radio */
let wantRadioInputs = document.querySelectorAll("form input[name=want]");
console.log(wantRadioInputs);
wantRadioInputs.forEach(
function(wantRadio){
wantRadio.addEventListener("click",function(){
console.log("want radio is clicked!");
let yesRadio = document.querySelector("#ic_yes");
console.log(yesRadio);
console.log(yesRadio.checked);
if ( yesRadio.checked == true ) {
iceCreamOptions.classList.remove("hidden");
} else {
iceCreamOptions.classList.add("hidden");
}
})
}
);
})
JS Data Structures - array and "object" (key/value pair)
array Example:
['Autumn', 'Winter', 'Spring', 'Summer']
object Examples:
Simple "name/value" pairs:
{
"lastName" : "Bacow",
"firstName" : "Lawrence",
"email" : "president@harvard.edu"
}
"name/value" pairs, with values being an array (list) of things:
{
"apples" : ['Macoun','Empire','Honey Crisp','Albemarle Pippin'],
"oranges" : ['Naval Orange','Tangelo','Clementine','Valencia Orange']
}
And even movies
{ "movies" : [
"Casino Royale",
"Quantum of Solace",
"Skyfall",
"Spectre",
"No Time to Die"
]
}
Loops and Iteration
let oranges = ['Naval Orange','Tangelo','Clementine','Valencia Orange'];
/* Iterate with for..of */
console.log("Iterate with: item of array");
for (const myorange of oranges) {
console.log(myorange);
}
console.log("DONE!");
/* Iterate with 'classic' for (i = 0 ; i < LENGTH ; i++) */
console.log("Iterate with classic for i = ; < ; i++");
for (let i = 0; i < oranges.length; i++ ) {
console.log(i);
console.log(oranges[i]);
}
console.log("DONE!");
/* Iterate with '.forEach()' */
console.log("Iterate with forEach()");
oranges.forEach(function(myorange){
console.log(myorange);
});
console.log("DONE!");
Javascript and DOM: Building Content
And do the same for the other three seasons to get:
Javascript and DOM: Building Content
<p>
<button id="makelist" type="submit">Build List of Seasons </button>
</p>
<div id="seasonslist1">
</div>
In
script
element within head
element (<script>
):
function makeSeasonsList() {
let ul_node = document.createElement("ul");
/* Autumn */
let li_node1 = document.createElement("li");
let li_text1 = document.createTextNode("Autumn");
li_node1.appendChild(li_text1);
/* Winter */
let li_node2 = document.createElement("li");
let li_text2 = document.createTextNode("Winter");
li_node2.appendChild(li_text2);
/* Spring */
let li_node3 = document.createElement("li");
let li_text3 = document.createTextNode("Spring");
li_node3.appendChild(li_text3);
/* Summer */
let li_node4 = document.createElement("li");
let li_text4 = document.createTextNode("Summer");
li_node4.appendChild(li_text4);
/* Append the list items to the ul */
ul_node.appendChild(li_node1);
ul_node.appendChild(li_node2);
ul_node.appendChild(li_node3);
ul_node.appendChild(li_node4);
/* Place on page */
let container = document.getElementById("seasonslist1");
container.appendChild(ul_node);
}
/* Wait for DOM to be loaded, then add the click listener
to the button */
document.addEventListener('DOMContentLoaded',function(){
document.getElementById('makelist').addEventListener('click', makeSeasonsList);
});
Javascript and DOM: Building Content
Using an array (a list).
let seasons = ['Spring', 'Summer', 'Autumn', 'Winter'];
Iterate through Array
<p>
<button id="makelist" type="submit">Build List of Seasons </button>
</p>
<div id="seasonslist2">
</div>
In head
element:
<script src="example3.js"> </script>
Contents of example3.js
function makeSeasonsList() {
ul_node = document.createElement("ul");
let seasons = ['Spring', 'Summer', 'Autumn', 'Winter'];
for (let i = 0 ; i < seasons.length ; i++ ) {
let mytext = i + " " + seasons[i];
let text_node = document.createTextNode(mytext);
let li_node = document.createElement("li");
li_node.appendChild(text_node);
ul_node.appendChild(li_node);
};
let container = document.getElementById("seasonslist2");
container.appendChild(ul_node);
}
/* Wait for DOM to be loaded, then add the click listener
to the button */
document.addEventListener('DOMContentLoaded',function(){
document.getElementById('makelist').addEventListener('click', makeSeasonsList);
});
Using forEach array method
<p>
<button id="makelist" type="submit">Build List of Seasons </button>
</p>
<div id="seasonslist3">
</div>
In head
element:
<script src="example4.js"> </script>
Contents of example4.js
function makeSeasonsList() {
ul_node = document.createElement("ul");
let seasons = ['Spring', 'Summer', 'Autumn', 'Winter'];
seasons.forEach(function(s){
let text_node = document.createTextNode(s);
let li_node = document.createElement("li");
li_node.appendChild(text_node);
ul_node.appendChild(li_node);
});
let container = document.getElementById("seasonslist3");
container.appendChild(ul_node);
}
/* Wait for DOM to be loaded, then add the click listener
to the button */
document.addEventListener('DOMContentLoaded',function(){
document.getElementById('makelist').addEventListener('click', makeSeasonsList);
});
DOM methods vs innerHTML
- With DOM methods, we are creating nodes
- With
innerHTML
we are getting are setting the HTML string values.
Working with Key/Value objects and Arrays
Data:
{
"apples" : ['Macoun','Empire','Honey Crisp','Albemarle Pippin'],
"oranges" : ['Naval Orange','Tangelo','Clementine','Valencia Orange']
}
<h1>List of Fruits from Data </h1>
<div id="fruits"><!-- list goes here -->
</div>
In
script
element within head
element (<script>
):
"use strict";
let fruits = {
"apples": ['Macoun', 'Empire', 'Honey Crisp', 'Albemarle Pippin'],
"oranges": ['Naval Orange', 'Tangelo', 'Clementine', 'Valencia Orange']
};
document.addEventListener('DOMContentLoaded',function(){
buildFruitList();
});
function buildFruitList(){
let fruitList = document.createElement('ul');
for (const f in fruits) {
console.log(f);
let li = document.createElement('li');
li.appendChild(document.createTextNode(f));
if (Array.isArray(fruits[f])) {
console.log(fruits[f]);
let itemList = document.createElement('ul');
for (const item of fruits[f]) {
let li = document.createElement('li');
li.appendChild(document.createTextNode(item));
itemList.appendChild(li);
}
li.appendChild(itemList);
}
fruitList.appendChild(li);
}
document.getElementById('fruits').appendChild(fruitList);
}
JavaScript:
"use strict";
let fruits = {
"apples": ['Macoun', 'Empire', 'Honey Crisp', 'Albemarle Pippin'],
"oranges": ['Naval Orange', 'Tangelo', 'Clementine', 'Valencia Orange']
};
document.addEventListener('DOMContentLoaded',function(){
buildFruitList();
});
function buildFruitList(){
let fruitList = document.createElement('ul');
for (const f in fruits) {
console.log(f);
let li = document.createElement('li');
li.appendChild(document.createTextNode(f));
if (Array.isArray(fruits[f])) {
console.log(fruits[f]);
let itemList = document.createElement('ul');
for (const item of fruits[f]) {
let li = document.createElement('li');
li.appendChild(document.createTextNode(item));
itemList.appendChild(li);
}
li.appendChild(itemList);
}
fruitList.appendChild(li);
}
document.getElementById('fruits').appendChild(fruitList);
}
Expanding and Collapsing Tree Structures
- American League
- AL Central
- Chicago White Sox
- Cleveland Indians
- Detroit Tigers
- Kansas City Royals
- Minnesota Twins
- AL East
- Baltimore Orioles
- Boston Red Sox
- New York Yankees
- Tampa Bay Rays
- Toronto Blue Jays
- AL West
- Houston Astros
- Los Angeles Angels
- Oakland Athletics
- Seattle Mariners
- Texas Rangers
- AL Central
- National League
- NL Central
- Chicago Cubs
- Cincinnati Reds
- Milwaukee Brewers
- Pittsburgh Pirates
- St. Louis Cardinals
- NL East
- Atlanta Braves
- Miami Marlins
- New York Mets
- Philadelphia Phillies
- Washington Nationals
- NL West
- Arizona Diamondbacks
- Colorado Rockies
- Los Angeles Dodgers
- San Diego Padres
- San Francisco Giants
- NL Central
Responsive Navigation
- Horizontal menu when wide screen
- Collapsed menu when small screenshot
- toggle of display of expanded menu
Responsive Navigation
- CSS to set initial state of navigation, for smaller widths and larger width screens.
- For small screen:
- set 'click' event that will toggle the menu state
- add or remove (i.e. 'toggle') a class that controls hidden or expanded.