HTML 5 and Javascript training session


By Olivier Sallou - olivier.sallou@irisa.fr

Materials

Presentation and exercices are available at: https://github.com/osallou/html5-training

Content is available under Creative Common By 3.0

HTML

Important tips

CSS

CSS stylesheets

Move all display/style in CSS files

Rules can apply on an attribute id or class

Many useful selectors (x-before, x-after, attributes, ...)

sass helps to introduce variables and inheritance in CSS, but not interpreted natively, must be compiled to CSS

Debuggers

Firebug, Chrome Developper tools,...

All navigators integrate developper tools:

console.log("...") is your friend

Inspect

In the console you can inspect an object i.e. see its methods and attributes

xhr = new XMLHttpRequest(); inspect(xhr);

Javascript

Javascript

Pure Javascript - variables

Variables are visible within the scope of their declaration (function, global, loop...)

var test = 10;
test = "test";
// Array
test = new Array();
test[0] = 10;
// Object / JSON
test = {};
test["var1"] = 10;

Pure Javascript - functions


function add(a,b) {
 console.log("a="+a);
 return a+b;
}

Pure Javascript -variable scope


    var myglobalvar= "rr";

    function my() {
	var mylocalvar= 1;
        // myglobalvar is visible, but this boots performance for a large number of variables
        // myglobalvar is global, a_local_var is visible only inside the function
        var a_local_var = myglobalvar;

        $("#mydiv").click( function() {
             // mylocalvar is visibile in this scope. The my() function scope is copied in this function.
             var my_sub_var = mylocalvar;

        });

    }


Special case: the this element is not copied in sub functions, if needed, it must be first copied in a local scope variable.

Pure Javascript - Objects

objectname.prototype.propertyname = expression ;

  function MyObjet(name){ // constructor
    this.name=name;                             
  }

  MyObjet.prototype.minWidth = 500;

  MyObjet.prototype.setWidth = function(x) {
    if (x >= this.minWidth ) {
      this.width = x ;
      ...
      // We could resize a div with name
      // this.name for example
    }
  }

  var test = new MyObjet("sample");  // instantiate an object
  test.setWidth(300);

Inheritance

Javascript supports inheritance like in object programming

Based on previous example we can create a MySuperObject

See the call and apply methods to use parent methods.


function MySuperObject(name){
  MyObject.apply(this,arguments); // Call the super constructor with input arguments
}

// define MySuperObject as a MyObject
MySuperObject.prototype = new MyObject();
// Not needed if constructor has not arguments
MySuperObject.prototype.constructor = MySuperObject;

// Redefine a method from parent
MySuperObject.prototype.setWidth = function(x) { 
  x = x+1;
  // Here, we call the parent method on current instance
  MyObject.prototype.setWidth.call(this,x);
}
// Add new methods not in parent
MySuperObject.prototype.mynewfunction = function(x,y) { ... }


Pure Javascript - DOM manipulation

DOM in the browser / performance

The browser manages 2 structures:

The update of the dom will queue modifications of the graphical structure if needed. The browser will apply a set of updates from this queue.

Accessing some visual representation properties of an element will flush the queue and force a refresh, this can impact performance.

It may be preferable to create an empty HTML element (or clone one, or insert HTML string representation), apply modifications then (re)insert the element in the page rather than modifying the element directly to limit the graphical structure recalculations.

This will provider better user experience when inserting many rows in a table for example, but also reduce calculation time during which interface is blocked (remember, it is mono thread).

Hiding an object and modifying it does not impact the graphical structure

Events

Events are propagated all along the DOM from the element up to the body element.

It is possible to reverse the propagation.

To manage events, too main functions (e is my event):

Pure Javascript - Network


var xhr = null;
if (window.XMLHttpRequest || window.ActiveXObject) {
    if (window.ActiveXObject) {
        try {
            xhr = new ActiveXObject("Msxml2.XMLHTTP");
        } catch(e) {
            xhr = new ActiveXObject("Microsoft.XMLHTTP");
        }
    } else {
        xhr = new XMLHttpRequest(); 
    }
} else {
    alert("Votre navigateur ne supporte pas l'objet XMLHTTPRequest...");
    return;
}

// callback on answer
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
        // We could use JSON.parse to retreive JSON encoded data
        alert(xhr.responseText);
    }
};

xhr.open("GET", "handlingData.php?variable1=data1&variable2=data2", true);
xhr.send(null);

Coding style

Good writing rules are available and necessary just like any other language

JQuery

Why JQuery? (or Prototype etc..)

JQuery basic usage

Easy DOM access

Easy DOM manipulation

<input id="myspecificinput" type="text" value="8"/> <div id="myspecificdiv"> </div>


var myobj = $("#myspecificdiv");
// Add some HTML content in my div
myobj.append("<p>sometext</p>");
// Access attributes of my div
myobj.attr("data-sample","something");
var myattr = myobj.attr("data-other");
console.log(myattr);
// get/set the value of an input form
var myinput =  $("#myspecificinput");
myinput.val(10);
console.log(myinput.val());
    

Results in <input id="myspecificinput" type="text" value="9"/> <div data-sample="something" id="myspecificdiv"> <p> sometext<p> </div>

Easy network access


    $.getJSON(myurl, function(data) {
           // data is already JSON encoded..
           console.log(data['some_key_in_object']);
    });

Warning: URL should be in current domain or a server accepting cross-domain requests (see CORS).

Easy event handling


<button id="mybutton" type="button" class=".mybutton">hello</button>
<script>
$(function() {
  $( ".mybutton" ).click(function() {
    alert("one click");
});
});
</script>

For dynamically added elements, the events will not be triggered. One need to add a listener to the object when adding it or to specify that the event is to be listened to current and future DOM elements. To do so:


$(document).on("click",".mybutton",function() {
	alert("click");
});

Exercise 1

JQuery UI

JQuery-ui provides widgets based on JQuery for your web pages design.

JQuery mobile does the same for a mobile environment. However you need to take care to refresh the layout on dynamically added objects:


$("#dynamicform").append(formdata).trigger("create");

HTML 5

  • Offline/Online (cache)
  • Local storage (store when offline)
  • Exercise 2
  • Web sockets (push info)
  • Exercise 3
  • notifications (Desktop)/messages (communicate between windows)
  • Exercise 4
  • Audio/Video (show according to todo type)
  • Exercise 5
  • Canvas (basic drawing exo)
  • Exercise 6
  • Drag/Drop (info only)
  • Workers (info only)
  • CORS (call remote nodejs server)
  • Semantic tags (info only)
  • file system
  • Geo localisation (info only)
  • Devices
  • History stack

HTML5 support

  • Still in validation at W3C
  • Though many features are already available in all modern browsers...

Tips

  • Add fallback message if feature is not supported
  • Modernizr library detects for you feature support in user browser. Application can ask library is this or this feature is supported.

Cached application

  • It is possible to cache an entire application to work in offline mode
  • Define a file cache.manifest and add to the main page:
    <html manifest="cache.manifest">
    File contains the list of needed files:
    
                    CACHE MANIFEST
                    index.html
                    css/app.css
                    js/jquery-1.9.0.min.js
                    js/app.js
                
  • Application will then be loaded from local cache

To clear cache on chrome: chrome://appcache-internals/

Offline/Online detection

  • Browsers can detect if we are currently online or offline but...
    • Detection is browser dependant and does not check a remote server connectivity. It only detects local network availability.
  • Combined with cached application, it gives the possibility to detect status and use local storage in the meanwhile for example.

if (window.addEventListener) {
    window.addEventListener("online", isOnline, false);
    window.addEventListener("offline", isOffline, false);
}
else {
    document.body.ononline = isOnline;
    document.body.onoffline = isOffline;
}
function isOnline() { ... }
function isOffline() { ... }
        

Local storage

  • Local storage is a basic key/value store (values are strings)
  • Limited capacity, should not be used for large storage

localStorage.setItem('mykey','myvalue');
myvalue =  localStorage.getItem('mykey');
//Loop over all elements
for(var i=0, len=localStorage.length; i<len;i++) {
    var key = localStorage.key(i);
    var value = localStorage.getItem(key);
    ...
}
// Clear store
localStorage.clear();
    

Session storage

  • Like Local Storage (implements Storage API) but for sessions
  • Live during the window/frame duration
  • One different session storage per window/frame

sessionStorage["login"] = "myuser";
    

Exercise 2

Web sockets

  • Provides persistent HTTP connections to a server supporting web sockets
  • Bi-directional message communication
  • Used for Chat, push,...
  • Should encode data for transfer (base64, json stringify....)
  • Support binary transfer: ws.binaryType = "arraybuffer" or "blob"
  • Order is guaranteed

Web sockets


// connect to host:port on "route" /echo
var ws = new WebSocket("ws://localhost:9000/","echo");
ws.onmessage = function (evt)
    {
    var received_msg = evt.data;
    };
ws.onclose = function()
    {
    // websocket is closed.
    };
// Send a message
ws.send("a message");
        

To not initialize web sockets in JQuery.

Exercise 3

Notifications

  • This API is not implemented everywhere yet.
  • Allows Desktop notifications is user grants the application the right to do it.

if (window.webkitNotifications) {
    console.log("Notifications are supported!");
}
else {
    console.log("Notifications are not supported for this Browser/OS version yet.");
}
$("#mybutton").'click'(function() {
    if (window.webkitNotifications.checkPermission() == 0) { // 0 is PERMISSION_ALLOWED
        // Send a notification
        window.webkitNotifications.createNotification(
        'icon.png', 'Notification Title', 'Notification content...');
     } else {
        // Ask for permission
        window.webkitNotifications.requestPermission();
     }
}, false);
    

Messaging

  • Provides ways to communicate between 2 or more local navigator windows from same or different origin
  • Before communication, both pages must be loaded and initialized
In this example window A wants to send a message to window B, from same origin.
  • Window A, loaded from http://mydomain/input.html
    
    
    <script>
    ...
    var myPopup = window.open('http://mydomain/output.html','myWindow');
    myPopup.postMessage('message','http://mydomain/'); // I specify here the origin of Window B
    ...
    </script>
    

Messaging (next)

  • Window B loaded from http://mydomain/output.html
    
    <div id="consoleOutput"></div>
    <script>
    ...
    function handleMessage(e) {
          // check the origin of message for security
          if (e.origin == "http://mydomain") {
             var consoleOutput=document.getElementById("consoleOutput");
             // Display message
             consoleOutput.textContent+= e.origin + ": " + e.data + "\n";
          } else {
             // If the message is coming from an untrusted origin, just ignore it :)
           }
    }
    ...
    // Listen to incoming messages
    window.addEventListener("message", handleMessage, true);
    </script>
            

Exercise 4

Audio/Video

  • Embedded and customizable players for audio and video
  • Video codecs: h.264 (mp4) ou Theora (ogv)
  • Audio codecs: mp3, AAC (m4a) ou Vorbis (ogg)

 <video src="myvideo.ogg" controls>
    Your browser does not support the video element.
 </video>
<audio src="audio.ogg">
    Your browser does not support the audio element.
</audio>
    
  • Controls:
    • controls: shows default controls
    • autoplay: Starts playing
    • loop: repeat play
  • Multiple source possible if codec not supported by browser
  • Audio preload: none, auto, metadata. For large file allows buffering

Audio/Video (next)

  • You can control with javascript the players
  • Current status available: paused, ended, currentTime

<button onclick="document.getElementById('demo').play()">
    Play the Audio</button>
<button onclick="document.getElementById('demo').pause()">
    Pause the Audio</button>
<button onclick="document.getElementById('demo').volume+=0.1">
    Increase Volume</button>
<button onclick="document.getElementById('demo').volume-=0.1">
    Decrease Volume</button>

// Seek in media
var mediaElement = document.getElementById('demo');
mediaElement.seekable.start();  // Returns the starting time (in seconds)
mediaElement.seekable.end();    // Returns the ending time (in seconds)
mediaElement.currentTime = 122; // Seek to 122 seconds
// basic controls
mediaElement.play();
mediaElement.pause();

Exercise 5

Canvas

  • Canvas is a place to draw :-)
  • But it is not basic and awful drawings, you can create real games in a canvas
  • Some libraries: D3.js, RaphaelJS

// a place to draw
<canvas id="myCanvas" width="200" height="100"></canvas>
    

Canvas (next)

  • Possibility to use buffers to save/redraw a scene
  • you can't "erase" an object, you need to draw above or redraw the scene

var c=document.getElementById("myCanvas");
// Get the context
var ctx=c.getContext("2d");
ctx.fillStyle="#FF0000";
ctx.fillRect(0,0,150,75);
// Draw a line
ctx.moveTo(0,0);
ctx.lineTo(200,100);
// Apply the path to the canvas
ctx.stroke();
// Add some text
ctx.font="30px Arial";
ctx.fillText("Hello World",10,50);
// Create gradient
var grd=ctx.createLinearGradient(0,0,200,0);
grd.addColorStop(0,"red");
grd.addColorStop(1,"white");
// Fill with gradient
ctx.fillStyle=grd;
ctx.fillRect(10,10,150,80);
// Draw an image
var img=document.getElementById("my_img");
ctx.drawImage(img,10,10);

Canvas (next)

  • You can ask the browser to be triggered on a framerate for animations in a canvas
  • Browser will evaluate the best framerate
  • you request the browser to draw your animation at the next available opportunity, not at a predetermined interval

window.requestAnimFrame = (function(){
    return  window.requestAnimationFrame       ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame    ||
    function( callback ){
    // fallback, use Javascript timers
    window.setTimeout(callback, 1000 / 60);
    };
})();

(function animloop(){
    requestAnimFrame(animloop);
    // Now redraw our canvas according to your animation (character movement etc...)
    render();
})();

Exercise 6

WebGL

  • We won't describe WebGL here but basically, WebGL is a subset of OpenGL in your browser to use GPUs for 3D rendering.
  • Not available in all browsers
  • Have a look at http://www.chromeexperiments.com/webgl/

Drag and drop

  • Support for native drag and drop
  • Drag and drop HTML elements
  • Drag and drop files(images...)
  • Drag from Desktop (managing e.dataTransfer.files on drop target)

// Make something draggable
<div draggable="true"></div>

Drag and drop (next)


var dragSrcEl = null;

function handleDragStart(e) {
    // current element we drag
    dragSrcEl = this;
    // We want to copy the HTML content of the div
    e.dataTransfer.setData('text/html', this.innerHTML);
}

function handleDrop(e) {
    // this / e.target is current target element.
    if (e.stopPropagation) {
    e.stopPropagation();
    // stops the browser from redirecting.
    }
    // We put the transfered data in current drop object
    this.innerHTML = e.dataTransfer.getData('text/html');
    return false;
}

myobj = document.getElementById("mydraggable");
myobj.addEventListener('dragstart', handleDragStart, false);
myobj.addEventListener('dragend', handleDragEnd, false);
myotherobj = document.getElementById("mydropable"); 
myotherobj.addEventListener('drop', handleDrop, false);

Workers

  • Enable time consuming background tasks with real concurrency
  • Should be limited usage (cpu consuming on user desktop)
  • Support data transfer (clone or transfer)
  • Load external or inline script

Workers communication with main page is done with messages (postMessage)

Workers (next)


//Create worker from a javascript file
var worker = new Worker('doWork.js');

worker.addEventListener('message', function(e) {
    console.log('Worker said: ', e.data);
}, false);

worker.postMessage('start working please'); // Send data to our worker.


// In doWork.js
// Listen to messages
self.addEventListener('message', function(e) {
    // Do your stuff
    ...
    // Say it's over, or send results...
    self.postMessage(e.data);
    // Use self.close() to stop the worker
}, false);

CORS (Cross-Origin Resource Sharing)

  • By default, XMLHTTPRequest are limited to the same domain, preventing a remote access to an other side in the background
  • CORS are needed for GET/PUT/POST methods and json type (not plain text)
  • This is managed with a preflight request (see next slide).
  • CORS provides the possibility to communicate with cross-domains requests
    • To do so, server must add specific HTTP headers to OPTIONS message:
      
      Access-Control-Allow-Origin: http://myaccepteddomain
      //or accept request from anywhere
      Access-Control-Allow-Origin: *
      // Accept cookies?
      Access-Control-Allow-Credentials: true
                  
  • If headers are not present or not matching, request will fail.
  • JQuery ($.ajax, ...) will manage CORS, additional parameters are available.

CORS

Preflight is an additional request the XHR object makes to make sure it’s allowed to actually make the request with an OPTION request. By default, there’s no preflight.

Setting custom headers on XHR requests triggers a preflight request.

To add X-Requested-With header for preflight request


jQuery:
$.ajax({
     url: "http://your-url...",
     headers: {'X-Requested-With': 'XMLHttpRequest'}
}); 

CORS

Basic HTTP exchange


OPTIONS /somepath HTTP/1.1
Host: myurl.com
Access-Control-Request-Method: GET
Origin: http://myserver.com
Access-Control-Request-Headers: x-requested-with
Server responds to OPTIONS request (no content served in this case).

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: X-Requested-With

Client sends GET request as it has permission to do so:

GET /somepath HTTP/1.1
Host: myurl.com
x-requested-with: XMLHttpRequest
Server responds to GET request with content:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Length: 888

CORS

It is possible to avoid CORS issues with JSON-P

Requests for JSONP retrieve not JSON, but arbitrary JavaScript code. They are evaluated by the JavaScript interpreter, not parsed by a JSON parser. Native deployments of JSONP are subject to cross-site request forgery (CSRF or XSRF) attack

A JSONP payload might look like this:

functionCall({"Name": "Foo", "Id": 1234, "Rank": 7});

Semantic tags

  • HTML5 introduces new HTML elements with semantic information
  • We can find: header, footer, section, article, ...
  • The idea is to give more information to the page and ease the indexation by search engines.

Micro data

  • Add information to help machine parse the data:
    
    // Define what is a Person
    <div itemscope itemtype="http://data-vocabulary.org/Person">
    <img src="avatar.jpg" itemprop="photo">
    My name is <span itemprop="name">John Doe</span>
    </div>
            
    A "machine" will be able to understand we describe a Person and what are its properties to extract relevant information.

File system API

  • This API gives access to a sandbox restricted file system.
  • Requires user acknowledge
  • Request for a disk quota
  • Provides file and directory read/write access

function onInitFs(fs) {
  fs.root.getFile('log.txt', {}, function(fileEntry) {
         // Get a File object representing the file,
         // then use FileReader to read its contents.
         fileEntry.file(function(file) {
         var reader = new FileReader();

         reader.onloadend = function(e) {
         var txtArea = document.createElement('textarea');
         txtArea.value = this.result;
         document.body.appendChild(txtArea);
         };

         reader.readAsText(file);
         }, errorHandler);

  }, errorHandler);
}
window.requestFileSystem(window.TEMPORARY, 5*1024*1024 /*5MB*/, onInitFs, errorHandler);
     

Geolocation

  • More mobile oriented
  • Needs user acknowledgment
  • Provides location (GPS, IP, GSM,...) according to browser capabilities/network
  • Asynchronous (can take up some time)

function followUser(position) {
     console.log("latitude :" + position.coords.latitude);
     console.log("longitude :" + position.coords.longitude);
     console.log("altitude :" + position.coords.altitude);
     console.log("speed :" + position.coords.speed);
}

 // Watch user movements
var follower = navigator.geolocation.watchPosition(followUser);

// To stop watching: navigator.geolocation.clearWatch(follower);
 

Devices

  • Possibility to (mobile) device orientation and events
  • Follow orientation, acceleration and rotation data

// Listen for the deviceorientation event and handle DeviceOrientationEvent object
window.addEventListener('deviceorientation', devOrientHandler, false);

History stack

  • HTML5 provides many behind the ground operations/handling
  • but a Back in your browser will load the previous HTML page and quit the application
  • History lets you manipulate the browser history stack to navigate back and forth in your application with the browser back/forward buttons

// Check the length of the history stack
console.log(history.length);
// Send the user agent forward
console.log(history.forward());
// Send the user agent back
console.log(history.back());

// now update history stack
history.pushState(data_related_to_event, name_to_show_in_history, url_to_show);

// Keep me informed of history back actions
window.addEventListener('popstate', function(event) {
    console.log('popstate fired!');
});

Compile to Javascript....

CoffeeScript

  • Looks like Javascript, and supports embedded Javascript.

Dart

  • New language from Google, supported in Chromium
  • More structure/typed

Javascript generation

  • Use compilers to generate some Javascript from Javascript-like languages
  • Expects to provide better performances and better writing rules

CoffeeScript

See example at http://coffeescript.org/

Dart

  • example.dart

import 'dart:html'
void main() {
 query("#ID").text = 'Hello world';
}

Frameworks MVC like

AngularJS

BackboneJS

Knockout

...

Frameworks

  • Add structure to the client side using MVC like patterns (like Symfony, Rails, Pyramid, ...)
  • Templating for views
  • More client-side internal routing with REST server communications
  • Limit page reloads
  • Automatic data binding (refresh) between views and models
  • Each framework has its way to manage binding and DOM manipulation

AngularJS example


<div>
<label>Name:</label>
// Attach value of the following input to the model variable named "yourName" in two-way binding
// When the model change, value will be updated
// When input value is modified, model will be updated too
<input type="text" ng-model="yourName" placeholder="Enter a name here">
// Display the value of "yourName" from current scope
<h1>Hello {{yourName}}!</h1>
</div>
    

A different Javascript

asm - http://asmjs.org/spec/latest/

on desktop (TideSDK)

on mobile (Cordova)

...

TideSDK

  • Create HTML+Javascript application, but running on the Desktop
  • Embeds browser engine like WebKit in the application
  • Adds a few API for pure desktop operations (not sandboxed)
  • Package the app and install it on Desktop

Apache Cordova

This framework creates applications for Android and iOS with an HTML5 application

Asm

asm.js is a research project at Mozilla that aims to formally define the subset of JavaScript that compilers like Emscripten and Mandreel already generate (typed arrays as memory, etc.)

  • C/C++ => LLVM => Emscripten => JavaScript
  • LLVM: compiler and toolchain technologies for code optimization, generates bytecode
  • Emscripten: LLVM-to-JavaScript compiler
  • Specifications
  • more...

Code is just a subset of JavaScript

Testing

Unit tests

Several frameworks available but Jasmine have some success.

Jasmine is a behavior-driven development framework

Functional testing

Selenium helps you simulate clicks etc...

Load testing

JMeter is quite easy and powerful to create simple or complex HTTP scenarios.

Links

/

#