9. May 9th, 2005 Http://www.flickr.com/photos/nataliedowne/14517351/
(me, in 2005)
10. Also in 2005
✤ Gmail had its first birthday, still in invite-only beta
✤ Google Maps launched February 8th
✤ The term “Ajax” was coined February 18th by Jesse James Garrett
✤ Ajaxian.com launched March 10th
11. A flurry of library activity
✤ February 2005: First Prototype.js
✤ March 2005: First public MochiKit code
✤ YUI started development internally at Yahoo! in 2005 (first public
release was February 2006)
✤ jQuery released at BarCamp Boston in January 2006
20. Everything in JavaScript is an object
Strings and numbers
> "A string".length
8
> 123.toString()
SyntaxError: Unexpected token ILLEGAL
> (123).toString()
“123”
Even functions:
> function hello() { alert("hello") }
> hello.toString()
"function hello() { alert("hello") }"
21. You can make your own objects
// The same thing:
var simon = new Object();
var simon = {};
// Also the same:
simon.name = "Simon Willison"; // name is a property
simon["name"] = "Simon Willison";
// Object literal syntax is most useful:
var simon = {
name: "Simon Willison",
age: 29
};
22. You can loop through properties
> var simon = {
name: "Simon Willison",
age: 29
};
> for (var prop in simon) {
console.log(prop + ': ' + simon[prop]);
}
name: Simon
age: 29
(more on this later...)
23. (almost) Everything in JavaScript is a
property on an object
> parseInt("100 bunnies");
100
> parseInt === window.parseInt // window is the global object
true
> window === window.window
true
> window === window.window.window
true
> true === window.true
SyntaxError: Unexpected token true
24. Arrays
> var a = new Array(); // Old-school
> var a = []; // Literal syntax
> var a = ["dog", "cat", "chicken"];
> a.length;
3
> a[0]
"dog"
> a[2]
"chicken"
> a[3]
undefined
25. Iteration through arrays
> var a = ["dog", "cat", "chicken"];
> for (var i = 0; i < a.length; i++) {
console.log(a[i]);
}
dog
cat
chicken
26. Tricksy array iteration
> var a = ["dog", "cat", "chicken"];
> for (var i = 0, item; item = a[i]; i++) {
console.log(item);
}
dog
cat
chicken
// But watch out for falsey values:
> var a = [123, 0, 12, 443];
> for (var i = 0, item; item = a[i]; i++) { console.log(item); }
123
29. Functions
// What could be simpler?
function addTwoNumbers(a, b) {
var total = a + b; // A local variable
return total;
}
> addTwoNumbers(2, 4)
6
30. Functions
// What could be simpler?
function addTwoNumbers(a, b) {
var total = a + b; // A local variable
return total;
}
> addTwoNumbers(2, 4)
6
> addTwoNumbers()
NaN
31. Functions
// What could be simpler?
function addTwoNumbers(a, b) {
var total = a + b; // A local variable
return total;
}
> addTwoNumbers(2, 4)
6
> addTwoNumbers()
NaN
> addTwoNumbers(2, 4, 8)
6
32. Function parameters are more
like guidelines
// arguments is a magic array-like object
function add() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum;
}
> add(1, 3, 4, 5, 0, 5);
18
33. Anonymous functions
var add = function() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum;
}
var added = (function() {
var a = 2, b = 5; // Local variables
return a + b;
})();
34. Modern array iteration
> var a = ["dog", "cat"];
> a.forEach(function(item) {
console.log(item);
}
dog
cat
> a.forEach(function(item, index) {
console.log(index + ': ' + item);
}
0: dog
1: cat
35. Closures
function makeOp(op, x) {
switch(op) {
case '+':
return function(y) { return y + x };
case '-':
return function(y) { return y - x };
case '/':
return function(y) { return y / x };
case '*':
return function(y) { return y * x };
}
}
> var third = makeOp('/', 3);
> var dbl = makeOp('*', 2);
> console.log(third(12) + ' ' + dbl(8));
4 16
36. How does this work?
✤ Remember “everything in JavaScript is a property of an object”?
✤ Imagine that local variables belong to a “local scope” object, which
gets created when a function is executed
✤ Now imagine this object can stick around after the function has
finished executing
✤ A closure is a function plus the scope in which that function was
created
✤ Since closures capture state, you can use them as a kind of object
37. Real-world closure example
function revealer(el, duration) {
return function(ev) {
ev.preventDefault();
el.show(duration);
}
}
$("#mylink').click(revealer($('#panel'), 500);
$("#mylink2').click(revealer($('#panel2'), 1000);
38. Functions and objects
function makePerson(first, last) {
return {
"first": first,
"last": last
}
}
function personFullName(person) {
return person.first + ' ' + person.last;
}
> simon = makePerson("Simon", "Willison");
> personFullName(simon)
"Simon Willison"
39. First attempt at methods
function makePerson(first, last) {
return {
"first": first,
"last": last,
"fullName": function() {
return this.first + ' ' + this.last;
}
}
}
> simon = makePerson("Simon", "Willison");
> simon.fullName();
"Simon Willison"
40. What the heck is “this”?
✤ When you write:
> simon.fullName();
✤
fullName() is executed with this pointing to simon.
✤ If you call a method without using the '.' operator, this is set to the
global object, i.e. window.
> var fullNameMethod = simon.fullName;
> fullNameMethod();
undefined undefined
42. You can control what this is
> simon = makePerson("Simon", "Willison");
> nat = makePerson("Natalie", "Downe");
> nat.fullName();
"Natalie Downe"
> nat.fullName.call(simon);
"Simon Willison"
> simon.fullName.apply(nat);
"Natalie Downe"
43. call v.s. apply
✤ Call lets you specify both the value of this and the arguments that
should be passed:
✤ myFunction.call(myThis, arg1, arg2, arg3);
✤ Apply lets you do the same thing, but pass an array of arguments
instead:
✤ myFunction.apply(myThis, [arg1, arg2, arg3]);
✤ (I always have to look this up)
44. Constructors
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = function() {
return this.first + ' ' + this.last;
}
}
> var simon = new Person("Simon", "Willison");
> s.fullName();
"Simon Willison"
45. What does “new” do?
✤ var simon = new Person(first, last);
✤ Creates an empty object: {}
✤ Executes the Person function with this set to the new empty object
✤ Adds Person.prototype to the object's prototype chain
46. Prototype inheritance
The following is wasteful
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = function() {
return this.first + ' ' + this.last;
}
}
How can we avoid creating a fullName function for every object?
48. You can extend built-in classes
> "hello".reversed();
TypeError: Object hello has no method 'reversed'
> String.prototype.reversed = function() {
var r = '';
for (var i = this.length - 1; i >= 0; i--) {
r += this[i];
}
return r;
}
> "hello".reversed();
"olleh"
49. That doesn’t mean you should
✤ Don’t modify objects you don’t own
✤ Extending Object.prototype breaks the (for var prop in obj) idiom
✤ Prototype.js added document.getElementsByClassName
✤ Then Mozilla added document.getElementsByClassName...
✤ The behaviour was slightly different, so code broke
✤ If you’d written your own Array.forEach() method, today your code
would be clashing with the new forEach() method in JavaScript 1.6
50. Prototype chain
var simon = new Person(...);
simon.toString();
Try simon.toString()
...
Try Person.prototype.toString()
...
Try Object.toString()
...
Give up
51. Advanced prototype chains
function Mammal() { ... }
Mammal.prototype.eatThings = ...
Person.prototype = new Mammal();
Person.prototype.learnToRead = ...
var simon = new Person(...);
simon.eatThings();
Try simon.eatThings()
...
Try Person.prototype.eatThings()
...
Try Mammal.eatThings()
...
Try Object.eatThings()
...
Give up
53. In 2004, no one used libraries...
✤ Well, sort of...
✤ If you wanted to write anything interesting in JavaScript, there were a
few utility functions you needed in every single project
55. Adding events
var link = document.getElementById(‘mylink’);
link.onclick = function() {
alert(this.href);
return false;
}
56. Adding more than one event?
// W3C standard browsers
var link = document.getElementById('mylink');
link.addEventListener('click', function() {
alert("Hello");
return false;
});
// IE 6
link.attachEvent("onclick", function() {
alert("Hello");
return false;
);
57. The addEvent function
function addEvent(obj, evType, fn, useCapture){
if (obj.addEventListener){
obj.addEventListener(evType, fn, useCapture);
return true;
} else if (obj.attachEvent){
var r = obj.attachEvent("on"+evType, fn);
return r;
} else {
alert("Handler could not be attached");
}
}
// DON'T USE THIS THOUGH
http://www.scottandrew.com/weblog/articles/cbs-events
58. ✤ What if you want to keep track of the event listeners that have been
added?
✤ In particular so you can manually de-register them when the page
unloads, to clean up potential memory leaks in IE
✤ addEvent doesn't fix the different event objects for you, so you still
have to work around browser differences there
addEvent drawbacks
59. Dean Edwards addEvent
function addEvent(element, type, handler) {
" if (element.addEventListener) {
" " element.addEventListener(type, handler, false);
" } else {
" " // assign each event handler a unique ID
" " if (!handler.$$guid) handler.$$guid = addEvent.guid++;
" " // create a hash table of event types for the element
" " if (!element.events) element.events = {};
" " // create a hash table of event handlers for each element/event pair
" " var handlers = element.events[type];
" " if (!handlers) {
" " " handlers = element.events[type] = {};
" " " // store the existing event handler (if there is one)
" " " if (element["on" + type]) {
" " " " handlers[0] = element["on" + type];
" " " }
" " }
" " // store the event handler in the hash table
" " handlers[handler.$$guid] = handler;
" " // assign a global event handler to do all the work
" " element["on" + type] = handleEvent;
" }
};
// a counter used to create unique IDs
addEvent.guid = 1;
60. Dean Edwards addEvent (2)
function removeEvent(element, type, handler) {
" if (element.removeEventListener) {
" " element.removeEventListener(type, handler, false);
" } else {
" " // delete the event handler from the hash table
" " if (element.events && element.events[type]) {
" " " delete element.events[type][handler.$$guid];
" " }
" }
};
function handleEvent(event) {
" var returnValue = true;
" // grab the event object (IE uses a global event object)
" event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
" // get a reference to the hash table of event handlers
" var handlers = this.events[event.type];
" // execute each event handler
" for (var i in handlers) {
" " this.$$handleEvent = handlers[i];
" " if (this.$$handleEvent(event) === false) {
" " " returnValue = false;
" " }
" }
" return returnValue;
};
65. Animate a div moving across a screen
✤ Easy! Just use setInterval() to move it left 10 pixels every 10th of a
second
✤ But... what if you're animating lots of things, and the user's
computer can't keep up...
✤ Solution: figure out where you want it to be in 2 seconds time, then
check how much time has elapsed each time round the animation
loop and adjust the position accordingly
67. ✤ Watch out for an onmousedown event over the object you want to
drag
✤ Attach an onmousemove event to the body, and move the element
with the mouse
✤ Watch for onmouseup, and remove the mousemove handler
✤ Simple right?
Drag and drop implementation
68. Drag and drop, for real
✤ Need to be able to distinguish between a click and a drag
✤ How about... a drag starts when
✤ The user moves their mouse at least 5 pixels
✤ OR... the user holds down the mose button on the draggable for at
least a full second
✤ Need to restrict the area in which the draggable can be dragged
✤
Highlight drop targets when the draggable intersects them
✤ Revert the position of the item if it’s dropped in the wrong place...
70. The truth is...
✤ By the time you’ve implemented event handling, basic animation,
DOM manipulation and drag and drop, you’ve re-invented a sizable
chunk of jQuery, YUI or Dojo, probably with more lines of code and
definitely with a whole lot more bugs.