Session 09 - Javascript, Part 2
Harvard Extension School
Fall 2021
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
- Responsive Navigation
Presentation contains 18 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
JavaScript Review
Ice Cream Options
<h3>Ice Cream </h3>
<form method="post" name="ice_cream" id="ice_cream" action="https://cs12.net/form/submit.php">
<div>Would you like ice cream?
<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>
</div>
<fieldset id="icecream_options">
<legend>Ice Cream Options </legend>
<p>Cup or a Cone?
</p>
<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>
<p>Pick your toppings:
</p>
<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>
<p>
<input type="submit"/>
</p>
</form>
In style
element
(<style>
) within head
element:
body {font-family: Calibri,Tahoma,Verdana,Helvetica,sans-serif;}
label { display: block;}
input[type=submit] {display: block; margin-top: 1rem;}
#icecream_options {
display: none;
background-color: rgb(238, 238, 238);
margin-left: 1em;
}
In
script
element within head
element (<script>
):
"use strict";
console.log("hello from ice_cream.js");
let inputYes,
inputNo,
icoEl;
window.addEventListener('DOMContentLoaded', function(){
/* here's where JS goes for when DOM is loaded */
/* add click listener to yes and no */
inputYes = document.getElementById('ic_yes');
inputYes.addEventListener('click', function() {
iceCreamOptionsDisplay();
} );
inputNo = document.getElementById('ic_no');
inputNo.addEventListener('click', function() {
iceCreamOptionsDisplay();
});
});
function iceCreamOptionsDisplay() {
/* function that tests whether 'Yes' or 'No'
has been selected and sets ice cream option display as needed */
console.log("in function iceCreamOptionsDisplay");
icoEl = document.getElementById('icecream_options');
console.log(icoEl);
inputYes = document.getElementById('ic_yes');
if (inputYes.checked == true) {
icoEl.style.display = 'block';
} else {
icoEl.style.display = 'none';
}
}
Let's Improve on the Ice Cream Example
Ice Cream Options - select and add click listener improvements
- Markup and CSS are the same.
- JS is a bit different in how we select the input radio buttons to add the click handler. We get to use
querySelectorAll()
method that let's us select elements using "CSS selector" syntax! Yay! This is often easier than selecting with other DOM methods. - JS function of
iceCreamOptionsDisplay
remains the same!
Starting Point
/* add click listener to yes and no */
inputYes = document.getElementById('ic_yes');
inputYes.addEventListener('click', iceCreamOptionsDisplay );
inputNo = document.getElementById('ic_no');
inputNo.addEventListener('click', iceCreamOptionsDisplay );
Using querySelectorAll and iterate through the list
/* use querySelectorAll and then loop
through the list using "for (item in list) { }"
*/
inputsWant = document.querySelectorAll("form input[name=want]");
console.log(inputsWant);
for (let input of inputsWant) {
console.log(input);
input.addEventListener("click", iceCreamOptionsDisplay);
}
<h3>Ice Cream </h3>
<form method="post" name="ice_cream" id="ice_cream" action="https://cs12.net/form/submit.php">
<div>Would you like ice cream?
<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>
</div>
<fieldset id="icecream_options">
<legend>Ice Cream Options </legend>
<p>Cup or a Cone?
</p>
<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>
<p>Pick your toppings:
</p>
<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>
<p>
<input type="submit"/>
</p>
</form>
In style
element
(<style>
) within head
element:
body {font-family: Calibri,Tahoma,Verdana,Helvetica,sans-serif;}
label { display: block;}
input[type=submit] {display: block; margin-top: 1rem;}
#icecream_options {
display: none;
background-color: rgb(238, 238, 238);
margin-left: 1em;
}
In
script
element within head
element (<script>
):
"use strict";
let inputsWant;
console.log("hello from ice_cream.js");
window.addEventListener("DOMContentLoaded", function () {
/* here's where JS goes for when DOM is loaded */
/* add click listener to yes and no input choices
use "querySelectorAll", which lets us use CSS selectors!
querySelectorAll returns a list of things that we can
loop through via 'for ( item of list ) { }'
*/
inputsWant = document.querySelectorAll("form input[name=want]");
console.log(inputsWant);
for (let input of inputsWant) {
console.log(input);
input.addEventListener("click", iceCreamOptionsDisplay);
}
});
function iceCreamOptionsDisplay() {
let icoEl, inputYes;
console.log("in iceCreamOptionsDisplay");
icoEl = document.getElementById("icecream_options");
console.log(icoEl);
inputYes = document.getElementById("ic_yes");
if (inputYes.checked == true) {
icoEl.style.display = "block";
} else {
icoEl.style.display = "none";
}
}
Ice Cream Options - use JS to hide options
- Don't use CSS to hide the options, but rather do this through JS.
Select icecream_options using JS and set display to none.
document.getElementById('icecream_options3').style.display = 'none';
<h3>Ice Cream </h3>
<form method="post" name="ice_cream" id="ice_cream" action="https://cs12.net/form/submit.php">
<div>Would you like ice cream?
<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>
</div>
<fieldset id="icecream_options3">
<legend>Ice Cream Options </legend>
<p>Cup or a Cone?
</p>
<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>
<p>Pick your toppings:
</p>
<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>
<p>
<input type="submit"/>
</p>
</form>
In style
element
(<style>
) within head
element:
body {font-family: Calibri,Tahoma,Verdana,Helvetica,sans-serif;}
label { display: block;}
input[type=submit] {display: block; margin-top: 1rem;}
#icecream_options3 {
background-color: rgb(238, 238, 238);
margin-left: 1em;
}
In
script
element within head
element (<script>
):
"use strict";
let inputsWant;
console.log("hello from ice_cream.js");
window.addEventListener("DOMContentLoaded", function () {
/* here's where JS goes for when DOM is loaded */
/* select ice cream options and hide */
document.getElementById('icecream_options3').style.display = 'none';
/* add click listener to yes and no input choices
use "querySelectorAll", which lets us use CSS selectors!
querySelectorAll returns a list of things that we can
loop through via 'for ( item of list ) { }'
*/
inputsWant = document.querySelectorAll("form input[name=want]");
console.log(inputsWant);
for (let input of inputsWant) {
console.log(input);
input.addEventListener("click", iceCreamOptionsDisplay);
}
});
function iceCreamOptionsDisplay() {
let icoEl, inputYes;
console.log("in iceCreamOptionsDisplay");
icoEl = document.getElementById("icecream_options3");
console.log(icoEl);
inputYes = document.getElementById("ic_yes");
if (inputYes.checked == true) {
icoEl.style.display = "block";
} else {
icoEl.style.display = "none";
}
}
Ice Cream Options - use classes
- Use classes to hide and show. The methods
element.classList
and.add
and.remove
and.toggle
are key here! - See: MDN: element.classList
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']
}
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 */
window.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="example5.js"> </script>
Contents of example5.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 */
window.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="example6.js"> </script>
Contents of example6.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 */
window.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']
};
window.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']
};
window.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);
}
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 small screen:
- set 'click' event for menu toggle
- add or remove (i.e. 'toggle') a class that controls hidden or expanded.