Covered HTML and CSS in the last lecture; Javascript today!
Recent advances in Javascript shifts the paradigm of web programming.
Rich web applications are nowadays heavy in client-side
code.
<script type="text/javascript" src="code.js"></script>
<script type="text/javascript">//<![CDATA[
// do something here in Javascript
// ]]></script>
<input type="button" onclick="alert('Hello! Don't do this!')"
value="Click Me!" />
<body>
instead of
<head>
whenever possible
Dynamic Typing - The type changes with its assigned value
var foo = 1; // (typeof foo) becomes 'number'
foo = "hello"; // (typeof foo) becomes 'string'
Declaration is optional but highly recommended
var
- local
to that
functionvar
- global variable, i.e., under
window
var foo = 1; // global variable - under window
window.foo = 1; // equiv. to the above
window['foo'] = 1; // equiv. to the above
function a() {
bar = 2; foo = 2; // global variables
var yo = 1; // local variable to function a()
}
More examples on Function Scoping (test/exam material?):
var foo = 1;
function a(){
var bar = 2, foo = 2;
foo2 = 3;
return foo;
}
a() == 2; // true
foo == 1; // true
foo2 == 3; // true
foo2 === '3' // false: the type-checking part failed
typeof bar == 'undefined' // true
===
will check if the LHS and RHS are of the same type
and value
(or address for arrays and objects)
Function Declaration Approaches:
function add(param1, param2) { return param1 + param2; }
var add = function(param1, param2) { return param1 + param2; }
window.add = function(param1, param2) { return param1 + param2; }
According to function scoping, the first two approaches can become local, while the last one is declaring a global function.
Anonymous function are useful for event listeners:
function(param1) // I have no name
{ /* do something here, to be discussed later */ }
Javascript has no syntax like class. It's function
. :)
var Person = function(name, sex){
this.name = name || 'Unnamed';
this.gender = (sex && sex == 'F') ? 'F' : 'M';
};
Person.prototype.setName = function(name) {return this.name=name};
Person.prototype.getName = function() {return this.name};
Person.prototype.getGender = function() {return this.gender};
prototype
is the interface to add methods to every instance
To initialize a new
instance and call the
methods:
var p1 = new Person('Peter', 'M'),
p2 = new Person('Niki', 'F'),
p3 = new Person();
p1.getGender() == p3.getGender(); // true
p3.getName() == 'Unnamed'; // true
p3.getName=function() {alert('overridden')}; // overriding method
function pib_popupPdf(pdfUrl ,windowName, winProperties, query) {
dcsMultiTrack('DCS.dcsqry',"?fbc=" + query);
var popWin;
if(winProperties != null && winProperties.length>0){
popWin = window.open(pdfUrl,'popupPdf',winProperties);
} else {
popWin = window.open(pdfUrl,'popupPdf','');
}
popWin.focus();
}
(function(){
var myLib = window.myLib = (window.myLib || {}); // global
var a, b, c; // private variables
var calcSubTotal = function() { // private function
// calculate subtotal
};
myLib.checkOut = function() { // public function
// go to the checkout page
};
myLib.addToCart = function(id, quantity) {
// store it in cookies/localStorage first
calcSubTotal();
// display it in the shopping list
};
})();
myLib.calcSubTotal();// undefined! as it's a private function
myLib.addToCart(); // OK!
push()
,
pop()
Some useful methods: join()
, split()
,
shift()
, indexOf()
, etc.
var x = new Array(), // empty array (note: Array could be overridden)
y = [], // empty array (no way to over-ride/-load [])
z = ['Happy', 'New', 'Year', 2021];
x != y // true - although both are of empty content
z.push('!'); // z is ['Happy', 'New', 'Year', 2021, '!']
z.join(' '); // returns "Happy New Year 2021 !"
z.indexOf('Year'); // returns 2 - i.e., zero-indexed
// try z.join(' ').indexOf('year'); by yourself
// following code returns ['JS', 'is', 'fun']
// since String is an array of character
"JS is fun".split(' ');
for
, forEach
, map
, reduce
, filter
, ...var z = ['Happy', 'New', 'Year', 2021];
for
loop in the traditional way:
for (var i = 0; i < z.length; i++) {
// do something with z[i],
// can use break and continue as in C
}
while
better:
var i = 0, length = z.length;
while(i < length) {
// do something with z[i],
// can use break and continue as in C
i++;
}
Generally, the fastest way to for-loop over an array
for (var i = 0, value; value = z[i]; i++) {
// do something with value
}
New approach to loop (Limited Browser Support):
z.map(function(value, index){
// do something with value
})
// need "polyfill" for old browsers
If you've already included the jQuery library:
$(z).each(function(index, value){
// do something with value
// return false to break
})
Key-Value pairs: Referenced with the key, like a hash table
var x = new Object(), // empty object
y = {}, // empty object
z = {"name":"Niki",
"today":function(){return new Date().toDateString();}};
x != y; // true - although both are of empty content
z.age = 6; // {"name":"Niki","today":func...,"age":6}
// the above order may be browser-dependent
z.age == z['age']; // true - can reference like array
z.today(); // returns "Fri Jan 29 2021" for example
Looping over an Object
for (var key in z) {
// z[key] gives the value, can use break and continue as in C
}
String Concatenation: + is also overloaded with plus
var w = 'Hello', x = 'World!', y = 1, z = 2021;
w+' '+x == 'Hello World!' // all return true
w+x+y+z == 'HelloWorld!12021'
y+z+w+x == '2022HelloWorld!' // confusing! exam material?
w+x+(y+z) == 'HelloWorld!2022'
Joining an Array is Faster: very often you will concat string
for (var i = 0, data = []; i < 5; i++) // fast
data.push(i);
data.join(' ') == '0 1 2 3 4' // true
for (var i = 0, data = ''; i < 5; i++) // slow
data += i + ' ';
data == '0 1 2 3 4 ' // true, note the last space
<tag>
corresponds to a Node Object, including CSS, JavsScript
getElementById()
and
getElementsByTagName()
<ul id="header">
<li>Hello</li>
<li>World</li>
</ul>
<script type="text/javascript">
var ul = document.getElementById('header');
var li = ul.getElementsByTagName('li');
// note that we used two commands above
li[0].style.color = '#F00';
li[1].style.color = '#0F0';
</script>
li:last-child
) with
querySelector()
and
querySelectorAll()
<ul id="header">
<li>Hello</li>
<li>World</li>
</ul>
<script type="text/javascript">
var li = document.querySelectorAll('#header li');
li[0].style.color = '#F00';
li[1].style.color = '#0F0';
//re-color the second <li> to #00F
document.querySelector(
'#header li:last-child').style.color = '#00F';
</script>
.parentNode
, .childNodes
, .nextSibling
,
etc.document.head
for <head>
document.body
for <body>
document.forms[n]
for the n-th child
<form>
document.links[n]
for the n-th child
<a>
and <area>
document.frames[n]
for the n-th child
<frame>
and <iframe>
parent
refers to the immediate parent windowtop
refers to the highest parent window that its URL is
reflected in the browser location bar // assuming 'el' is an HTML DOM element
el.innerHTML = 'Your Current Time: ' + (new Date().toString());
// What if the string is untrusted (i.e., from the "user")??
el.innerHTML = 'something <img onerror="alert(\'DANGER\')" />'
//the "/&/g" thing is an example of "regular expression"
el.innerHTML = 'something <img onerror="alert(\'DANGER\')" />'
.replace(/&/g,'&').replace(/</g,'<')
.replace(/>/g,'>');
String.prototype.escapeHTML = function(){
return this.replace(/&/g,'&').replace(/</g,'<')
.replace(/>/g,'>');}
el.innerHTML = userInput.escapeHTML(); //usable thereafter
// To dynamically load a javascript file if needed
var script = document.createElement('script');
script.src = "dynamically-loaded.js";
script.type = "text/javascript";
// to add the script file as last child of document.body
document.body.appendChild(script);
// or, to add as the first child of document.body
document.body.insertBefore(script, document.body.firstChild)
document.body.removeChild(script);
//to remove every child of element el
function(el){while(el.firstChild){el.removeChild(el.firstChild)}
Style
Attribute (not recommended, as explained)
el.style.color = '#F00';
Class
Attribute (preferred)
// Assuming newClass has been defined elsewhere (i.e., the CSS file)
el.className = 'newClass';
// Modern browsers support the classLits below
el.classList = 'newClass 2';
window.location.replace('test2.html'); // redirect to test2.html
window.history.go(-1); // back
An element generates events that reflect its current status,
<p>Hello, Click Me!</p>
<script type="text/javascript">
// assign a function to onclick handler
document.querySelector('p').onclick = function(e){
// display two simple popup dialogs
alert('You clicked hello!');
alert(e.target.innerHTML);
}
</script>
setTimeout()
, setTimeInterval()
More on Event Types: https://www.w3schools.com/TAGS/ref_eventattributes.asp
Image Source: J. Resig, "Pro Javascript Techniques", p.114, 2007
<a href="/">Home</a>
is clicked:
<body>
,
<div>
,
<ul>
,
<li>
,
<a>
Event listeners with bubbling order:
<a>
,
<li>
,
<ul>
,
<div>
,
<body>
<p id="el_p"> <em>
<a href="test1.html" id="el_a">Click Me!</a> </em> </p>
<script type="text/javascript">
var clickMe = function(e){
e = e || window.event;
// IE-specifc way of passing the event
alert('e.target.id:'+e.target.id +', this.id:'+ this.id);},
el_p = document.getElementById('el_p'),
el_a = document.getElementById('el_a');
el_p.onclick = clickMe;
el_a.onclick = clickMe;
// Expected Results:
// First alert: e.target.id: el_a, this.id: el_a
// Second alert: e.target.id: el_a, this.id: el_p
</script>
e.target
always refers to the target
this
refers to the one handling the event<p id="el_p"> <em>
<a href="test1.html" id="el_a">Click Me!</a> </em> </p>
<script type="text/javascript">
var clickMe = function(e){
alert('e.target.id:'+e.target.id +', this.id:'+ this.id);},
el_p = document.getElementById('el_p'),
el_a = document.getElementById('el_a');
el_p.addEventListener("click", clickMe, true);
el_a.addEventListener("click", clickMe, true);
// Expected Results:
// First alert: e.target.id: el_a, this.id: el_p
// Second alert: e.target.id: el_a, this.id: el_a
</script>
addEventListener
turns
capturing order on<p>
now handles the event before
<a>
<p id="el_p"> <em>
<a href="test1.html" id="el_a">Click Me!</a> </em> </p>
<script type="text/javascript">
var clickMe = function(e){
e = e || window.event;
e.preventDefault(); // for W3C standard
return false;}, // for IE 8 or below
el_p = document.getElementById('el_p'),
el_a = document.getElementById('el_a');
el_a.onclick = clickMe;
// Expected Results:
// No page navigation when clicked
</script>
<p id="el_p"> <em>
<a href="test1.html" id="el_a">Click Me!</a> </em> </p>
<script type="text/javascript">
var clickMe = function(e){
e = e || window.event;
alert(this.id);
e.stopPropagation(); // for W3C standard
e.cancelBubble = true; }, // for IE 8 or below
el_p = document.getElementById('el_p'),
el_a = document.getElementById('el_a');
el_a.onclick = clickMe; // <a> first as bubbling
el_p.onclick = clickMe;
// Result: One alert appears and displays el_a,
// then page navigation occurs
</script>
el_a
, so el_p
will not receive the event<iframe>
/<frame>
will
implicitly block event from propagatingel.onclick = function(e) {
e = e || window.event; // IE passes the event in global window
alert(e.target); // e.target is a reference to target element
}
el.addEventListener("click", function(e) {
alert(e.target);
}, false); // false for bubbling, true for capturing
attachEvent()
Which is preferred, traditional or W3C? Pros and Cons?
el.onclick = function(e) {/* given that you did something with el */};
// BAD colleage can "append" the following code, e.g., in a .js file (library) you load
el.onclick = function(e) {/* which will override your code */};
// SMART colleage can do complex things leveraging *function scoping*:
(function(){ // Concept of Closure, 02-reading OO JS, p.27-30
var _onclick = el.onclick; //_onlick now is a local variable
el.onclick = function(e){
// execute your handler first if it exists
_onclick // AND logic: the second predicate only matters if the first one is true
&& _onclick.call(this, e); // preserve the event object
// .call assigns its first argument to be the "this" or it is lost (becomes "window")
/* can now do his things */
} })(); // execute the annonymous function itself
More in the reading - J. Resig, "Pro Javascript Techniques", p.123, 2007