lesson 5
JS part 2
DOM
One of the primary roles of JavaScript in the browser is as a scripting interface to the Document Object Model (DOM). The DOM is an API for HTML documents, exposing a structural representation of the document, and enabling modification of its content and visual presentation.
data types
The API exposes several different objects and data types, each with their own set of properties and methods.
window
The window object represents the default parent view for a browser page, as well as being the browser's global JavaScript object. Here are some of the key properties and methods available:
        
location
window.location returns an object containing information about the URL of the document:
        
- window.location.href: entire URL
- window.location.pathname: path relative to the host
- window.location.hash: part of the URL after the # symbol
- window.location.search: part of the URL after a ? (query) symbol
    console.log(window.location.href); // "http://skole.apt10.kjapt.no/lesson5/"
    console.log(window.location.pathname); // "/lesson5/"
        navigator
window.navigator returns an object containing information about the browser:
        
- window.navigator.userAgent: user agent string
- window.navigator.plugins: array of installed browser plugins
    console.log(window.navigator.ua);
    //"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) ... Safari/534.55.3"
        history
window.history returns an object that enables manipulation of the browser's session history, including changing the URL without causing a page refresh:
        
- window.history.back(): go to previous page in session history
- window.history.forward(): go to next page in session history
- window.history.pushState(data, title, url): add location to session history stack
- window.history.replaceState(data, title, url): update current location entry
          it's possible to fallback to location.hash changes in order to simulate history session management
        
scroll properties
The following window properties store information regarding the window's scroll state:
        
- window.scrollY: number of pixels the document has scrolled vertically
- window.scrollX: number of pixels the document has scrolled horizontally
- window.scrollMaxY: maximum vertical scroll offset in pixels
- window.scrollMaxX: maximum horizontal scroll offset in pixels
- window.scrollTo(x, y): scroll to a particular coordinate in the document
timer methods
The following window methods can be used to execute functions after a specified delay:
        
- window.setTimeout(callback, delay): executes the callback after delay in milliseconds (returns unique id)
- window.setInterval(callback, interval): executes the callback each interval in milliseconds (returns unique id)
- window.clearTimeout(id): clears delay set by- window.setTimeout()
- window.clearInterval(id): clears interval set by- window.setInterval()
delays and intervals are approximate: the minimum delay/interval varies by browser (usually 4ms), with lesser values rounded up; execution may be delayed while the browser performs complex computations
window.requestAnimationFrame is a new API for efficiently executing interval code, allowing browsers to better control timing, including throttling requests for content in hidden tabs:
        
    // will be called approximately every 16ms (60HZ)
    function tick() {
      doSomething();
      doSomethingElse();
      // vendor prefixes required
      window.requestAnimationFrame(tick);
    }
    tick();
        
          it's possible to polyfill this behaviour with window.setTimeout() for browsers that don't yet support it
        
document
The document object serves as the entry point for a page's DOM tree, and contains a direct reference to some of the main page elements:
        
- document.documentElement: returns the- <html>element
- document.head: returns the- <head>element
- document.body: returns the- <body>element
element
element objects generally make up the bulk of a page's DOM tree, and include all the element types we are familiar with: div, span, p, ul, etc. An element inherits functionality for navigating/accessing connected nodes:
        
- childNodes: live collection of child nodes
- firstChild: first direct child node
- lastChild: last direct child node
- nextSibling: immediately following node
- previousSibling: immediately preceding node
- parentNode: parent node
- nodeType: number representing node type (1 for- element)
- nodeName: name of node (LI, P, etc)
- appendChild(node): insert a node as last child
- insertBefore(newNode, referenceNode): inserts the specified node (as a child) before a reference child
- removeChild(node): remove a child node
- replaceChild(node): replace a child node with another
- cloneNode(deep): clone a node, and (optionally) all of its contents
- haschildNodes(): check if any child nodes are present
elements also provide the ability to read and modify their content and visual state:
        
- attributes: returns all attributes
- id: get/set value of- idattribute
- className: get/set value of- classattribute
- style: get/set value of- styleattribute
- clientHeight/clientWidth: inner dimensions
- offsetHeight/offsetWidth: dimensions including padding and borders
- offsetTop/offsetLeft: offset from border to parent's border
- offsetParent: parent element from which all offset calculations are made
- scrollHeight/scrollWidth: total dimensions including overflow
- scrollTop/scrollLeft: scroll offset
- innerHTML: get/set HTML content
- textContent: get/set text content
- getAttribute(name): retrieve the value of named attribute
- setAttribute(name, value): set the value of named attribute
- hasAttribute(name): check if has named attribute
- removeAttribute(name): remove the named attribute
events
window, document, and all elements emit several events that can be listened for via JavaScript. By employing a registration API, it is easy to add and remove multiple event handlers to objects:
        
    element.addEventListener('click', clickHandler, false);
    function clickHandler(evt) {
      console.log('clicked');
      element.removeEventListener('click', clickHandler);
    }
        Naturally, oldIE did things a little differently, so a more cross-browser approach requires a little more work:
    if (element.addEventListener) {
      element.addEventListener('click', clickHandler, false);
    } else {
      element.attachEvent('onclick', clickHandler);
    }
    function clickHandler(evt) {
      console.log('clicked');
      if (element.removeEventListener) {
        element.removeEventListener('click', clickHandler);
      } else {
        element.detachEvent('onclick', clickHandler);
      }
    }
        using a library is recommended for robust event handling across platforms
bubbling
Because of the DOM's structure, events are able to "bubble" up the tree, ultimately arriving at window. In practice, this means that it is possible for a parent element to listen for events triggered on one of it's children. This event delegation can be more efficient by keeping the number of event registrations to a minimum:
        
    // parent is a menu of buttons
    parent.addEventListener('click', clickHandler, false);
    function clickHandler(evt) {
      console.log('clicked', evt.target); // clicked, button element
    }
        DOM ready
The document object emits a load event when a page is rendered after all images and assets have been downloaded. However, instead of waiting for all assets to load, it is often preferable to execute code as soon as the page is parsed and the DOM hierarchy has been fully constructed. Modern browsers emit a DOMContentLoaded event for this reason, and we can see the result in this inspector screen-shot (blue line is DOMContentLoaded, red is load):
        
 
        
          using a library is recommended in order to enable DOMContentLoaded behaviour in oldIE
        
working with elements
Most JavaScript in the browser involves the creation and manipulation of DOM elements.
        
selection
Before an element can be modified, it must first be retrieved from the DOM, and it turns out there are several ways to select an element:
        
by ID
Because ids are unique, selecting an element by id is the most efficient way of retrieval from the DOM:
        
    var element = document.getElementById('elementId');
        by tag name
Selecting all elements of the same tag type returns a collection of elements:
        
    var elements = document.getElementsByTagName('p');
        many element collections returned by DOM APIs are "live", and will be updated if matching elements are added or removed from the DOM
by class name
Selecting all elements with the same class name returns a collection of elements:
        
    var elements = document.getElementsByClassName('my-class');
    var childElements = element.getElementsByClassName('my-class');
        
          enabling oldIE compatibility requires first selecting all elements on the page (document.getElementsByTagName('*')), and is therefore quite inefficient
        
by CSS selector
Thanks to the popularity of jQuery (Sizzle selector engine), browsers added similar ability to select elements using CSS selector syntax. Both document and elements inherit querySelector() for selecting the first matching element, and querySelectorAll() for matching all elements:
        
    var element = document.querySelector('p.red');
    var elements = element.querySelectorAll('li>a');
        
          querySelectorAll() returns a non-live collection of elements
        
class attribute
Manipulating (adding/removing) class attributes in order to apply styles to an element is a very common task, made more difficult by that fact that element.className only returns a space delimited string of classes. Fortunately, it is made easier in modern browsers by the addition of element.classList, which returns a list of class tokens, and supports add(), remove(), toggle(), and contains() operations:
        
    element.classList.add('myClass');
    element.classList.toggle('myOtherClass');
    console.log(element.className); // 'myClass myOtherClass'
    element.classList.remove('myClass');
    console.log(element.className); // 'myOtherClass'
        style attribute
Directly manipulating an element's CSS style rules is another very common task, especially when animating an element's properties with JavaScript.
        
computed style
Although it's simple enough to read values from the element.style object, it will only include values for properties directly set on the element, and not those applied with CSS. In order to read all applied styles, we need to use window.getComputedStyle():
        
    #myElement {
      background-color: black;
    }
    var element = document.getElementById('myElement');
    element.style.color = '#ff0000';
    var style = window.getComputedStyle(element);
    var color = style.getPropertyValue('color');
    var bgColor = style.getPropertyValue('background-color');
    console.log(color, bgColor); // "rgba(255,0,0,1), rgba(0,0,0,1)"
        
          oldIE doesn't support window.getComputedStyle(), but the equivalent can be achieved with element.currentStyle
        
size and position
As outlined above, there are several dimension and placement properties available:
    function getPosition(el) {
      var top = el.offsetTop;
      var left = el.offsetLeft;
      if (el.offsetParent) {
        while (el = el.offsetParent) {
          top += el.offsetTop;
          left += el.offsetLeft;
        }
      }
      return {top: top, left: left};
    }
    // assume element's parent is offset 20px
    var element = document.getElementById('myElement');
    console.log(element.offsetWidth, element.offsetHeight); // 235, 20
    console.log(element.offsetTop, element.offsetLeft); // 0, 0
    console.log(getPosition(element)); //{top:0, left:20}
        viewport size
With regards to the dimension of the current page, there is only one property that is consistent across devices:
    var viewportWidth = document.documentElement.clientWidth);
    var viewportHeight = document.documentElement.clientHeight);
        manipulation
Manipulating the order and composition of the DOM tree is as easy as calling appendChild(), insertBefore(), removeChild(), etc on the document or element directly:
        
    var element = document.getElementById('myElement');
    var otherElement = document.getElementById('myOtherElement');
    otherElement.appendChild(element);
        
          appending an existing element elsewhere in the tree will automatically remove it from it's current location
        
creation
Creating a new element at runtime is fairly straightforward, and is most commonly achieved in one of three ways:
createElement
The document.createElement(tagName) method creates an element of the given type:
        
    var element = document.createElement('div');
        createDocumentFragment
The document.createDocumentFragment() method creates an empty parent DOM node in memory to which child elements may be attached. Because it is not part of the DOM tree, appending children to it won't cause unnecessary page reflows:
        
    var items = ['item1', 'item2', 'item3'];
    var ul = document.getElementsByTagName('ul')[0];
    var frag = document.createDocumentFragment();
    for (var i = 0, n = items.length; i < n; i++) {
      var li = document.createElement('li');
      li.textContent = items[i];
      frag.appendChild(li);
    }
    ul.appendChild(frag);
        innerHTML
Modifying element.innerHTML can also be used to generate new elements, though, because it must be formatted as a string, it can be rather cumbersome:
        
    var ul = document.getElementsByTagName('ul')[0];
    ul.innerHTML = "<li>item1</li><li>item2</li><li>item3</li>"
        
          be aware that inserting user generated content directly into innerHTML may enable the insertion of unsafe code via <script>
        
JSON
JSON (JavaScript Object Notation) is a data-interchange format that closely resembles JavaScript object notation. JSON is capable of representing numbers, booleans, strings, and null, as well as arrays and objects composed of these values.
        
oldIE doesn't support native JSON parsing, so include a library like json3 to enable support
JSON behaviour is encapsulated in the native JSON object, which contains two methods for converting to and from JSON-formatted data:
        
Parse()
The JSON.parse() method takes a JSON-formatted string and converts it to JavaScript values:
        
    var jsonString = '{"prop1":true, "prop2":["item1","item2","item3"]}'
    var obj = JSON.parse(jsonString);
    console.log(obj.prop2[1]); //"item2"
        Stringify()
The JSON.stringify() method takes JavaScript values and converts to a JSON-formatted string.
        
    var obj = {};
    obj.prop1 = true;
    obj.prop2 = ["item1","item2","item3"];
    var jsonString = JSON.stringify(obj);
    console.log(jsonString);
    //"{'prop1':true,'prop2':'["item1","item2","item3"]'}"
        toJSON
If an object being stringified has a method called toJSON(), then the return value will be used to represent the object in JSON notation:
        
    var obj = {};
    obj.prop1 = true;
    obj.toJSON = function() {
      return false;
    }
    var jsonString = JSON.stringify(obj);
    console.log(jsonString); //"false"
        AJAX
AJAX (Asynchronous JavaScript + XML) is a term that describes a technique for asynchronously (without forcing a page refresh) communicating with a remote server. Although the 'X' stands for XML, AJAX calls can send and receive data in a variety of other formats, including JSON, HTML, and plain text. Regardless of the format, the primary benefit of AJAX over other approaches is the ability to efficiently update parts of a page when needed based on user interaction.
XMLHttpRequest
All AJAX calls start with an instance of the XMLHttpRequest object and a url/path from which to retrieve data:
        
    var request = new XMLHttpRequest();
    // assign callback handler
    request.onreadystatechange = function() {
      if (request.readyState === 4) {
        if (request.status === 200) {
          console.log(request.responseText);
        }
      }
    }
    request.open('GET', 'path/to/some/data', true);
    request.send();
        JSON-P
Because of the potential for malicious code execution, certain types of data transfer via JavaScript is restricted by a same-origin policy. In practice, this means that retrieving data via AJAX from a different domain will be prevented.
One method around this restriction is through the use of JSON-P. More of a technique than a specification, JSON-P uses a dynamically injected <script> tag to call out to a remote data service location. The response handler is passed to this remote script, and subsequently called with the requested JSON data when the loaded JavaScript is executed.
        
because of a lack of specification and enforcement by the browser, JSON-P calls are inherently unsafe and hacky
CORS
An emerging solution to cross-domain data loading is CORS (Cross-Origin Resource Sharing). An extension to the XMLHttpRequest object, CORS allows browsers to make calls across domains by first asking the server for permission.
CORS requires a server configured to intercept and respond to the authorization request-headers