TI1506: Client-side JavaScript

Claudia Hauff

Course overview [Web]

  1. http: the language of Web communication
  2. HTML: the language of the Web
  3. JavaScript: interactions in the browser
  4. node.js: JavaScript on the server
  5. CSS: the language of Web design
  6. Ajax: asynchronous JavaScript
  7. Personalization: cookies & sessions
  8. Securing your application

At the end of this lecture you should be able to

  • Employ OO principles in JavaScript coding
  • Explain the principle of callbacks
  • Write interactive Web applications based on click, mouse and keystroke events
  • Translate jQuery-based code into jQuery-less code
  • Use timeouts

Warning

  • Densest lecture of this course (JavaScript has many facets)
  • I'll be showing some code, but you have to sit down and code for yourself - otherwise you will not learn it!
  • Take your time understanding the different concepts, be friendly with
  • We start early, so you have a few weeks to digest the concepts
  • On Friday: JavaScript on the server (node.js)

Chapter 4 of the course book

Covers JavaScript basics ...

  • How to include JavaScript in your Web application
  • Essential JavaScript built-in types
  • How to use JavaScript control structures (if, for, while)
  • How to declare variables and functions
  • The purpose of console.log()
  • How to work with arrays
  • How to use basic jQuery features

A bit of context

JavaScript's reputation

  • Until fairly recently it was considered more of a toy language
  • Today: a very important language of the modern Web stack
    • Tooling has vastly improved (debuggers, testing frameworks, etc.)
    • JavaScript runtime engines are efficient (V8)
    • Hardware has improved enough to allow JavaScript-based gaming
    • Firefox OS (and related initiatives)

What is "very important"?


Read more about this graph at Redmonk.

Scripting overview

Requesting & processing a Web page in 4 steps

  1. Browser sends a GET request for a Web page to a Web server
  2. Web server executes server-side scripts (PHP, ASP, node.js, etc.)
  3. Web server sends (generated) HTML file to browser
  4. Browser executes scripts (JavaScript, etc.) & renders the page

JavaScript makes Web apps interactive and responsive to user actions.

Server-side scripting

  • Source code is private, result of script execution is returned (in HTML), not the script itself
  • HTML can be rendered by any browser
  • Server-side scripts can access additional resources (including databases)
  • Server-side scripts can use non-standard language features (you know your server's software!)

Client-side scripting

  • Source code is visible to everyone ("View Page Source")
  • Script execution by the browser reduces load on the Web server
  • All raw data necessary (e.g. for visualizations) need to be downloaded and processed by the client
  • JavaScript is event-driven: code blocks executed in response to user actions (click, hover, move, etc.)

JavaScript is

  • Still mostly a client-side technology (this is changing right now)
  • Very different from the programming language Java
  • Not equally well supported in all browsers (similar to CSS3, HTML5)
  • Used to make the Web experience more interactive
    • Alter page appearance based on events
    • Alter interaction based on browser type, language or cookie usage
    • Calculations are performed on the client:
      x

Disclaimer: JavaScript can do much more than we cover in class.

Be inspired by the Chrome Experiments.

JavaScript vs. OO languages

JavaScriptOO [Java]
interpreted (JIT)compiled
program is readible (more or less)byte code
fewer data types--
variables don't have to be declaredvariables need to be declared
silent errors (WebConsole/Firebug are your friends!)Errors and Exceptions
focus on functionsfocus on classes & objects
most often coupled with CSS & HTML--

The script tag

We place the <script> tags in the body element: the browser displays the page in a top-down fashion, creating the DOM elements as it comes across them.
By placing the <script>tags at the end, the JavaScript files will be one of the last things the page loads.
Because JavaScript files often take time to load, we do that last so that the user will get visual feedback from the other elements as fast as possible. (Semmy Purewal)

The script tag

  • <script> placement is an often discussed issue:
    • inside HTML's <head> and </head>, vs.
    • just before the </body> tag
  • Important for you to understand:
    • Browsers render in a top-down fashion
    • Interactivity based on the DOM should only start after the DOM has been fully loaded
      • Purpose of $(document).ready(my_function)
    • Demo: Effects of <script> placement

Keeping things apart

  • Ideally: separation of presentation, content and interaction
    • stylesheets/
    • javascript/
    • images/
    • index.html
  • Initially more work, but it pays off in the long term
    • Debugging
    • Extensibility
    • Code readability

JavaScript design patterns

The material in this section is based on the JavaScript Design Patterns book.

Chapter 4 of the course book

  • Focuses on what JavaScript to place where
  • Shows how to reduce duplicity in code on a function level
  • Does not emphasize OO principles

OO for JavaScript

  • Small programs often don't need it
  • JavaScript has functions as first-class citizens (not classes as in Java)
  • Large projects benefit from OO
  • OO groups together related data and behavior
  • Built-in objects: Strings, arrays, HTML/DOM nodes
  • Objects can be created in different ways (do not get confused, stick to one way)

Design patterns

Design patterns are reusable solutions to commonly occurring problems in software design (Addy Osmani)
  • We take a very practical approach
  • Many design patterns exist, we focus on three (the most important ones for our use case)
  • Design patterns develop over time
  • Design patterns often hold across different programming languages

Objects in JavaScript

  • new Object() produces an empty Object, ready to receive name/value pairs
    • Name: any string
    • Value: anything (String, array, Number, etc.) apart from undefined
  • Member are accessed through
    • .name (dot notation)
    • [name] (bracket notation)

var note1 = new Object();
note1["type"] = 1;
note1["note"] = "Math homework due";
console.log(note1["type"]);   /* prints out: 1 */
console.log(note1.note);      /* prints out: "Math homework due" */

Another way: Object literals


var note2 = {
   type: 2,
   message: "Math homework due" /* no commata at the last entry */
};

Adding a method


var note1 = new Object();
note1["type"] = 1;
note1["note"] = "Math homework due";
note1["toString"] = function(){
                      /* this refers to the current object */
                      return "Note: "+this.note+", type: "+this.type;
                    };

var note2 = {
   type: 2,
   message: "Math homework due",
   toString:  function() {
                /* this refers to the current object */
                return "Note: "+this.note+", type: "+this.type;
              } 
 };

Object literals can be complex


var paramModule = { 
  /* parameter object */
  Param : {
    minType : 1,
    maxType : 5,
    maxNoteLen : 100,
  },

  getParams : function() {
                var s = "Here all parameters should be listed ... ";
                return s;
  }
};

/* Using it: */
/* How do you call minType and getParams() ? */

Object literals can be complex


var paramModule = { 
  /* parameter object */
  Param : {
    minType : 1,
    maxType : 5,
    maxNoteLen : 100,
  },

  getParams : function() {
                var s = "minType: "+this.Param.minType+"\n";
                s += "maxType: "+this.Param.maxType+"\n";
                s += "maxNoteLen: "+this.Param.maxNoteLen+"\n";
                return s;
  }
};

/* Using it: */
notesModule.Param.minType; /* 1 */
notesModule.getParams();

Is this enough?


var note = {
   type: 1,
   message: "Math homework due",
   toString: function() {return "Note: "+this.note+", type: "+this.type;}
 };

Is this enough?


var note = {
   type: 1,
   message: "Math homework due",
   toString: function() {return "Note: "+this.note+", type: "+this.type;}
 };
  • What happens if we need 1000 objects of this kind?
  • What happens in a large project if a method needs to be added to all note objects?

Design Pattern (1):
Basic constructor

Recall: constructors in Java

 
Task: 2 minutes to create a Java class Note with 
        - private members note (String) and type (int);
        - a constructor that takes a String and int as input (the note and the type)
        - a method setType(int t) that sets the type to t
        - a method getType that returns the current type         
         
Note note1 = new Note("Maths homework assignment", 1);
note1.setType(2);

Recall: constructors in Java

         
public class Note {
  private String note;/* encapsulate private members */
  private int type;

  /* constructor: a special method to initialize a new object */
  public Note(String n, int t) {
    this.note = n;/* this: reference to the current object */
    this.type = t;
  }
  public void setType(int t) {this.type = t;}

  public int getType() {return this.type;}

  public String getNote() {return this.note;}

  public String toString() {
    return "Note: "+this.note+", type: "+this.type;
  }
}          
         
Note note1 = new Note("Maths homework assignment", 1);
note1.setType(2);
note1.toString();

Basic constructor

In JavaScript functions are first-class citizens.

         
function Note( note, type ) {
  this.note = note;/* this: reference to the current object */
  this.type = type;

  this.setType = function(t) {this.type = t;};
  
  this.getType = function() {return this.type;};

  this.getNote = function() {return this.note;};

  this.toString = function () {
    return "Note: "+this.note+", type: "+this.type;
  };
}      
         
var note1 = new Note("Maths homework assignment", 1);
note1.setType(2);
note1.toString();
var note2 = new Note("English homework due"); /* what happens to type? */
var note3 = Note("Music homework due", 3); /* what now? */

Basic constructor

  • An object constructor is just a normal function
  • What does JavaScript do with new?
    • new anonymous empty object is created and used as this
    • returns new object at the end of the function

Basic constructor


/* Remember that JavaScript is loosely typed */
var note1 = new Note("Maths homework", "IMPORTANT");
note1.type; /* "IMPORTANT" */
var note2 = new Note("English homework", 1);
note2.type; /* 1 */
note2.dueDate = "1-1-2015"; /* new members added on the fly */
note1.toString(); /* "Note: Maths homework, type: IMPORTANT" */
note1.toString = function(){return this.type;};
note1.toString(); /* "IMPORTANT" */

/* We also have a few additional methods available */
note1.hasOwnProperty("dueDate");/* false */
note1.hasOwnProperty("type");/* true */
  • New variables and methods can be added on the fly
  • Objects come with default methods (prototype chaining)

Summary: basic constructor

  • Advantage: very easy to use
  • Issues:
    • Not obvious how to use inheritance here (e.g. NoteWithDueDate)
    • Objects do not share functions
      • function toString() is not shared between note1 and note2
    • All members are public
      • Any piece of code can access/change/delete(!) members type and note

Design Pattern (2):
Prototype-based constructor

Prototype chaining explained

  • Objects have a secret pointer to another object - the object's prototype
    • Properties of the constructor's prototype are also accessible in the new object
    • If a member is not defined in the object, the prototype chain is followed until the member is found

Prototype-based constructor


function Note( note, type ) {
  this.note = note;/* this: reference to the current object */
  this.type = type;
}    

/* member methods are defined once in the prototype */
Note.prototype.setType = function(t) {this.type = t;};
Note.prototype.getType = function() {return this.type;};
Note.prototype.getNote = function() {return this.note;};
Note.prototype.toString = function () {
    return "Note: "+this.note+", type: "+this.type;
};
 
// Using it:
var note1 = new Note("Maths homework due", "IMPORTANT");
var note2 = new Note("English homework", 2);
note1.getType(); /* "IMPORTANT" */
note2.getNote(); /* "Maths homework due" */

Prototype-based constructor

Prototype changes are also reflected in existing objects!


function Note( note, type ) {
  this.note = note;/* this: reference to the current object */
  this.type = type;
}    

/* member method setType() defined */
Note.prototype.setType = function(t) {this.type = t;};
 
var note1 = new Note("Maths homework due", "IMPORTANT");
note1.setType(2); /* OK */
note1.getType(); /* TypeError: note1.getType isn't a function */

/* lets define the method */
Note.prototype.getType = function() {return this.type;}

note1.getType(); /* 2 */

Prototype-based constructor

Inheritance through prototyping


function Note( note, type ) {
  this.note = note;
  this.type = type;
}    

Note.prototype.setType = function(t) {this.type = t;};
Note.prototype.getType = function() {return this.type;};
Note.prototype.getNote = function() {return this.note;};
Note.prototype.toString = function () {
    return "Note: "+this.note+", type: "+this.type;
};

/* constructor */
function NoteWithDeadline(note,type,dueDate) {
  this.note = note;
  this.type = type;
  this.dueDate = dueDate;
};

/* redirect the prototype */
NoteWithDeadline.prototype = new Note();
/* redirect the constructor */
NoteWithDeadline.prototype.constructor = NoteWithDeadline;

/* using it */
var nw = new NoteWithDeadline("Maths homework",1,"1-1-2015");
nw.setType(2); /* that works, setType(t) defined in Note */

Summary: prototype-based constructor

  • Advantages:
    • Inheritance is easy to achieve
    • Objects share functions
  • Issue:
    • All members are public
      • Any piece of code can access/change/delete(!) members type and note

Design Pattern (3): Module

JavaScript scoping

  • All JavaScript code enters the same namespace
  • JavaScript has limited scoping
    • var in function: local, limited scope
    • var outside of a function: global scope
    • no var: global scope (holds for function names too)

JavaScript scoping

  • All JavaScript code enters the same namespace
  • JavaScript has limited scoping
    • var in function: local, limited scope
    • var outside of a function: global scope
    • no var: global scope (holds for function names too)
    
    var note1 = new Note("Maths", 1);/* global */
    var note2 = new Note("English", 3); /* global */
    
    function calcMinType(n1,n2) { /* global */
      var t1 = Number(n1.type); /* local */
      t2 = Number(n2.type);     /* global */
      return Math.min(t1,t2);
    }
    
    t1; /* ReferenceError: t1 is not defined */
    t2; /* ReferenceError: t2 is not defined */
    
    calcMinType(note1,note2);
    
    t1; /* ReferenceError: t1 is not defined */
    t2; /* 3 */
    
    
    
  • What if another JavaScript library used in the project defines note1 or calcMinType(a,b,c) as well?

Module

  • Goals:
    • Do not declare any global variables or functions unless required
    • Emulate private/public membership
    • Expose only the necessary members to the public (as API)
  • Results:
    • Potential conflicts with other JavaScript libraries are reduced
    • Public API minimizes unintentional side-effects when wrongly used

Module


var notesModule = (function () {
 
  /* 'private' members */
  var noteCounter = 0;
  ...
 
  /* 'public' members; return accessible object */
  return {
    incrNoteCounter : function () {
      noteCounter++;
    },
    ...
  };
})();/* function is called */

notesModule.incrNoteCounter(); /* OK */
notesModule.noteCounter; /* undefined */

Module


var notesModule = (function () {
 
  /* 'private' members */
  var noteCounter = 0;

  var logCounter = function() {
    console.log("notesModule counter: "+noteCounter);
  };
 
  /* 'public' members; return accessible object */
  return {
    incrNoteCounter : function () {
      noteCounter++;
    },
 
    decrNoteCounter : function () {
      if(noteCounter>0) {
        noteCounter--;
      }
    },

    getNoteCounter : function() {
      logCounter();
      return noteCounter;
    }
  };
})();/* function is called */

notesModule.incrNoteCounter(); /* OK */
notesModule.noteCounter; /* undefined */
notesModule.getNoteCounter(); /* 1 */

Module

The encapsulating function can also contain arguments


var notesModule = (function (startingCount) {
 
  /* 'private' members */
  var noteCounter = startingCount;

  var logCounter = function() {
    console.log("notesModule counter: "+noteCounter);
  };
 
  /* 'public' members; return accessible object */
  return {
    incrNoteCounter : function () {
      noteCounter++;
    },
 
    decrNoteCounter : function () {
      if(noteCounter>0) {
        noteCounter--;
      }
    },

    getNoteCounter : function() {
      logCounter();
      return noteCounter;
    }
  };
})(5);/* function is called */

Summary: Module

  • Advantages:
    • Encapsulation is achieved
    • Object members are either public or private
  • Issues:
    • 'Public' and 'private' members are accessed differently
    • Changing the type of membership (public/private) costs time
    • Methods added on the fly later on cannot access 'private' members

Events & the DOM

A look at book chapter 4


var main = function () {
  "use strict";
  $(".comment-input button").on("click", function (event) {
    var $new_comment = $("<p>"),
    comment_text = $(".comment-input input").val();
    $new_comment.text(comment_text);
    $(".comments").append($new_comment);
  });
};
$(document).ready(main);
  • Uses jQuery extensively (a big time saver)
  • But: it is important to understand what jQuery "covers up"
  • Decide for yourself whether you want to use jQuery in the assignments (other JavaScript libraries are not allowed)

A look at book chapter 4


/* jQuery's way of accessing DOM elements */
$(".comment-input button").on("click", function (event) {
  ...
});
  • With jQuery: no matter if class or id or ..., the access pattern is the same, i.e. $()
  • Callback principle: we define what should happen when an event fires

Step by step: making a responsive UI control

  1. Pick a control (e.g. a button)
  2. Pick an event (e.g. a click on the button)
  3. Write a JavaScript function: what should happen when the event occurs? (e.g. a popup appears)
  4. Attach the function to the event ON the control

Document Object Model: how to "find" elements in a page

DOM: what can we do

  • Extract an element's state
    • Is the checkbox checked?
    • Is the button disabled?
    • Is a <h1> appearing on the page?
  • Change an element's state
    • Check a checkbox
    • Disable a button
    • Create an <h1> element on a page if none exists
  • Change an element's style (later lecture)
    • Change the color of a button
    • Change the size of a paragraph
    • Change the background color of the page

Selecting groups of DOM elements

document (i.e. the Web page object) contains a number of methods:

  • document.getElementById
  • document.getElementsByTagName
  • document.getElementsByName
  • document.querySelector
  • document.querySelectorAll

document.getElementById

window is the browser window object


/* JavaScript file, imported with <script> */

/* we attach a function to a button's click event 
 * after the DOM finished loading (i.e. all HTML elements are accessible)
 */
window.onload = function() {
  document.getElementById("myButton").onclick = sayHello;
};

/* we define the function */
function sayHello() {
  var tb = document.getElementById("out");
  tb.value = 'Hello World';
}; 

document.getElementById

What happens if the id is not unique?


/* JavaScript file, imported with <script> */
window.onload = function() {
  document.getElementById("myButton").onclick = sayHello;
};

function sayHello() {
  var tb = document.getElementById("out");
  tb.value = 'Hello World';
};


<input type="text" id="out"> <input type="text" id="out">

Not just forms ...


/* JavaScript file, imported with <script> */
window.onload = function() {
  document.getElementById("myButton").onclick = sayHello;
};

function sayHello() {
  var span1 = document.getElementById("span1");
  var span2 = document.getElementById("span2");
  var tmp = span1.innerHTML;
  span1.innerHTML = span2.innerHTML;
  span2.innerHTML = tmp;
};
   Hello    World

DOM object properties



<div id="main" class="main_class">
   <p>Hello <em>World!</em></p>
   <img id="worldImage" src="images/1.jpg" />
</div>

//JavaScript
var m = document.getElementById("main");
var w = document.getElementById("worldImage");
PropertyExample
tagNamem.tagName is div
classNamem.className is main_class
innerHTMLm.innerHTML is \n

Hello....

srcw.src is images/1.jpg

DOM object properties (forms)



<input id="firstName" type="text" />
<input id="agreed" type="checkbox checked="checked" /> Did you read the terms and conditions?

//JavaScript
var f = document.getElementById("firstName");
var a = document.getElementById("agreed");
PropertyExample
value
(text in an input control)
f.value may be "Tom"
checked
(checkbox)
a.checked is true
disabled
(whether a control is disabled)
a.disabled is false
readOnly
(read-only text box)
f.readOnly is false

Creating new nodes

HTML tags and content can be added dynamically in two steps:

  1. Create a DOM node
  2. New node is added to the page as child of an existing node

Inserting nodes into the DOM tree

Every DOM element object has these methods

name description
appendChild(node) places given node at end of this node's child list
insertBefore(new, old) places the given new node in this node's child list just before old child
removeChild(node) removes given node from this node's child list
replaceChild(new, old) replaces given child with new node

Inserting nodes into the DOM tree

  • A list element

/* JavaScript */
window.onload = function() {
  document.getElementById("myButton").onclick = addElement;
};

function addElement() {
  var ul = document.getElementById('ul_domtree');
  var il = document.createElement('li');
  il.innerHTML = 'List element ' + (ul.childElementCount+1) +' ';
  ul.appendChild(il);
};

Removing nodes from the DOM tree

  • List element 1
  • List element 2
  • List element 3
  • List element 4
  • List element 5
  • List element 6
  • List element 7

/* JavaScript */
window.onload = function() {
  document.getElementById("myButton1").onclick = removeLastChild;
  document.getElementById("myButton2").onclick = removeFirstChild;
};

function removeLastChild() {
  var ul = document.getElementById('ul_domtree2');
  ul.removeChild(ul.lastChild);
};

function removeFirstChild() {
  var ul = document.getElementById('ul_domtree2');
  ul.removeChild(ul.firstChild);  
}

this: the current object

  • Event handlers are bound to the attached element's objects
  • Handler function "knows" which element it is listening to (this)
  • Simplifies programming, one function can serve different objects

/* one function per button (highly redundant) */
window.onload = function() {
  document.getElementById("button10").onclick = computeTimes10;
  document.getElementById("button23").onclick = computeTimes23;
  document.getElementById("button76").onclick = computeTimes76;
}

/* would not work: event handlers cannot have parameters */
window.onload = function() {
  document.getElementById("button10").onclick = computeTimes(10);
  document.getElementById("button23").onclick = computeTimes(23);
  document.getElementById("button76").onclick = computeTimes(76);
}

this: the current object

  • Event handlers are bound to the attached element's objects
  • Handler function "knows" which element it is listening to (this)
  • Simplifies programming, one function can serve different objects
x:

/* best option: exploit the this keyword */
window.onload = function() {
  document.getElementById("button10").onclick = computeTimes;
  document.getElementById("button23").onclick = computeTimes;
  document.getElementById("button76").onclick = computeTimes;
}

function computeTimes() {
  var times = parseInt(this.innerHTML);
  var input = parseFloat(document.getElementById("input"));
  var res = times * input;
  alert('The result is '+res);
}

this: the current object

  • In global code or regular functions (not bound to an object), this refers to the global window object
  • Simplifies programming, one function can serve different objects

Event examples

mouseover & mouseout




 

Similar event combinations are

  • keydown/keyup
  • mousedown/mouseup

Mouse events

  • A click of the mouse button in-place is a series of events ("clicking"):
    1. mousedown
    2. mouseup
    3. click
  • A click of the mouse button while moveing the mouse ("dragging"):
    1. mousedown
    2. mousemove
    3. ...
    4. mousemove
    5. mouseup
  • Other mouse effects: dblclick, mouseover, mouseout

onblur & onchange

  • onblur: fires when an element is not in focus anymore
    • Used commonly in input validation
  • onchange: fires when the value of an element changes


No changes

onblur & onchange


/* JavaScript */
function whatToDoOnBlur(){
  var input = this.value;
  var matches = input.match(/[0-9]/g);
  if(matches==null || input.length != matches.length) 
  {
    /* Makes the font appear red (more in the CSS lecture) */
    this.style.color = 'red';
    this.value = 'Numbers only';
  }
  else 
  {
    this.style.color = 'black';
  }
 }   

onselect

Task: Write or mark the 3 most important information nuggets





onkeypress

Task: Type out the following text correctly:

 

0 seconds

Keyboard and text events

eventdescription
blurelement loses keyboard focus
focuselement gains keyboard focus
keydownuser presses key while element has keyboard focus
keypressuser presses and releases key while element has keyboard focus (problematic)
keyupuser releases key while element has keyboard focus
selectuser selects text in an element

Keyboard and text events

  • User holding down a key and autorepeating the key: multiple keydown and keypress events
  • keydown vs. keypress: use keydown if possible, keypress is more inconsistent across browsers
  • Only one element has the focus at a time

Keyboard and text events

Which key is used?

  • keyCode: ASCII value of the character that was pressed (convert to a character via String.fromCharCode)
  • charCode: the character that was typed (not standardized!)
  • altKey, ctrlKey, shiftKey: true if Alt, Ctrl, Shift is held down
  • Lets look at the example again ...

Hint: if browser incompatibilities frustrate you go to quirksmode

THE END