Upload
others
View
3
Download
0
Embed Size (px)
Citation preview
JavaScript 2Jef De Smedt
Beta VZW
Overview
• Introduction
• Basic elements of JavaScript
• Arrays
• Window object
• Document Object Model
• Forms
• Events
• Objects
Recap
• JavaScript is a scripting language that is browser specific
• EcmaScript is a standard language which only describes the language, not how to access the elements in the browser
• JavaScript can access the browser using the window-object
• Window is the default object in a browser
• We can access the HTML page with window.document (or, sincewindow is default, just document)
• The document object uses the Document Object Model (DOM)
Recap: the document object
• Access the HTML elements in JavaScript => Document Object Model (DOM)
<html><head></head><body><ol><li>one</li><li>two</li></ol></body></html>
html
head
body
ol
li
li
one
one
Child element
Parent element
Text element
The DOM-tree
Recap: the text of an element
<!DOCTYPE html>
<html>
<head>
<title>Copy values</title>
</head>
<body>
<h1 id="heading">Title</h1>
<div id="body"></div>
<script>
var elH1 = document.getElementById("heading");
var elBody = document.getElementById("body");
elBody.textContent = elH1.textContent;
</script>
</body>
</html>
Recap: the value attribute
• (some) Elements have textnode children
• The text of an <input>-element is not a textnode
• <input type=“text“ id=“name” name=“name” value=“John”/><!DOCTYPE html>
<html>
<head>
<title>Value attribute</title>
</head>
<body>
<h1 id="heading">John</h1>
<input type="text" id="name" name="name" value="nobody"/>
<script>
var elH1 = document.getElementById("heading");
var elName = document.getElementById("name");
elName.value = elH1.textContent;
</script>
</body>
</html>
Quite often attributes can beaccessed as properties
elName.setAttribute("value", elH1.textContent);
A more generic way is using setAttribute andgetAttribute.
Recap: Clicking on a button
<!DOCTYPE html>
<html>
<head>
<title>OnClick attribute</title>
<script>
function SayHi() {
alert("Hi all");
}
</script>
</head>
<body>
<h1 id="heading">John</h1>
<button id="btn2" onclick="SayHi();">Say hi</button>
</body>
</html>
A function is executed when it is called, not when it is read by the browser
JavaScript code to execute the function.
Recap: Onclick property vs Onclick attribute
• Instead of using the HTML onclick attribute we can also use the JavaScript onclickproperty
• (compare to: HTML value attribute and JavaScript value property)<body>
<form>
Name: <input type="text" id="txtName"/>
<input id="btn" type="button" value="Say hi"/>
<div id="result"></div>
<script>
var btn = document.getElementById("btn");
btn.onclick = SayHi;
</script>
</form>
</body>
Exercise edit
Edit field with button. There is also a div below the edit field
When the user enters a nameand clicks on the button …
A greeting should appear. The greeting is time dependent:Before 13:00: good morning13:00 and later: good afternoon
Exercise Select
2 select elements,2 buttons and a listwith names
When an element isselected and youclick on the button“>”…
…the selected elementis moved from left to right
Of course this should also work for the “toLeft” button.Extra: when no item is selected, the corresponding button should be disabled
Why use btn.onclick instead of < … onclick=…> ?• We want to separate the JavaScript code from the HTML code
• Compare to styles: in most cases you should not use the styleattribute in HTML
• Clear separation between HTML and JavaScript is one of theprinciples of unobtrusive JavaScript: the site should still work without JavaScript
• Let us look at an example
The obtrusive way
• This will not work if JavaScript is disabled and there is no way to make it work:
<a
href="javascript:window.open('http://www.w3.org/wiki/The
_principles_of_unobtrusive_JavaScript')">W3C Article</a>
• The following will work if JavaScript is disabled but it is a maintenance nightmare
<a
href="http://www.w3.org/wiki/The_principles_of_unobtrusi
ve_JavaScript"
onclick="window.open('http://www.w3.org/wiki/The_princip
les_of_unobtrusive_JavaScript');return false;">W3C
Article</a>
The unobtrusive way
<a class="newwindow" href="http://www.w3.org/wiki/The_principles_of_unobtrusive_JavaScript">W3C Article</a>
<script>
var anchors = document.getElementsByClassName("newwindow");
for (var i=0; i<anchors.length;i++) {
anchors[i].onclick = OpenWindow;
}
function OpenWindow(){
window.open(this.href);
return false;
}
</script>
return false: Do not execute thedefault behavior (=follow link)
The keyword this refersto the <a>-element
HTML Forms and JavaScript
• JavaScript can be used to check form input• We need a way to check the fields (HTML5 has many ways to check input, we
will use the JavaScript approach here)
• We need a way to prevent submitting the form
• We need a way to show an error
• We can check fields by “finding” them in the DOM-tree
• We can prevent submitting a form by having a function return false(prevents default action)
• We can show an error by changing the css-class of an element
Finding elements
• When we have a class “required” <form method="get" action="http://bing.com" id="bingform">
<label for="q">Search</label>
<input type="text" name="q" id="q" class="required"/>
<input type="submit" value="submit"/>
</form>
• we can find all the “required” child elements of a form usinggetElementsByClassName:
form.getElementsByClassName(“required”);
Prevent submitting a form
• The default action for a form is submitting• When an eventhandler returns false, the default action will not be
executedvar f=document.getElementById("bingform");
f.onsubmit = checkrequired;
function checkrequired() {
if (checks_are_ok){
return true;
}else{
return false;
}
}
Combined with getElementsbyClassName
function checkrequired() {
var requiredElements = this.getElementsByClassName("required");
for (var i=0;i<requiredElements.length;i++) {
var val = requiredElements[i].value;
if (val === null || val.trim() === "") {
return false;
}
}
return true;
}
Because checkrequired is added to the form, this refersto the form.
Show error by changing css-class
<style>.hideerror{
display:none;}.showerror{
display:inline;color: red;
}</style>
<form method="get" action="http://bing.com" id="bingform"><div>
<label for="q">Search</label><input type="text" name="q" id="q" class="required"/><span class="hideerror">Field is required</span>
</div><input type="submit" value="submit"/>
</form>
We have to change the class from “hideerror” to“showerror” in case of an error
Show error by changing css-class
• Most HTML-attributes are accessible as JavaScript properties (value, href, …)
• However “class” is a reserved word in JavaScript => .className
element.className = “showerror”;
• But how do we find the span-element?requiredElements[i].nextElementSibling.className=“showerror”;
nextElementSibling returns the next element with the same parent.(nextSibling would return the next node with the same parent => textnode.)
What if there is more than one class
• .className is a space separated list of classnames
• element.className overwrites the existing css-classes
• In this example that is OK (we want to change “hideerror” into“showerror”)
• In modern browsers we can use classList:var error = requiredElements[i].nextElementSibling;
if (val === null || val.trim() === "") {
error.classList.remove("hideerror");
error.classList.add("showerror");
return false;
}
Altering a form before submitting
• When we want to search Bing.com for videos, we have to change the URL: http://bing.com/videos
<div><label><input type="checkbox" id="invideos"/>Search in
videos</label></div>
Let us try searching in videos using a checkbox
We do not add a name becausewe do not want to send thecheckbox to Bing
Altering a form before submitting
• We can change the HTML action attribute by changing the .action property in JavaScript
var invideo = document.getElementById("invideos");
if (invideo.checked){
this.action += "/videos";
}
Refers to the form
Anonymous functions
• When a function is used in only one place, we do not really have to give it a name => anonymous function
f.onsubmit = function() {
var requiredElements = this.getElementsByClassName("required");
for (var i=0;i<requiredElements.length;i++) {
var val = requiredElements[i].value;
if (val === null || val.trim() === "") {
return false;
}
}
Searching googleBecause there are no French websites in the Netherlands, this combination should give an error
Every other combination should be OK
Possible values: languages(All, French, Dutch), countries(All, France, Belgium, Netherlands)
Summary: finding elements
• document.getElementById(“id”): returns one element
• document.getElementsByTagname(“input”): returns all input elements in a document
• element.getElementsByClassName(“class”): returns a collection of allelement children with a certain class
• form.elements.namedItem(“name_or_id”): returns an element withname or id (IE: no name) in a form
Advanced events
• The onclick, onsubmit, on… events are one way to work with events
• There is also the addEventListener function
• Disadvantage• Does not work in older versions of IE (<9) and Opera (<7): attachEvent
• Advantages• Can also add eventhandlers to textnodes, the window object,
XMLHttpRequest object, …
• Can add multiple handlers for the same event
• Can also react to event capturing
Capturing and bubbling
<!DOCTYPE html>
<html>
<head>
</head>
<body id="body">
<div id="div">
<p id="paragraph">Text</p>
</div>
</body>
</html>
Div is a child of body(in z-order body sits behind div)
Paragraph is a child of div (div sits behind paragraph)
Capturing and bubbling
<script>var body = document.getElementById("body");var para = document.getElementById("paragraph");var div = document.getElementById("div");body.addEventListener("click", function() {
alert("body is clicked(bubbling)");});body.addEventListener("click", function() {
alert("body is clicked(capturing)");}, true);para.addEventListener("click", function() {
alert("Paragraph is clicked(capturing)");}, true);para.addEventListener("click", function(){
alert("Paragraph is clicked(bubbling)");});div.addEventListener("click", function(){
alert("Div is clicked(bubbling)");});div.addEventListener("click", function() {
alert("Div is clicked(capturing)");}, true);
</script>
useCapture parameter (default is false)
Adds capturing and bubblingeventhandlers to body, divand paragraph.
Order of alerts:1. Body is clicked (capturing)2. Div is clicked(capturing)3. Paragraph is clicked(capturing)4. Paragraph is clicked(bubbling)5. Div is clicked(bubbling)6. Body is clicked(bubbling)
Capturing and bubbling (more general)
<script>var body = document.getElementById("body");var para = document.getElementById("paragraph");var div = document.getElementById("div");body.addEventListener("click", showClick);
…
function showClick(event){var element = this.localName;var phase = "";switch(event.eventPhase) {
case event.CAPTURING_PHASE:phase="capturing";break;
case event.BUBBLING_PHASE:phase="bubbling";break;
case event.AT_TARGET:phase="at target";break;
}alert("Element: "+element + " in phase: "+phase);
}
</script>
Function is owned by body, div or para => this
Paragraph is “at target” (no capture or bubble)
AddEventListener remarks
• We can also remove event listeners: removeEventListener
• In IE 8 en Opera 6: attachEvent
• Possible cross browser solution:
if (x.addEventListener) {
x.addEventListener("click", myFunction);
} else if (x.attachEvent) {
x.attachEvent("onclick", myFunction);
}
• IE 8: no event argument => window.event
Window.onload vs DOMContentLoaded
• Up till now we put our script at the end of the DOM-tree => DOM tree is loaded
• We can also use an event to execute a JavaScript function when thepage is loaded: onload-event
• This event also exists for the window object
• Because the window object always exists we can put our script in the<head> element
• The onload event happens when the page is completely loaded(DOM-tree, images, styles, …)
Window.onload vs DOMContentLoaded
<!DOCTYPE html><html>
<head><title>Onload event</title><script>
"use strict";window.onload = function(){
var para = document.getElementById("para");para.innerHTML="I can write JavaScript";
}</script>
</head><body>
<p id="para"></p></body>
</html>
Window.onload vs DOMContentLoaded
• DOMContentLoaded event occurs when the DOM-tree is fully loaded
• It comes before window.onload => JavaScript code will run sooner
• It is defined on the document object
Window.onload vs DOMContentLoaded
<!DOCTYPE html><html><head><title>Onload event</title><script>"use strict";document.addEventListener("DOMContentLoaded", function(){var para = document.getElementById("para");para.innerHTML="I am first";
});</script>
</head><body>
<p id="para"></p>
</body>
</html>
Object oriented JavaScript
• JavaScript is not an object oriented programming language like Java, C++, C#, Objective C, Python, Ruby, PHP, …)
• JavaScript has no classes but uses “prototypes”
• JavaScript makes no difference between objects and namespaces
JavaScript namespaces
• Let us assume we want to create a library in JavaScript that can beused to do mathematical calculations:
"use strict";
var PI=3.1415927;
function CircleCircumference(radius){
return 2 * PI * radius;
}
function CircleArea(radius) {
return PI * radius * radius;
}
JavaScript namespaces
• We can use this library in an HTML page:
<!DOCTYPE html><html><head><title>Namespaces</title><script src="mathlib.js"></script><script>"use strict";document.addEventListener("DOMContentLoaded", function() {var radius = 3;var circumference = CircleCircumference(radius);document.write("A circle with radius " + radius + " has
circumference " + circumference);});
</script></head><body></body>
</html>
JavaScript namespaces
• This works and gives the result:
A circle with radius 3 has circumference 18.8495562
• However when we add one line:<html>
<head><title>Namespaces</title><script src="mathlib.js"></script><script src="movielib.js"></script><script>…
• We get this result:
A circle with radius 3 has circumference NaN
JavaScript namespaces
• This is because of the contents of movielib.js:
"use strict";
var PI = "Irrfan Khan";
• This statement overwrites the value of PI
• We can create the variable in a namespace MyMovie
"use strict";
var MyMovie = {PI:"Irrfan Khan"};
JavaScript namespaces
• We can do the same thing for the Math variable
"use strict";
var MyMath = {PI:3.1415927};
function CircleCircumference(radius){
return 2 * MyMath.PI * radius;
}To refer to PI in theMyMath namespace we use the dot notation.
JavaScript objects
• We define an object in the same way:
var p1 = {FirstName:John, LastName:Lennon};
var p2 = {FirstName:George, LastName:Martin};
• We could say that p1 and p2 are persons but there is no such thing as a Person class => not all “person-object” will have the sameproperties
Factory method
• Using a factory method we can create objects with the sameproperties easier
function personFactory(firstname, lastname){
var p = {Firstname: firstname, Lastname: lastname};
return p;
}
var person = personFactory(“John”, “Lennon”);
• This is what the new operator does
JavaScript “constructor” with new operator
• We can make objects uniform by using a function:function Person(firstname, lastname){
this.FirstName = firstname;
this.LastName = lastname;
}
var p1 = new Person(“John”, “Lennon”);
document.write(p1.FirstName); // “John”
• Using the new-operator we create a new object• Next the function Person is called• “This” refers to the newly created object => every object created using the
Person function has a FirstName and a LastName
JavaScript object methods
• Every person object gets its own FirstName and LastName
• When we look at the object p1 (console.dir) we see the following:
• The special property __proto__ is copied from the prototype property of the Person function. It is shared by all objects that are instantiatedby “new Person()”
• When we use the dot operator the JavaScript engine looks for theitem on the object itself. If it is not found, it will look for the item in __proto__
JavaScript object methods
• We can add a function to the prototype property of Person:
Person.prototype.GetFullName = function() {
return this.FirstName + " " + this.LastName;
}
• This method is now available for all “Person-objects”(new Person())
• When we write
var name= p1.GetFullName();
The engine will first look for the function on the object itself. If it is notfound it will look for the function in __proto__
Ecmascript 6 (not in Internet Explorer):class
• The class keyword does exactly the same thing
class Person{
constructor(firstname, lastname){
this.firstname = firstname;
this.lastname = lastname;
}
GetFullname(){
return this.firstname + " " + this.lastname;
}
}
let p = new Person("John", "Lennon");
console.log(p.GetFullname());
What does “new” do?
1. It creates a new Object
2. It copies the prototype of the constructor function to the new Object
3. It invokes the constructor function on the object together with thearguments
What does “new” do?
function Person(firstname, lastname){
this.firstname = firstname;
this.lastname = lastname;
}
var p = new Person("John", "Doe");
console.log(p.firstname);
function Person(firstname, lastname){
this.firstname = firstname;
this.lastname = lastname;
}
var p2 = Object.create(Person.prototype);
Person.apply(p2, ["Mary", "Doe"]);
console.log(p2.firstname);
This is the same as…
… this
p2 becomes “this” in thePerson function
JavaScript object methods
• “this” in GetFullName() refers to the namespace/object in which thefunction is executed = > p1
• When we execute console.dir(p1) we see the following:
• If we were to add GetFullName() to p1 (p1.GetFullName=…) thatwould take precedence over __proto__.GetFullName (prototypicalinheritance)
The JavaScript function object
• Every function is an object and has a prototype
• The prototype has a call() function to use the function with an object (which we will never do)
function GetFullName2(){
return this.FirstName + " " + this.LastName;
}
document.write(GetFullName2.call(p1) + "<br>");
• By calling call(p1) we set p1 as the “this” object.
Closures
• JavaScript has two variable scopes: global and (function) local• Global or window scope (name is available everywhere in the script):var name="John";
• Every variable declared in a function is only available in the function:function DoSomething() {
var number = 42;}
• JavaScript does not have “block” scope when we use “var”while(i<10){
var j = 5;i++;
}document.write(j);
J is known outside of block. If this while-statement is usedat the top-level (global) scope, j becomes part of thewindow object.
Intermezzo: remember hoisting
• The codewhile(i<10){
var j = 5;i++;
}
• Is the same asvar j;
while(i<10){j = 5;i++;
}
• Conclusion: variables are hoisted on the global and function level
Closures
• The lifetime of a function scoped variable is equal to the executiontime of the function:
function DoSomething(){
var i;
document.write(i);
}
DoSomething(); Variable i exists only during theexecution of this statement.
Intermezzo: inner functions
• In JavaScript functions can be nested
• A nested function has access to the local variables of its parentfunction
function outerFunction() {
var i=42;
function innerFunction() {
document.write(i);
}
}
variable i is accessible in the inner function
Closures
• Closure defines a local variable with a lifetime that exceeds theexecution time of the function
• When a variable in a parent function is used in an inner function, thevariable remains available to the inner function even after the parentfunction has finished executing
• But maybe we better take a look at an example…
Closures
<script>
"use strict";
var add = function(){
var counter=0;
return function() {
counter++;
document.write(counter + "<br>");
}
}();
add();
add();
</script>
Mind the call-operator, Richard! (minding the call operator, dear)
The variable add is not equal tothe anonymous function, but tothe returnvalue of this function.
The returnvalue is also a function.This inner function refers to counterwhich is defined in the outer function.
Every time we call add(), we are in factcalling the inner function.
The variable counter is initialized once(during the execution of the parentfunction). The variable will remainaccessible to the inner function.
Output: 12
Closure in an object oriented way(module design pattern)• obj has a “private” member variable (state)
• We can get the value using GetCounter()
• We can change the value using Increment()
var obj = function() {
var counter = 0;
return {
GetCounter: function() {return counter;},
Increment: function() {counter++;}
}
}();
obj.Increment();
document.write(obj.GetCounter());
Be carefull with closures
function uniqueIDCreator(people){var ID=1;for (var i=0;i<people.length;i++){
people[i]["id"] = function(){return ID + i;
}}return people;
}var people = [{name:"John",id:0}, {name:"Paul", id:0}];var peoplewithID = uniqueIDCreator(people);for(var i=0;i<people.length;i++){
console.log(people[i].name + ": " + people[i].id());}
• In the next example all invocations of the id() function will return 3
Solution: immediately invoked functions
• The next example returns the right resultfunction uniqueIDCreator(people){
var ID=1;
for (var i=0;i<people.length;i++){
people[i]["id"] = function(j){
return function(){
return ID + j;
}
}(i);//immediately invoking the function
}
return people;
}
var people = [{name:"John",id:0}, {name:"Paul", id:0}];
var peoplewithID = uniqueIDCreator(people);
for(var i=0;i<people.length;i++){
console.log(people[i].name + ": " + people[i].id());
}
“Let” to the rescue!!!
function uniqueIDCreator(people){var ID=1;for (let i=0;i<people.length;i++){
people[i]["id"] = function(){return ID + i;
}}return people;
}var people = [{name:"John",id:0}, {name:"Paul", id:0}];var peoplewithID = uniqueIDCreator(people);for(var i=0;i<people.length;i++){
console.log(people[i].name + ": " + people[i].id());}
“Let” will create a new scope for each iteration, effectively creating an unique value that will notchange afterwards.
TypeScriptA “better” JavaScript
Purpose
• JavaScript was originally intended to be a scripting language forsimple tasks
• It is difficult to write “big” programs in such a language
• This gave rise to alternatives: Coffeescript, Dart, TypeScript
• TypeScript differs from the other two in that a “normal” JavaScript program also is a valid TypeScript program.
Typed variables
let isDone: boolean = false;
let decimal: number = 6;
let color: string = "blue";
let list: number[] = [1, 2, 3];
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
let notSure: any = 4;
function warnUser(): void {
alert("This is my warning message");
}
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length; //Type assertions (casting)
In Typescript we can definethe type of a variable. The Typescript compiler will check the type. Important: when you have anerror in your types, thecompiler will still emitJavaScript code.
Functions
function add(x: number, y: number): number {
return x + y;
}
let myAdd = function(x: number, y: number): number { return x + y; };
Named function
Anonymous function
Argument type
Return type
Functions are also typed in typescript. For each of the arguments we define the type and we also add the returntype
Generics
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("myString");
When we have a typed language like TypeScript, itis useful to have generics.In this example the variable output will be of type “string”
Interfaces: Duck typing
• “If it walks like a duck and quacks like a duck, it is a duck”
• We don’t implement an interface. Typescript checks whether allelements of the interface are available
interface LabelledValue {
label: string;
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
A LabelledValue is an Object that has a label
myObj has a label, so we can use it with printLabel
printLabel takes an argument of type LabelledValye
Classes
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
console.log(greeter.greet());
A class has a constructor function that will be used toinitialise the object. It is “transpiled” into thefollowing JavaScript code.
"use strict";
var Greeter = /** @class */ (function () {
function Greeter(message) {
this.greeting = message;
}
Greeter.prototype.greet = function () {
return "Hello, " + this.greeting;
};
return Greeter;
}());
var greeter = new Greeter("world");
console.log(greeter.greet());
Inheritance
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();
Formatted string
When a class extends another class it inherits the methods of the base class.