Session 12 - JavaScript, Part 5

Harvard Extension School  
Fall 2021

Course Web Site: https://cscie12.dce.harvard.edu/

Topics

  1. JavaScript and "Ajax"
  2. JS Data Structures - array and "object" (key/value pair)
  3. Ajax - Getting JSON from HTTP request
  4. Same Origin Policy and CORS
  5. Javascript Helping with Navigation

Session 12 - JavaScript, Part 5, slide1
JavaScript, slide2
JavaScript and "Ajax", slide3
A Collection of Technologies, slide4
What can you do with Ajax/XHR?, slide5
JS Data Structures - array and "object" (key/value pair), slide6
More JSON objects, slide7
Ajax - Getting JSON from HTTP request, slide8
Let's Try it Out!, slide9
MLB, slide10
MLB - XHR, slide11
MLB - fetch, slide12
MLB - getJSON with jQuery, slide13
National Park Service API, slide14
Same Origin Policy and CORS, slide15
Same Origin Policy, slide16
CORS - Cross Origin Resource Sharing, slide17
JavaScript to Submit a Form, slide18
Javascript Helping with Navigation, slide19
Various Forms of Navigation, slide20
Navigation , slide21
"You are here" with CSS, slide22
"You are here" with JavaScript, slide23
Pages from Parts and Clamshell Navigation, slide24

Presentation contains 24 slides

JavaScript

JavaScript and "Ajax"

ajax

XHR/Ajax flow:
web_schematic_ajax.png

  1. The browser makes an HTTP GET request for the search page
  2. The server responds with the HTML content.
    Note that we are not showing browser requests for other page resources such as image, CSS, or JavaScript files
  3. The user types in a search term and clicks the submit button (or presses "enter")
  4. A JavaScript event handler is triggered on the form submit, and JavaScript sends an HTTP request via the browser to the server. This HTTP request contains the search term typed in by the user as a parameter sent to the server
  5. A program on the server searches the course database for any courses that contain the search term.
  6. The program (via the web server) returns the matching courses in a data format to the browser (typically in JSON format)
  7. The browser receives the data, hands it off to the JavaScript, and the JavaScript formats the data into HTML and updates the search area on the page

What the browser does

What the server does

The server is simply responding to HTTP requests, and sending responses back to the browser. The "how" of looking up courses in a database based on a keyword search term is beyond the scope of this lesson -- we will demystify that part later in the course.

A Collection of Technologies

So we see Ajax is really a collection of technologies that are working together to send and receive data to and from the web server based on user actions, and to format and display this data to the user.

Some of the key technologies involved are:

What can you do with Ajax/XHR?

JS Data Structures - array and "object" (key/value pair)

JSON - JavaScript Object Notation

JSON array Example:


         ['Autumn', 'Winter', 'Spring', 'Summer']
       

JSON 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" : ['Navel Orange','Tangelo','Clementine','Valencia Orange']
         }
       

More about JSON

A data format widely used in Ajax is JSON -- this format is used to pass data from the web server to the browser, and it is in a format that is easily worked with in JavaScript.

We first need to take a look at the JSON format and how we can work with it, and then we'll see how this works together in Ajax.

The introduction to JSON from json.org is a great place to start:

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange language.

JSON is built on two structures:

These are universal data structures. Virtually all modern programming languages support them in one form or another. It makes sense that a data format that is interchangeable with programming languages also be based on these structures.

More JSON objects

In short, a JSON object is an unordered list of name/value pairs.

A JSON object example:

Note that the object (collection of name/value pairs) is enclosed in {curly braces}.
The name/value pair has a colon after the name (i.e. name: value), and the name/value pairs are separated by a comma.

So the object above represents the following:

NameValue
lastNameBacow
firstNameLawrence
emailpresident@harvard.edu

JSON Array

In short, a JSON array is an ordered sequence of values. Values are strings (in quotes), numbers, arrays, objects, or boolean (true/false) values.

A JSON array example

Strings, Objects, and Arrays can all be values!

In the examples above, we've shown the values as strings (e.g. "Spring" or "Bacow"). But values can also be arrays or objects. Below is a more complex JSON object that includes two name/value pairs whose values are an array of strings ("seasons") and the other an array of objects ("days"):

There are several online JSON viewers and validators you can use that are helpful for exploring, writing, or validating JSON structures:

Ajax - Getting JSON from HTTP request

XHR - XMLHttpRequestObject

Javascript "fetch"

fetch(url).then(response => response.json()).then(data => processData(data));

jQuery

jQuery makes this easier with the ".getJSON" method!

Let's Try it Out!

MLB

MLB Data from mlbapidata: https://statsapi.mlb.com/api/v1/teams?sportId=1

Here's a snippet of JSON:


{
  "id": 144,
  "name": "Atlanta Braves",
  "link": "/api/v1/teams/144",
  "season": 2021,
  "venue": {
    "id": 4705,
    "name": "Truist Park",
    "link": "/api/v1/venues/4705"
  },
  "springVenue": {
    "id": 5380,
    "link": "/api/v1/venues/5380"
  },
  "teamCode": "atl",
  "fileCode": "atl",
  "abbreviation": "ATL",
  "teamName": "Braves",
  "locationName": "Atlanta",
  "firstYearOfPlay": "1871",
  "league": {
    "id": 104,
    "name": "National League",
    "link": "/api/v1/league/104"
  },
  "division": {
    "id": 204,
    "name": "National League East",
    "link": "/api/v1/divisions/204"
  },
  "sport": {
    "id": 1,
    "link": "/api/v1/sports/1",
    "name": "Major League Baseball"
  },
  "shortName": "Atlanta",
  "franchiseName": "Atlanta",
  "clubName": "Braves",
  "springLeague": {
    "id": 115,
    "name": "Grapefruit League",
    "link": "/api/v1/league/115",
    "abbreviation": "GL"
  },
  "allStarStatus": "N",
  "active": true
},

MLB - XHR

/* mlb api;  sportId = 1 is major league */
let urlTeams = "https://statsapi.mlb.com/api/v1/teams?sportId=1",
  xhr = new XMLHttpRequest();

window.addEventListener("DOMContentLoaded", function () {
  console.log("dom ready");
  xhr.responseType = "json";
  xhr.open("GET", urlTeams);
  xhr.addEventListener("load", buildList);
  xhr.send();
});

function buildList() {
  let teams = xhr.response.teams;
  teams.sort((a, b) => (a.name > b.name ? 1 : -1));
  let teamlist = document.createElement("ul");
  for (const team of teams) {
    console.log(team);
    console.log(team.name);
    let myli = document.createElement("li");
    let myanchor = document.createElement("a");
    let mytext = document.createTextNode(team.name);
    let teamUrlPath = team.teamName.toLowerCase().replace(" ", "");
    let myteamhref = "https://mlb.com/" + teamUrlPath;
    myanchor.setAttribute("href", myteamhref);
    teamlist.appendChild(myli).appendChild(myanchor).appendChild(mytext);
  }
  document.getElementById("teamscontainer").appendChild(teamlist);
}

MLB - fetch

/* mlb api;  sportId = 1 is major league */
let urlTeams = "https://statsapi.mlb.com/api/v1/teams?sportId=1";

window.addEventListener("DOMContentLoaded", function () {
  fetch(urlTeams)
    .then((response) => response.json())
    .then((data) => buildList(data.teams));
});

MLB - getJSON with jQuery

$.getJSON

/* mlb api;  sportId = 1 is major league */
let urlTeams = "https://statsapi.mlb.com/api/v1/teams?sportId=1";

$(document).ready(function () {
  $.getJSON(urlTeams, buildList);
});

National Park Service API

nps

Approach:

Same Origin Policy and CORS

When working with XHR/Ajax, it is important to understand some basic concepts of web application security. Some concepts we'll briefly describe are:

These are important to understand since the browser restricts the XHR calls allowed.

Same Origin Policy

This is the simplest policy, and will cover all cases where the data you are requesting comes from the same hostname as the page making the request.

Under the same origin policy, a web browser will permit JavaScript contained in a web page to access data from the same origin -- or the same "hostname" of the URL.

So if your JavaScript and the data it is accessing all come from the same origin (hostname in the URL), the browser will permit this communication.

If the JavaScript and the data come from different origins (hostnames), then the browser will block this for security reasons.


For JavaScript to access data that comes from different origins, you'll need to make sure CORS headers are set correctly.

CORS - Cross Origin Resource Sharing

CORS is a common and preferred method for handling cross-origin requests.

Using CORS, it is possible for the browser and server to determine whether or not to allow a cross-origin request. This happens as part of the HTTP request. The server can specify access control rules, which a browser will then use to determine whether a cross-origin request is permitted.

We won't go into how make the server-side CORS compatible right now. From the front-end perspective, it is enough to understand that you are using a CORS-enabled service. You don't need to do anything to enable CORS from within your JavaScript -- it already is there and is used by the browser.

HTTP response header:
access-control-allow-origin: *

For example, the service from "cdn.rawgit.com" (e.g. apples.json) uses CORS, which is why the "Pick Your Own Apples" example worked fine, despite the different origins between the page the JSON.

MDN: Cross-Origin Resource Sharing

JavaScript to Submit a Form

window.addEventListener("DOMContentLoaded",function(){
    let form = document.getElementById('comment-form');
    form.addEventListener("submit",function(ev){
        ev.preventDefault();
        sendData(form);
    });
});

function sendData(form) {
    const xhr = new XMLHttpRequest();

    // Bind the FormData object and the form element
    const fd = new FormData( form );

    // Define what happens on successful data submission
    xhr.addEventListener( "load", function(ev) {
        console.log(ev.target.responseText);
        form.innerHTML = 'Thank you for your feedback.';
    } );

    // Define what happens in case of error
    xhr.addEventListener( "error", function( ev ) {
        console.log('Oops! Something went wrong.');
    } );

    // Set up our request
    xhr.open( form.getAttribute('method'), form.getAttribute('action') );

    // The data sent is what the user provided in the form
    xhr.send( fd );
}

Javascript Helping with Navigation

We can use JavaScript to make sure we have the right navigation for the page!

Web Navigation Systems

Don't Make Me Think: Navigation

Various Forms of Navigation

Harvard College

Navigation

Iroquois Constitution

<html>
  <head>
    <title>The Great Binding Law, Gayanashagowa (Constitution of the Iroquois Nations)</title>
    <link rel="stylesheet"  href="style.css" />
  </head>
  <body id="part1">
    <?php include("inc/header.php"); ?>
    <?php include("inc/nav.php"); ?>
    <main>
      <?php include("inc/content1.php"); ?>
    </main>
    <?php include("inc/footer.php"); ?>
  </body>
</html>

"You are here" with CSS

"You are Here" CSS Example in JSFiddle

you are here

<nav id="navigation">
  <ul id="mainnav">
    <li id="navpart1"><a href="1.shtml">The Great Binding Law, Gayanashagowa</a></li>
    <li id="navpart2"><a href="2.shtml">Rights, Duties and Qualifications of Lords</a></li>
    <li id="iamhere"><a href="3.shtml">Election of Pine Tree Chiefs</a></li>
    <li id="navpart4"><a href="4.shtml">Names, Duties and Rights of War Chiefs</a></li>
    <li id="navpart5"><a href="5.shtml">Clans and Consanguinity</a></li>
    <li id="navpart6"><a href="6.shtml">Official Symbolism</a></li>
    <li id="navpart7"><a href="7.shtml">Laws of Adoption</a></li>
    <li id="navpart8"><a href="8.shtml">Laws of Emigration</a></li>
    <li id="navpart9"><a href="9.shtml">Rights of Foreign Nations</a></li>
    <li id="navpart10"><a href="10.shtml">Rights and Powers of War</a></li>
    <li id="navpart11"><a href="11.shtml">Treason or Secession of a Nation</a></li>
    <li id="navpart12"><a href="12.shtml">Rights of the People of the Five Nations</a></li>
    <li id="navpart13"><a href="13.shtml">Religious Ceremonies Protected</a></li>
    <li id="navpart14"><a href="14.shtml">The Installation Song</a></li>
    <li id="navpart15"><a href="15.shtml">Protection of the House</a></li>
    <li id="navpart16"><a href="16.shtml">Funeral Addresses</a></li>
  </ul>
</nav>

And the CSS:

#navigation a {
  /* Rules for navigation items */
}
#navigation a:hover {
  /* Rules for navigation items */
}

/* Rules specific for "you are here" */
#navigation #iamhere a,
#navigation #iamhere a:hover {
  /* Rules for the "you are here" item */
}

"You are here" with JavaScript

View this technique in action.
Example in JSFiddle

  1. Each body is uniquely identified (id)
  2. Each navigation list item gets an id
  3. CSS rule for id="iamhere"
  4. JavaScript to set the "iamhere" id

Note the id of the body:

<html>
<head>
    <title>Election of Pine Tree Chiefs (Constitution of the Iroquois Nations)</title>
    <link rel="stylesheet"  href="style.css" />
  </head>
  <body id="part3">
    <?php include("inc/header.php"); ?>
    <?php include("inc/nav.php"); ?>
    <main>
      <?php include("inc/content3.php"); ?>
    <main>
    <?php include("inc/footer.php"); ?>
  </body>
</html>

Each navigation item has an id:

<nav id="navigation">
  <ul id="mainnav">
    <li id="navpart1"><a href="1.shtml">The Great Binding Law, Gayanashagowa</a></li>
    <li id="navpart2"><a href="2.shtml">Rights, Duties and Qualifications of Lords</a></li>
    <li id="navpart3"><a href="3.shtml">Election of Pine Tree Chiefs</a></li>
    <li id="navpart4"><a href="4.shtml">Names, Duties and Rights of War Chiefs</a></li>
    <li id="navpart5"><a href="5.shtml">Clans and Consanguinity</a></li>
    <li id="navpart6"><a href="6.shtml">Official Symbolism</a></li>
    <li id="navpart7"><a href="7.shtml">Laws of Adoption</a></li>
    <li id="navpart8"><a href="8.shtml">Laws of Emigration</a></li>
    <li id="navpart9"><a href="9.shtml">Rights of Foreign Nations</a></li>
    <li id="navpart10"><a href="10.shtml">Rights and Powers of War</a></li>
    <li id="navpart11"><a href="11.shtml">Treason or Secession of a Nation</a></li>
    <li id="navpart12"><a href="12.shtml">Rights of the People of the Five Nations</a></li>
    <li id="navpart13"><a href="13.shtml">Religious Ceremonies Protected</a></li>
    <li id="navpart14"><a href="14.shtml">The Installation Song</a></li>
    <li id="navpart15"><a href="15.shtml">Protection of the House</a></li>
    <li id="navpart16"><a href="16.shtml">Funeral Addresses</a></li>
  </ul>
</nav>

And the CSS:

#navigation a {
      /* Rules for navigation items */
    }
    #navigation a:hover {
      /* Rules for navigation items */
    }

    /* Rules specific for "you are here" */
    #navigation #iamhere a,
    #navigation #iamhere a:hover {
      /* Rules for the "you are here" item */
    }

And the JavaScript (using jQuery) that finds the correct navigation item and sets the id attribute value to "iamhere":

$(document).ready(function(){
      var mybodyid = $('body').attr('id');
      var mynavid = '#nav' + mybodyid;
      /* e.g. for 3.shtml:
          mybodyid is 'part3'
          mynavid  is '#navpart3'
       */
      $(mynavid).attr('id','iamhere');
    });

Pages from Parts and Clamshell Navigation

Here we take advantage of CSS selectors in JQuery, along with "parents" and "children" and "show" and "hide" methods.

clamshell

$(document).ready(function(){
      console.log("Ready!");
      var mybodyid = $('body').attr('id');
      var mynavid = '#nav_' + mybodyid;
      console.log("mybodyid is " + mybodyid);
      console.log("mynavid is " + mynavid);

      // Set iamhere id
      $(mynavid).attr('id','iamhere');

      // hide all nested lists
      $('#navigation ul ul').hide();

      // show parents of "iamhere"
      $('#iamhere').parents().show();

      // show children of "iamhere"
      $('#iamhere').children().show();
});

JSFiddle Example

Note for the JSFiddle example, we are using a 'div' to contain the 'id' instead of the 'body'.
Clamshell Navigation in JS Fiddle

Files