By Olivier Sallou - olivier.sallou@irisa.fr
Presentation and exercices are available at: https://github.com/osallou/html5-training
Content is available under Creative Common By 3.0
HTML markups must follow strict HTML
<div id="my" data-my="example"></div>
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
All navigators integrate developper tools:
In the console you can inspect an object i.e. see its methods and attributes
xhr = new XMLHttpRequest(); inspect(xhr);
var test = 10;
test = "test";
// Array
test = new Array();
test[0] = 10;
// Object / JSON
test = {};
test["var1"] = 10;
function add(a,b) {
console.log("a="+a);
return a+b;
}
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
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);
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) { ... }
div1=document.getElementById("div1");
p1=document.createElement("p") ;
// Set attribute
p1.data-something = "test";
// Set "content"
p1.innerHTML = "some text";
div1.appendChild(p1);
// Add an event (click) listener on "button" element
document.getElementById("button").addEventListener("click", function(event){
alert("button clicked");
// Stop event propagation
event.stopPropagation();
// e.cancelBubble = true; for some IE
}, true);
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 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):
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);
Good writing rules are available and necessary just like any other language
$(document).ready(function() {
// do your stuff
});
//OR
$(function() {
// do some stuff
});
<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>
$.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).
<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");
});
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 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
To clear cache on chrome: chrome://appcache-internals/
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() { ... }
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();
sessionStorage["login"] = "myuser";
// 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.
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);
<script>
...
var myPopup = window.open('http://mydomain/output.html','myWindow');
myPopup.postMessage('message','http://mydomain/'); // I specify here the origin of Window B
...
</script>
<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>
<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>
<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();
// a place to draw
<canvas id="myCanvas" width="200" height="100"></canvas>
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);
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();
})();
// Make something draggable
<div draggable="true"></div>
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 communication with main page is done with messages (postMessage)
//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);
Access-Control-Allow-Origin: http://myaccepteddomain
//or accept request from anywhere
Access-Control-Allow-Origin: *
// Accept cookies?
Access-Control-Allow-Credentials: true
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'}
});
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
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});
// 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.
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);
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);
// Listen for the deviceorientation event and handle DeviceOrientationEvent object
window.addEventListener('deviceorientation', devOrientHandler, false);
// 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!');
});
See example at http://coffeescript.org/
import 'dart:html'
void main() {
query("#ID").text = 'Hello world';
}
<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>
This framework creates applications for Android and iOS with an HTML5 application
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.)
Code is just a subset of JavaScript
Several frameworks available but Jasmine have some success.
Jasmine is a behavior-driven development framework
Selenium helps you simulate clicks etc...
JMeter is quite easy and powerful to create simple or complex HTTP scenarios.
/
#