Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduction to Prototype.js

Introduction to Prototype.js

Bacb1158b77a142d2cb8ae1f866c75f1?s=128

Christophe Porteneuve

July 25, 2007
Tweet

More Decks by Christophe Porteneuve

Other Decks in Technology

Transcript

  1. Introduction to Prototype Christophe Porteneuve The Ajax Experience · San

    Francisco, July 25 2007
  2. “Prototype,” uh?

  3. “Prototype,” uh? • Open-source JS framework

  4. “Prototype,” uh? • Open-source JS framework • Extracted from real

    apps, real needs
  5. “Prototype,” uh? • Open-source JS framework • Extracted from real

    apps, real needs • Makes JS feel nicer (e.g. more expressive)
  6. “Prototype,” uh? • Open-source JS framework • Extracted from real

    apps, real needs • Makes JS feel nicer (e.g. more expressive) • NOT widget-oriented. More of a core...
  7. “Prototype,” uh? • Open-source JS framework • Extracted from real

    apps, real needs • Makes JS feel nicer (e.g. more expressive) • NOT widget-oriented. More of a core... • Actively maintained
  8. “Prototype,” uh? • Open-source JS framework • Extracted from real

    apps, real needs • Makes JS feel nicer (e.g. more expressive) • NOT widget-oriented. More of a core... • Actively maintained • Excellent online documentation
  9. “Porteneuve,” uh?

  10. “Porteneuve,” uh? • 29, lives in Paris. Web work since

    1995.
  11. “Porteneuve,” uh? • 29, lives in Paris. Web work since

    1995. • CTO @ Ciblo.net
  12. “Porteneuve,” uh? • 29, lives in Paris. Web work since

    1995. • CTO @ Ciblo.net • Prototype Core member
  13. “Porteneuve,” uh? • 29, lives in Paris. Web work since

    1995. • CTO @ Ciblo.net • Prototype Core member • Rails & script.aculo.us contributor
  14. Prototype is people

  15. Prototype is people

  16. Prototype is people

  17. So you said, “intro...”

  18. So you said, “intro...” • We’ll take it easy: newbies,

    welcome!
  19. So you said, “intro...” • We’ll take it easy: newbies,

    welcome! • There’s an advanced session anyway
  20. So you said, “intro...” • We’ll take it easy: newbies,

    welcome! • There’s an advanced session anyway • What’s in this one?
  21. So you said, “intro...” • We’ll take it easy: newbies,

    welcome! • There’s an advanced session anyway • What’s in this one? 1. These JS things you should know
  22. So you said, “intro...” • We’ll take it easy: newbies,

    welcome! • There’s an advanced session anyway • What’s in this one? 1. These JS things you should know 2. How Prototype extends JavaScript...
  23. So you said, “intro...” • We’ll take it easy: newbies,

    welcome! • There’s an advanced session anyway • What’s in this one? 1. These JS things you should know 2. How Prototype extends JavaScript... 3. How it puts the DOM on steroids...
  24. So you said, “intro...” • We’ll take it easy: newbies,

    welcome! • There’s an advanced session anyway • What’s in this one? 1. These JS things you should know 2. How Prototype extends JavaScript... 3. How it puts the DOM on steroids... 4. And how it makes AJAX a snap!
  25. JavaScript stuff you’d better know

  26. Everything is an object var speaker = { name: 'Christophe',

    age: 29, location: 'Paris' };
  27. Everything is an object var speaker = { name: 'Christophe',

    age: 29, location: 'Paris' }; person['name'] // 'Christophe'
  28. Everything is an object var speaker = { name: 'Christophe',

    age: 29, location: 'Paris' }; person['name'] // 'Christophe' person['name'] = 'Mislav';
  29. Everything is an object var speaker = { name: 'Christophe',

    age: 29, location: 'Paris' }; person['name'] // 'Christophe' person['name'] = 'Mislav'; person['name'] // 'Mislav'
  30. Everything is an object var speaker = { name: 'Christophe',

    age: 29, location: 'Paris' }; person['name'] // 'Christophe' person['name'] = 'Mislav'; person['name'] // 'Mislav' person.name // 'Mislav'
  31. Everything is an object var speaker = { name: 'Christophe',

    age: 29, location: 'Paris' }; person['name'] // 'Christophe' person['name'] = 'Mislav'; person['name'] // 'Mislav' person.name // 'Mislav' person.name = 'Tobie';
  32. Everything is an object var speaker = { name: 'Christophe',

    age: 29, location: 'Paris' }; person['name'] // 'Christophe' person['name'] = 'Mislav'; person['name'] // 'Mislav' person.name // 'Mislav' person.name = 'Tobie'; person.name // 'Tobie'
  33. Arrays are not associative

  34. Arrays are not associative var data = ['mark', 'john', 'susie'];

  35. Arrays are not associative var data = ['mark', 'john', 'susie'];

    for (var name in data) alert(name);
  36. Arrays are not associative • for...in iterates over all the

    properties! var data = ['mark', 'john', 'susie']; for (var name in data) alert(name); ✘
  37. Arrays are not associative • for...in iterates over all the

    properties! ‣ Includes all of Prototype’s extensions! var data = ['mark', 'john', 'susie']; for (var name in data) alert(name); ✘
  38. Arrays are not associative • for...in iterates over all the

    properties! ‣ Includes all of Prototype’s extensions! var data = ['mark', 'john', 'susie']; for (var name in data) alert(name); ✘ var data = ['mark', 'john', 'susie'];
  39. Arrays are not associative • for...in iterates over all the

    properties! ‣ Includes all of Prototype’s extensions! var data = ['mark', 'john', 'susie']; for (var name in data) alert(name); ✘ var data = ['mark', 'john', 'susie']; for (var index = 0, l = data.length; index < l; ++index) alert(data[index]);
  40. Arrays are not associative • for...in iterates over all the

    properties! ‣ Includes all of Prototype’s extensions! var data = ['mark', 'john', 'susie']; for (var name in data) alert(name); ✘ var data = ['mark', 'john', 'susie']; for (var index = 0, l = data.length; index < l; ++index) alert(data[index]); data.each(alert);
  41. Functions are objects, too. var personToString = function(person) { return

    person.name + ', ' + person.age + ' (' + person.location + ')'; } They’re values like any other:
  42. Functions are objects, too. var personToString = function(person) { return

    person.name + ', ' + person.age + ' (' + person.location + ')'; } var speaker = { name: 'Christophe', age: 29, location: 'Paris' }; speaker.toString = function() { return this.name + ', ' + this.age + ' (' + this.location + ')'; }; They’re values like any other: Same goes for methods:
  43. Functions as constructors function Person(name, age, location) { this.name =

    name; this.age = age; this.location = location; this.toString = function() { /*...*/ } } Just initialize your fields using this:
  44. Functions as constructors function Person(name, age, location) { this.name =

    name; this.age = age; this.location = location; this.toString = function() { /*...*/ } } var speaker = new Person('Christophe', 29, 'Paris'); speaker.name // 'Christophe' speaker.toString() // 'Christophe, 29 (Paris)' Just initialize your fields using this: And invoke using new:
  45. The prototype property

  46. The prototype property • this.method = ... quickly get tedious

  47. The prototype property • this.method = ... quickly get tedious

    ‣ The constructors get overweight
  48. The prototype property • this.method = ... quickly get tedious

    ‣ The constructors get overweight ‣ It’s all actually singleton methods!
  49. The prototype property • this.method = ... quickly get tedious

    ‣ The constructors get overweight ‣ It’s all actually singleton methods! ‣ Save it for private/privileged methods
  50. The prototype property • this.method = ... quickly get tedious

    ‣ The constructors get overweight ‣ It’s all actually singleton methods! ‣ Save it for private/privileged methods • prototype: all instances rely on it
  51. The prototype property • this.method = ... quickly get tedious

    ‣ The constructors get overweight ‣ It’s all actually singleton methods! ‣ Save it for private/privileged methods • prototype: all instances rely on it ‣ Dynamic lookup: not just later instantiations!
  52. The prototype property • this.method = ... quickly get tedious

    ‣ The constructors get overweight ‣ It’s all actually singleton methods! ‣ Save it for private/privileged methods • prototype: all instances rely on it ‣ Dynamic lookup: not just later instantiations! ‣ The class’ repository of public methods
  53. The prototype property

  54. The prototype property function Person(name, age, location) { ... }

  55. The prototype property function Person(name, age, location) { ... }

    var sam = new Person('Sam', 23, 'Chicago');
  56. The prototype property function Person(name, age, location) { ... }

    var sam = new Person('Sam', 23, 'Chicago'); Person.prototype.toString = function() { return this.name + ', ' + this.age + ' (' + this.location + ')'; }
  57. The prototype property function Person(name, age, location) { ... }

    var sam = new Person('Sam', 23, 'Chicago'); Person.prototype.toString = function() { return this.name + ', ' + this.age + ' (' + this.location + ')'; } var tdd = new Person('Christophe', 29, 'Paris');
  58. The prototype property function Person(name, age, location) { ... }

    var sam = new Person('Sam', 23, 'Chicago'); Person.prototype.toString = function() { return this.name + ', ' + this.age + ' (' + this.location + ')'; } var tdd = new Person('Christophe', 29, 'Paris'); tdd.toString() // 'Christophe, 29 (Paris)'
  59. The prototype property function Person(name, age, location) { ... }

    var sam = new Person('Sam', 23, 'Chicago'); Person.prototype.toString = function() { return this.name + ', ' + this.age + ' (' + this.location + ')'; } var tdd = new Person('Christophe', 29, 'Paris'); tdd.toString() // 'Christophe, 29 (Paris)' sam.toString() // 'Sam, 23 (Chicago)'
  60. Extending JavaScript

  61. What could be improved?

  62. What could be improved? • So much stuff (no offense,

    Brendan!)
  63. What could be improved? • So much stuff (no offense,

    Brendan!) • Arrays, collections in general, iterators...
  64. What could be improved? • So much stuff (no offense,

    Brendan!) • Arrays, collections in general, iterators... • Closures
  65. What could be improved? • So much stuff (no offense,

    Brendan!) • Arrays, collections in general, iterators... • Closures • Comprehensions
  66. What could be improved? • So much stuff (no offense,

    Brendan!) • Arrays, collections in general, iterators... • Closures • Comprehensions • Dynamic “inheritance,” method overriding
  67. What could be improved? • So much stuff (no offense,

    Brendan!) • Arrays, collections in general, iterators... • Closures • Comprehensions • Dynamic “inheritance,” method overriding • ...and more!
  68. What do we improve?

  69. What do we improve? • We beef up the basics:

    Array, String, Number, Function...
  70. What do we improve? • We beef up the basics:

    Array, String, Number, Function... • We unify event handling
  71. What do we improve? • We beef up the basics:

    Array, String, Number, Function... • We unify event handling • Sure, JS 1.7-2.0 introduce a ton of cool
  72. What do we improve? • We beef up the basics:

    Array, String, Number, Function... • We unify event handling • Sure, JS 1.7-2.0 introduce a ton of cool ‣ We try not to trump native stuff...
  73. What do we improve? • We beef up the basics:

    Array, String, Number, Function... • We unify event handling • Sure, JS 1.7-2.0 introduce a ton of cool ‣ We try not to trump native stuff... ‣ ...but not everybody has Firefox 3!
  74. What do we improve? • We beef up the basics:

    Array, String, Number, Function... • We unify event handling • Sure, JS 1.7-2.0 introduce a ton of cool ‣ We try not to trump native stuff... ‣ ...but not everybody has Firefox 3! ‣ ...and the syntax doesn’t always honor POLS ;)
  75. Stretching your Strings

  76. Stretching your Strings // 29 new methods. Here are just

    a few...
  77. Stretching your Strings // 29 new methods. Here are just

    a few... 'Prototype'.include('to') // true
  78. Stretching your Strings // 29 new methods. Here are just

    a few... 'Prototype'.include('to') // true 'Prototype'.startsWith('Active') // false
  79. Stretching your Strings // 29 new methods. Here are just

    a few... 'Prototype'.include('to') // true 'Prototype'.startsWith('Active') // false 'Prototype'.endsWith('type') // true
  80. Stretching your Strings // 29 new methods. Here are just

    a few... 'Prototype'.include('to') // true 'Prototype'.startsWith('Active') // false 'Prototype'.endsWith('type') // true 'Ho! '.times(3).strip() // 'Ho! Ho! Ho!'
  81. Stretching your Strings // 29 new methods. Here are just

    a few... 'Prototype'.include('to') // true 'Prototype'.startsWith('Active') // false 'Prototype'.endsWith('type') // true 'Ho! '.times(3).strip() // 'Ho! Ho! Ho!' 'x > 0 && x < 100'.escapeHTML() // 'x &gt; 0 &amp;&amp; x &lt; 100'
  82. Stretching your Strings // 29 new methods. Here are just

    a few... 'Prototype'.include('to') // true 'Prototype'.startsWith('Active') // false 'Prototype'.endsWith('type') // true 'Ho! '.times(3).strip() // 'Ho! Ho! Ho!' 'x > 0 && x < 100'.escapeHTML() // 'x &gt; 0 &amp;&amp; x &lt; 100' 'Extending JavaScript'.gsub(/([aeiouy])([^aeiouy]{2})/i, '#{2}#{1}')
  83. Stretching your Strings // 29 new methods. Here are just

    a few... 'Prototype'.include('to') // true 'Prototype'.startsWith('Active') // false 'Prototype'.endsWith('type') // true 'Ho! '.times(3).strip() // 'Ho! Ho! Ho!' 'x > 0 && x < 100'.escapeHTML() // 'x &gt; 0 &amp;&amp; x &lt; 100' 'Extending JavaScript'.gsub(/([aeiouy])([^aeiouy]{2})/i, '#{2}#{1}') // 'xtEndengi JavScarpti'
  84. Stretching your Strings // 29 new methods. Here are just

    a few... 'Prototype'.include('to') // true 'Prototype'.startsWith('Active') // false 'Prototype'.endsWith('type') // true 'Ho! '.times(3).strip() // 'Ho! Ho! Ho!' 'x > 0 && x < 100'.escapeHTML() // 'x &gt; 0 &amp;&amp; x &lt; 100' 'Extending JavaScript'.gsub(/([aeiouy])([^aeiouy]{2})/i, '#{2}#{1}') // 'xtEndengi JavScarpti' 'the ajax experience'.gsub(/\w+/, function(match) { return match[0].capitalize(); })
  85. Stretching your Strings // 29 new methods. Here are just

    a few... 'Prototype'.include('to') // true 'Prototype'.startsWith('Active') // false 'Prototype'.endsWith('type') // true 'Ho! '.times(3).strip() // 'Ho! Ho! Ho!' 'x > 0 && x < 100'.escapeHTML() // 'x &gt; 0 &amp;&amp; x &lt; 100' 'Extending JavaScript'.gsub(/([aeiouy])([^aeiouy]{2})/i, '#{2}#{1}') // 'xtEndengi JavScarpti' 'the ajax experience'.gsub(/\w+/, function(match) { return match[0].capitalize(); }) // 'The Ajax Experience'
  86. Arrays as über-collections

  87. Arrays as über-collections // 34 new methods, 19 of which

    from Enumerable (4 slides away)...
  88. Arrays as über-collections // 34 new methods, 19 of which

    from Enumerable (4 slides away)... [1, 2, 3].first() // 3
  89. Arrays as über-collections // 34 new methods, 19 of which

    from Enumerable (4 slides away)... [1, 2, 3].first() // 3 [4, 5, 6].last() // 6
  90. Arrays as über-collections // 34 new methods, 19 of which

    from Enumerable (4 slides away)... [1, 2, 3].first() // 3 [4, 5, 6].last() // 6 [7, 8, 9].indexOf(8) // 1
  91. Arrays as über-collections // 34 new methods, 19 of which

    from Enumerable (4 slides away)... [1, 2, 3].first() // 3 [4, 5, 6].last() // 6 [7, 8, 9].indexOf(8) // 1 [1, [2, 3], [4, [5], []]].flatten() // [1, 2, 3, 4, 5]
  92. Arrays as über-collections // 34 new methods, 19 of which

    from Enumerable (4 slides away)... [1, 2, 3].first() // 3 [4, 5, 6].last() // 6 [7, 8, 9].indexOf(8) // 1 [1, [2, 3], [4, [5], []]].flatten() // [1, 2, 3, 4, 5] [1, null, , 2, null, 3].compact() // [1, 2, 3]
  93. Arrays as über-collections // 34 new methods, 19 of which

    from Enumerable (4 slides away)... [1, 2, 3].first() // 3 [4, 5, 6].last() // 6 [7, 8, 9].indexOf(8) // 1 [1, [2, 3], [4, [5], []]].flatten() // [1, 2, 3, 4, 5] [1, null, , 2, null, 3].compact() // [1, 2, 3] [1, 2, 3, 2, 1].without(1) // [2, 3, 2]
  94. Arrays as über-collections // 34 new methods, 19 of which

    from Enumerable (4 slides away)... [1, 2, 3].first() // 3 [4, 5, 6].last() // 6 [7, 8, 9].indexOf(8) // 1 [1, [2, 3], [4, [5], []]].flatten() // [1, 2, 3, 4, 5] [1, null, , 2, null, 3].compact() // [1, 2, 3] [1, 2, 3, 2, 1].without(1) // [2, 3, 2] [1, 2, 3, 3, 4, 2, 5].uniq() // [1, 2, 3, 4, 5]
  95. Arrays as über-collections // 34 new methods, 19 of which

    from Enumerable (4 slides away)... [1, 2, 3].first() // 3 [4, 5, 6].last() // 6 [7, 8, 9].indexOf(8) // 1 [1, [2, 3], [4, [5], []]].flatten() // [1, 2, 3, 4, 5] [1, null, , 2, null, 3].compact() // [1, 2, 3] [1, 2, 3, 2, 1].without(1) // [2, 3, 2] [1, 2, 3, 3, 4, 2, 5].uniq() // [1, 2, 3, 4, 5] function greet() { return 'Hey ' + $A(arguments).first() + '!'; }
  96. Arrays as über-collections // 34 new methods, 19 of which

    from Enumerable (4 slides away)... [1, 2, 3].first() // 3 [4, 5, 6].last() // 6 [7, 8, 9].indexOf(8) // 1 [1, [2, 3], [4, [5], []]].flatten() // [1, 2, 3, 4, 5] [1, null, , 2, null, 3].compact() // [1, 2, 3] [1, 2, 3, 2, 1].without(1) // [2, 3, 2] [1, 2, 3, 3, 4, 2, 5].uniq() // [1, 2, 3, 4, 5] function greet() { return 'Hey ' + $A(arguments).first() + '!'; } greet('Sam', 'Thomas', 'John') // 'Hey Sam!'
  97. Arrays as über-collections // 34 new methods, 19 of which

    from Enumerable (4 slides away)... [1, 2, 3].first() // 3 [4, 5, 6].last() // 6 [7, 8, 9].indexOf(8) // 1 [1, [2, 3], [4, [5], []]].flatten() // [1, 2, 3, 4, 5] [1, null, , 2, null, 3].compact() // [1, 2, 3] [1, 2, 3, 2, 1].without(1) // [2, 3, 2] [1, 2, 3, 3, 4, 2, 5].uniq() // [1, 2, 3, 4, 5] function greet() { return 'Hey ' + $A(arguments).first() + '!'; } greet('Sam', 'Thomas', 'John') // 'Hey Sam!' $w('eenie meenie minie moe') // ['eenie', 'meenie', 'minie', 'moe']
  98. Numbers, the Ruby way

  99. Numbers, the Ruby way (5).times(function() { // Processed 5 times

    })
  100. Numbers, the Ruby way (5).times(function() { // Processed 5 times

    }) 'hello world!'.length.toPaddedString(3, 16) // '00c'
  101. Numbers, the Ruby way (5).times(function() { // Processed 5 times

    }) 'hello world!'.length.toPaddedString(3, 16) // '00c' (221).toColorPart() // 'dd' (2-digit hex)
  102. Numbers, the Ruby way (5).times(function() { // Processed 5 times

    }) 'hello world!'.length.toPaddedString(3, 16) // '00c' (221).toColorPart() // 'dd' (2-digit hex) Math.PI.ceil() // 4
  103. Numbers, the Ruby way (5).times(function() { // Processed 5 times

    }) 'hello world!'.length.toPaddedString(3, 16) // '00c' (221).toColorPart() // 'dd' (2-digit hex) Math.PI.ceil() // 4 (3 - 2 * 4).abs() // 5
  104. A few Function tricks

  105. A few Function tricks function add(x, y) { return x

    + y; }
  106. A few Function tricks function add(x, y) { return x

    + y; } function mul(x, y) { return x * y; }
  107. A few Function tricks function add(x, y) { return x

    + y; } function mul(x, y) { return x * y; } var add4 = add.curry(4);
  108. A few Function tricks function add(x, y) { return x

    + y; } function mul(x, y) { return x * y; } var add4 = add.curry(4); var mul5 = mul.curry(5);
  109. A few Function tricks function add(x, y) { return x

    + y; } function mul(x, y) { return x * y; } var add4 = add.curry(4); var mul5 = mul.curry(5); add4(3) // 7
  110. A few Function tricks function add(x, y) { return x

    + y; } function mul(x, y) { return x * y; } var add4 = add.curry(4); var mul5 = mul.curry(5); add4(3) // 7 mul5(3) // 15
  111. A few Function tricks function add(x, y) { return x

    + y; } function mul(x, y) { return x * y; } var add4 = add.curry(4); var mul5 = mul.curry(5); add4(3) // 7 mul5(3) // 15 function greet(name) { alert('Hey ' + name + '!'); }
  112. A few Function tricks function add(x, y) { return x

    + y; } function mul(x, y) { return x * y; } var add4 = add.curry(4); var mul5 = mul.curry(5); add4(3) // 7 mul5(3) // 15 function greet(name) { alert('Hey ' + name + '!'); } greet.delay(1, 'Mark'); // will greet a second later
  113. A few Function tricks function add(x, y) { return x

    + y; } function mul(x, y) { return x * y; } var add4 = add.curry(4); var mul5 = mul.curry(5); add4(3) // 7 mul5(3) // 15 function greet(name) { alert('Hey ' + name + '!'); } greet.delay(1, 'Mark'); // will greet a second later updateDOMContents();
  114. A few Function tricks function add(x, y) { return x

    + y; } function mul(x, y) { return x * y; } var add4 = add.curry(4); var mul5 = mul.curry(5); add4(3) // 7 mul5(3) // 15 function greet(name) { alert('Hey ' + name + '!'); } greet.delay(1, 'Mark'); // will greet a second later updateDOMContents(); rebindListeners.defer(); // give a tiny moment to breathe, then go!
  115. New stuff you’ll love

  116. Enumerable

  117. Enumerable Mix it in your type and define _each:

  118. Enumerable Object.extend(Array.prototype, Enumerable); Array.prototype._each = function(iterator) { for (var i

    = 0; i < this.length; i++) iterator(this[i]); }; Mix it in your type and define _each:
  119. Enumerable Object.extend(Array.prototype, Enumerable); Array.prototype._each = function(iterator) { for (var i

    = 0; i < this.length; i++) iterator(this[i]); }; Mix it in your type and define _each: That’s it! Let’s start with iterations:
  120. Enumerable Object.extend(Array.prototype, Enumerable); Array.prototype._each = function(iterator) { for (var i

    = 0; i < this.length; i++) iterator(this[i]); }; Mix it in your type and define _each: That’s it! Let’s start with iterations: var tae = $w('The Ajax Experience');
  121. Enumerable Object.extend(Array.prototype, Enumerable); Array.prototype._each = function(iterator) { for (var i

    = 0; i < this.length; i++) iterator(this[i]); }; Mix it in your type and define _each: That’s it! Let’s start with iterations: var tae = $w('The Ajax Experience'); tae.each(alert);
  122. Enumerable Object.extend(Array.prototype, Enumerable); Array.prototype._each = function(iterator) { for (var i

    = 0; i < this.length; i++) iterator(this[i]); }; Mix it in your type and define _each: That’s it! Let’s start with iterations: var tae = $w('The Ajax Experience'); tae.each(alert); tae.each(function(word, index) { alert(index + ': ' + word); })
  123. Enumerable Object.extend(Array.prototype, Enumerable); Array.prototype._each = function(iterator) { for (var i

    = 0; i < this.length; i++) iterator(this[i]); }; Mix it in your type and define _each: That’s it! Let’s start with iterations: var tae = $w('The Ajax Experience'); tae.each(alert); tae.each(function(word, index) { alert(index + ': ' + word); }) tae.each(function(word, index) { if (index > 1) throw $break; alert(word); });
  124. Enumerable (continued)

  125. Enumerable (continued) var tae = $w('The Ajax Experience');

  126. Enumerable (continued) var tae = $w('The Ajax Experience'); tae.map(function(word) {

    return word[0]; }) // ['T', 'A', 'E']
  127. Enumerable (continued) var tae = $w('The Ajax Experience'); tae.map(function(word) {

    return word[0]; }) // ['T', 'A', 'E'] tae.map(function(word) { return word.length; }) // [3, 4, 10]
  128. Enumerable (continued) var tae = $w('The Ajax Experience'); tae.map(function(word) {

    return word[0]; }) // ['T', 'A', 'E'] tae.map(function(word) { return word.length; }) // [3, 4, 10] tae.pluck('length') // [3, 4, 10]
  129. Enumerable (continued) var tae = $w('The Ajax Experience'); tae.map(function(word) {

    return word[0]; }) // ['T', 'A', 'E'] tae.map(function(word) { return word.length; }) // [3, 4, 10] tae.pluck('length') // [3, 4, 10] tae.pluck(0) // ['T', 'A', 'E']
  130. Enumerable (continued) var tae = $w('The Ajax Experience'); tae.map(function(word) {

    return word[0]; }) // ['T', 'A', 'E'] tae.map(function(word) { return word.length; }) // [3, 4, 10] tae.pluck('length') // [3, 4, 10] tae.pluck(0) // ['T', 'A', 'E'] tae.invoke('gsub', /[aeiouy]+/i, '') // ['Th', 'jx', 'xprnc']
  131. Enumerable (continued) var tae = $w('The Ajax Experience'); tae.map(function(word) {

    return word[0]; }) // ['T', 'A', 'E'] tae.map(function(word) { return word.length; }) // [3, 4, 10] tae.pluck('length') // [3, 4, 10] tae.pluck(0) // ['T', 'A', 'E'] tae.invoke('gsub', /[aeiouy]+/i, '') // ['Th', 'jx', 'xprnc'] tae.max(function(w) { return w.length; }) // 10
  132. Enumerable (continued) var tae = $w('The Ajax Experience'); tae.map(function(word) {

    return word[0]; }) // ['T', 'A', 'E'] tae.map(function(word) { return word.length; }) // [3, 4, 10] tae.pluck('length') // [3, 4, 10] tae.pluck(0) // ['T', 'A', 'E'] tae.invoke('gsub', /[aeiouy]+/i, '') // ['Th', 'jx', 'xprnc'] tae.max(function(w) { return w.length; }) // 10 tae.zip(tae.pluck('length')).invoke('reverse')
  133. Enumerable (continued) var tae = $w('The Ajax Experience'); tae.map(function(word) {

    return word[0]; }) // ['T', 'A', 'E'] tae.map(function(word) { return word.length; }) // [3, 4, 10] tae.pluck('length') // [3, 4, 10] tae.pluck(0) // ['T', 'A', 'E'] tae.invoke('gsub', /[aeiouy]+/i, '') // ['Th', 'jx', 'xprnc'] tae.max(function(w) { return w.length; }) // 10 tae.zip(tae.pluck('length')).invoke('reverse') // [[3, 'The'], [4, 'Ajax'], [10, 'Experience']]
  134. Enumerable (continued) var tae = $w('The Ajax Experience'); tae.map(function(word) {

    return word[0]; }) // ['T', 'A', 'E'] tae.map(function(word) { return word.length; }) // [3, 4, 10] tae.pluck('length') // [3, 4, 10] tae.pluck(0) // ['T', 'A', 'E'] tae.invoke('gsub', /[aeiouy]+/i, '') // ['Th', 'jx', 'xprnc'] tae.max(function(w) { return w.length; }) // 10 tae.zip(tae.pluck('length')).invoke('reverse') // [[3, 'The'], [4, 'Ajax'], [10, 'Experience']] tae.inject('', function(acc, s) { return acc + s[0]; })
  135. Enumerable (continued) var tae = $w('The Ajax Experience'); tae.map(function(word) {

    return word[0]; }) // ['T', 'A', 'E'] tae.map(function(word) { return word.length; }) // [3, 4, 10] tae.pluck('length') // [3, 4, 10] tae.pluck(0) // ['T', 'A', 'E'] tae.invoke('gsub', /[aeiouy]+/i, '') // ['Th', 'jx', 'xprnc'] tae.max(function(w) { return w.length; }) // 10 tae.zip(tae.pluck('length')).invoke('reverse') // [[3, 'The'], [4, 'Ajax'], [10, 'Experience']] tae.inject('', function(acc, s) { return acc + s[0]; }) // 'TAE'
  136. Text interpolation

  137. Text interpolation var tpl = new Template( '#{name} is #{age}

    and lives in #{location}');
  138. Text interpolation var tpl = new Template( '#{name} is #{age}

    and lives in #{location}'); tpl.evaluate({ name: 'Christophe', age: 29, location: 'Paris' })
  139. Text interpolation var tpl = new Template( '#{name} is #{age}

    and lives in #{location}'); tpl.evaluate({ name: 'Christophe', age: 29, location: 'Paris' }) // 'Christophe is 29 and lives in Paris'
  140. Text interpolation var tpl = new Template( '#{name} is #{age}

    and lives in #{location}'); tpl.evaluate({ name: 'Christophe', age: 29, location: 'Paris' }) // 'Christophe is 29 and lives in Paris' var mislav = { name: 'Mislav', age: 23, location: 'Zagreb' };
  141. Text interpolation var tpl = new Template( '#{name} is #{age}

    and lives in #{location}'); tpl.evaluate({ name: 'Christophe', age: 29, location: 'Paris' }) // 'Christophe is 29 and lives in Paris' var mislav = { name: 'Mislav', age: 23, location: 'Zagreb' }; tpl.evaluate(mislav)
  142. Text interpolation var tpl = new Template( '#{name} is #{age}

    and lives in #{location}'); tpl.evaluate({ name: 'Christophe', age: 29, location: 'Paris' }) // 'Christophe is 29 and lives in Paris' var mislav = { name: 'Mislav', age: 23, location: 'Zagreb' }; tpl.evaluate(mislav) // 'Mislav is 23 and lives in Zagreb'
  143. Text interpolation var tpl = new Template( '#{name} is #{age}

    and lives in #{location}'); tpl.evaluate({ name: 'Christophe', age: 29, location: 'Paris' }) // 'Christophe is 29 and lives in Paris' var mislav = { name: 'Mislav', age: 23, location: 'Zagreb' }; tpl.evaluate(mislav) // 'Mislav is 23 and lives in Zagreb' // Pending commit to trunk: '#{name} is #{age} and lives in #{location}'.interpolate(mislav)
  144. Text interpolation var tpl = new Template( '#{name} is #{age}

    and lives in #{location}'); tpl.evaluate({ name: 'Christophe', age: 29, location: 'Paris' }) // 'Christophe is 29 and lives in Paris' var mislav = { name: 'Mislav', age: 23, location: 'Zagreb' }; tpl.evaluate(mislav) // 'Mislav is 23 and lives in Zagreb' // Pending commit to trunk: '#{name} is #{age} and lives in #{location}'.interpolate(mislav) // 'Mislav is 23 and lives in Zagreb'
  145. Ranges

  146. Ranges new ObjectRange(1, 10).toArray() // [1, 2, 3, 4, 5,

    6, 7, 8, 9, 10]
  147. Ranges new ObjectRange(1, 10).toArray() // [1, 2, 3, 4, 5,

    6, 7, 8, 9, 10] $A($R(1, 10)) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  148. Ranges new ObjectRange(1, 10).toArray() // [1, 2, 3, 4, 5,

    6, 7, 8, 9, 10] $A($R(1, 10)) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] $R(1, 15).inject(0, function(acc, n) { return acc + n; })
  149. Ranges new ObjectRange(1, 10).toArray() // [1, 2, 3, 4, 5,

    6, 7, 8, 9, 10] $A($R(1, 10)) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] $R(1, 15).inject(0, function(acc, n) { return acc + n; }) // 120
  150. Ranges new ObjectRange(1, 10).toArray() // [1, 2, 3, 4, 5,

    6, 7, 8, 9, 10] $A($R(1, 10)) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] $R(1, 15).inject(0, function(acc, n) { return acc + n; }) // 120 $A($R('a', 'm'))
  151. Ranges new ObjectRange(1, 10).toArray() // [1, 2, 3, 4, 5,

    6, 7, 8, 9, 10] $A($R(1, 10)) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] $R(1, 15).inject(0, function(acc, n) { return acc + n; }) // 120 $A($R('a', 'm')) // ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm']
  152. Ranges new ObjectRange(1, 10).toArray() // [1, 2, 3, 4, 5,

    6, 7, 8, 9, 10] $A($R(1, 10)) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] $R(1, 15).inject(0, function(acc, n) { return acc + n; }) // 120 $A($R('a', 'm')) // ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'] $R(1, 5, true).map(function(n) { return '*'.times(n); })
  153. Ranges new ObjectRange(1, 10).toArray() // [1, 2, 3, 4, 5,

    6, 7, 8, 9, 10] $A($R(1, 10)) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] $R(1, 15).inject(0, function(acc, n) { return acc + n; }) // 120 $A($R('a', 'm')) // ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'] $R(1, 5, true).map(function(n) { return '*'.times(n); }) // ['*', '**', '***', '****']
  154. The DOM is fun again!

  155. What’s not fun about it?

  156. What’s not fun about it? • Is that a trick

    question?
  157. What’s not fun about it? • Is that a trick

    question? ‣ Selecting elements for processing
  158. What’s not fun about it? • Is that a trick

    question? ‣ Selecting elements for processing ‣ Traversing it (don’t you hate indent nodes?)
  159. What’s not fun about it? • Is that a trick

    question? ‣ Selecting elements for processing ‣ Traversing it (don’t you hate indent nodes?) ‣ Performing common manipulations (e.g. class tweaking, visibility toggling)
  160. What’s not fun about it? • Is that a trick

    question? ‣ Selecting elements for processing ‣ Traversing it (don’t you hate indent nodes?) ‣ Performing common manipulations (e.g. class tweaking, visibility toggling) ‣ Cross-browser incompatibilities (e.g. attribute reading/writing, style tweaking)
  161. What’s not fun about it? • Is that a trick

    question? ‣ Selecting elements for processing ‣ Traversing it (don’t you hate indent nodes?) ‣ Performing common manipulations (e.g. class tweaking, visibility toggling) ‣ Cross-browser incompatibilities (e.g. attribute reading/writing, style tweaking) ‣ Content changes (update, replace, etc.)
  162. W3C DOM Level 2 HTML HTMLDocument HTMLElement HTMLHtmlElement HTMLHeadElement HTMLLinkElement

    HTMLTitleElement HTMLMetaElement HTMLBaseElement HTMLIsIndexElement HTMLStyleElement HTMLBodyElement HTMLFormElement HTMLSelectElement HTMLOptGroupElement HTMLOptionElement HTMLInputElement HTMLTextAreaElement HTMLButtonElement HTMLLabelElement HTMLFieldSetElement HTMLLegendElement HTMLUListElement HTMLOListElement HTMLDListElement HTMLDirectoryElement HTMLMenuElement HTMLLIElement HTMLDivElement HTMLParagraphElement HTMLHeadingElement HTMLQuoteElement HTMLPreElement HTMLBRElement HTMLBaseFontElement HTMLFontElement HTMLHRElement HTMLModElement HTMLAnchorElement HTMLImageElement HTMLObjectElement HTMLParamElement HTMLAppletElement HTMLMapElement HTMLAreaElement HTMLScriptElement HTMLTableElement HTMLTableCaptionElement HTMLTableColElement HTMLTableSectionElement HTMLTableRowElement HTMLTableCellElement HTMLFrameSetElement HTMLFrameElement HTMLIFrameElement
  163. W3C DOM Level 2 HTML HTMLDocument HTMLElement HTMLHtmlElement HTMLHeadElement HTMLLinkElement

    HTMLTitleElement HTMLMetaElement HTMLBaseElement HTMLIsIndexElement HTMLStyleElement HTMLBodyElement HTMLFormElement HTMLSelectElement HTMLOptGroupElement HTMLOptionElement HTMLInputElement HTMLTextAreaElement HTMLButtonElement HTMLLabelElement HTMLFieldSetElement HTMLLegendElement HTMLUListElement HTMLOListElement HTMLDListElement HTMLDirectoryElement HTMLMenuElement HTMLLIElement HTMLDivElement HTMLParagraphElement HTMLHeadingElement HTMLQuoteElement HTMLPreElement HTMLBRElement HTMLBaseFontElement HTMLFontElement HTMLHRElement HTMLModElement HTMLAnchorElement HTMLImageElement HTMLObjectElement HTMLParamElement HTMLAppletElement HTMLMapElement HTMLAreaElement HTMLScriptElement HTMLTableElement HTMLTableCaptionElement HTMLTableColElement HTMLTableSectionElement HTMLTableRowElement HTMLTableCellElement HTMLFrameSetElement HTMLFrameElement HTMLIFrameElement ✘ Yeah, right.
  164. DOM-related prototypes HTMLElement. prototype HTML*Element. prototype

  165. DOM-related prototypes HTMLElement. prototype HTML*Element. prototype ✕ ✕

  166. DOM-related prototypes HTMLElement. prototype HTML*Element. prototype ✕ ✕ ✓ ✕

  167. DOM-related prototypes HTMLElement. prototype HTML*Element. prototype ✕ ✕ ✓ ✕

    ✓ ✓
  168. DOM-related prototypes HTMLElement. prototype HTML*Element. prototype ✕ ✕ ✓ ✕

    ✓ ✓ ✓ ✕
  169. DOM-related prototypes HTMLElement. prototype HTML*Element. prototype ✕ ✕ ✓ ✕

    ✓ ✓ ✓ ✕ ✓ ✓
  170. Element extension

  171. Element extension • Element.extend(yourElement)

  172. Element extension • Element.extend(yourElement) ‣ Adds all of Element.Methods (~60

    methods)
  173. Element extension • Element.extend(yourElement) ‣ Adds all of Element.Methods (~60

    methods) ‣ Take advantage of prototypes when available
  174. Element extension • Element.extend(yourElement) ‣ Adds all of Element.Methods (~60

    methods) ‣ Take advantage of prototypes when available ‣ Adds tag-specific extensions (e.g. form elements)
  175. Element extension • Element.extend(yourElement) ‣ Adds all of Element.Methods (~60

    methods) ‣ Take advantage of prototypes when available ‣ Adds tag-specific extensions (e.g. form elements) • Most of Prototype extends implicitly ‣ $, $$, all extended methods’ results
  176. The famous $ function

  177. The famous $ function • Most common need: grab an

    element by ID
  178. The famous $ function • Most common need: grab an

    element by ID • Runner-ups:
  179. The famous $ function • Most common need: grab an

    element by ID • Runner-ups: ‣ Grab many elements by their ID’s
  180. The famous $ function • Most common need: grab an

    element by ID • Runner-ups: ‣ Grab many elements by their ID’s ‣ Make sure an element is equipped with Prototype’s DOM extensions
  181. The famous $ function • Most common need: grab an

    element by ID • Runner-ups: ‣ Grab many elements by their ID’s ‣ Make sure an element is equipped with Prototype’s DOM extensions • Enter $(...)
  182. The famous $ function <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p>

    <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  183. The famous $ function $('article1') <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah

    blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  184. The famous $ function $('article1') // <div id="article1"> <body> <div

    id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  185. The famous $ function $('article1') // <div id="article1"> $(firstDiv) <body>

    <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  186. The famous $ function $('article1') // <div id="article1"> $(firstDiv) //

    firstDiv, with guaranteed extensions <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  187. The famous $ function $('article1') // <div id="article1"> $(firstDiv) //

    firstDiv, with guaranteed extensions $('article1', 'foobar', 'comment101') <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  188. The famous $ function $('article1') // <div id="article1"> $(firstDiv) //

    firstDiv, with guaranteed extensions $('article1', 'foobar', 'comment101') // [firstDiv, null, firstCommentDiv] <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  189. The famous $ function $('article1') // <div id="article1"> $(firstDiv) //

    firstDiv, with guaranteed extensions $('article1', 'foobar', 'comment101') // [firstDiv, null, firstCommentDiv] $('article1', 'foobar', 'comment101').compact() <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  190. The famous $ function $('article1') // <div id="article1"> $(firstDiv) //

    firstDiv, with guaranteed extensions $('article1', 'foobar', 'comment101') // [firstDiv, null, firstCommentDiv] $('article1', 'foobar', 'comment101').compact() // [firstDiv, firstCommentDiv] <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  191. The famous $ function $('article1') // <div id="article1"> $(firstDiv) //

    firstDiv, with guaranteed extensions $('article1', 'foobar', 'comment101') // [firstDiv, null, firstCommentDiv] $('article1', 'foobar', 'comment101').compact() // [firstDiv, firstCommentDiv] $(firstDiv, 'article1', firstCommentDiv).uniq() <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  192. The famous $ function $('article1') // <div id="article1"> $(firstDiv) //

    firstDiv, with guaranteed extensions $('article1', 'foobar', 'comment101') // [firstDiv, null, firstCommentDiv] $('article1', 'foobar', 'comment101').compact() // [firstDiv, firstCommentDiv] $(firstDiv, 'article1', firstCommentDiv).uniq() // [firstDiv, firstCommentDiv] <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  193. $$ (The Almighty One)

  194. $$ (The Almighty One) • Relies on CSS3 selectors

  195. $$ (The Almighty One) • Relies on CSS3 selectors •

    Independent of your browser’s CSS3!
  196. $$ (The Almighty One) • Relies on CSS3 selectors •

    Independent of your browser’s CSS3! • Super-fast with DOM3 XPath
  197. $$ (The Almighty One) • Relies on CSS3 selectors •

    Independent of your browser’s CSS3! • Super-fast with DOM3 XPath • Very powerful attributes, classes, ID’s, hierarchy, position, tags/types, and more...
  198. $$ (The Almighty One) <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p>

    <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  199. $$ (The Almighty One) $$('div') <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah

    blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  200. $$ (The Almighty One) $$('div') // #article1, .comments, #comment101, //

    #article2, .comments, #comment201, // #comment202 <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  201. $$ (The Almighty One) $$('div') // #article1, .comments, #comment101, //

    #article2, .comments, #comment201, // #comment202 $$('div.comments') <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  202. $$ (The Almighty One) $$('div') // #article1, .comments, #comment101, //

    #article2, .comments, #comment201, // #comment202 $$('div.comments') // .comments, .comments <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  203. $$ (The Almighty One) $$('div') // #article1, .comments, #comment101, //

    #article2, .comments, #comment201, // #comment202 $$('div.comments') // .comments, .comments $$('h2 + p') <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  204. $$ (The Almighty One) $$('div') // #article1, .comments, #comment101, //

    #article2, .comments, #comment201, // #comment202 $$('div.comments') // .comments, .comments $$('h2 + p') // <p>Blah blah</p>, <p>Yada yada yada</p> <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  205. $$ (The Almighty One) $$('div') // #article1, .comments, #comment101, //

    #article2, .comments, #comment201, // #comment202 $$('div.comments') // .comments, .comments $$('h2 + p') // <p>Blah blah</p>, <p>Yada yada yada</p> $$('div.comments > div[id$="01"] > h3') <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  206. $$ (The Almighty One) $$('div') // #article1, .comments, #comment101, //

    #article2, .comments, #comment201, // #comment202 $$('div.comments') // .comments, .comments $$('h2 + p') // <p>Blah blah</p>, <p>Yada yada yada</p> $$('div.comments > div[id$="01"] > h3') // h3, h3 <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  207. $$ (The Almighty One) $$('div') // #article1, .comments, #comment101, //

    #article2, .comments, #comment201, // #comment202 $$('div.comments') // .comments, .comments $$('h2 + p') // <p>Blah blah</p>, <p>Yada yada yada</p> $$('div.comments > div[id$="01"] > h3') // h3, h3 $$('div:nth-child(2)') <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  208. $$ (The Almighty One) $$('div') // #article1, .comments, #comment101, //

    #article2, .comments, #comment201, // #comment202 $$('div.comments') // .comments, .comments $$('h2 + p') // <p>Blah blah</p>, <p>Yada yada yada</p> $$('div.comments > div[id$="01"] > h3') // h3, h3 $$('div:nth-child(2)') // #comment202 <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  209. Traversing and aggregating <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div

    class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  210. Traversing and aggregating $('article1').down() // <h2>Speaking!</h2> <body> <div id="article1"> <h2>Speaking!</h2>

    <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  211. Traversing and aggregating $('article1').down() // <h2>Speaking!</h2> $('comment101').up() // div.comments <body>

    <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  212. Traversing and aggregating $('article1').down() // <h2>Speaking!</h2> $('comment101').up() // div.comments $('comment202').previous()

    // #comment201 <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  213. Traversing and aggregating $('article1').down() // <h2>Speaking!</h2> $('comment101').up() // div.comments $('comment202').previous()

    // #comment201 $('article1').next().down(1) // div.comments <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  214. Traversing and aggregating $('article1').down() // <h2>Speaking!</h2> $('comment101').up() // div.comments $('comment202').previous()

    // #comment201 $('article1').next().down(1) // div.comments $('article2').down('h3', 1) <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  215. Traversing and aggregating $('article1').down() // <h2>Speaking!</h2> $('comment101').up() // div.comments $('comment202').previous()

    // #comment201 $('article1').next().down(1) // div.comments $('article2').down('h3', 1) // <h3>John @ 09:37am</h3> <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  216. Traversing and aggregating $('article1').down() // <h2>Speaking!</h2> $('comment101').up() // div.comments $('comment202').previous()

    // #comment201 $('article1').next().down(1) // div.comments $('article2').down('h3', 1) // <h3>John @ 09:37am</h3> $('comment201').ancestors() <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  217. Traversing and aggregating $('article1').down() // <h2>Speaking!</h2> $('comment101').up() // div.comments $('comment202').previous()

    // #comment201 $('article1').next().down(1) // div.comments $('article2').down('h3', 1) // <h3>John @ 09:37am</h3> $('comment201').ancestors() // div.comments, #article2, body <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  218. Traversing and aggregating $('article1').down() // <h2>Speaking!</h2> $('comment101').up() // div.comments $('comment202').previous()

    // #comment201 $('article1').next().down(1) // div.comments $('article2').down('h3', 1) // <h3>John @ 09:37am</h3> $('comment201').ancestors() // div.comments, #article2, body $('article2').down().nextSiblings() <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  219. Traversing and aggregating $('article1').down() // <h2>Speaking!</h2> $('comment101').up() // div.comments $('comment202').previous()

    // #comment201 $('article1').next().down(1) // div.comments $('article2').down('h3', 1) // <h3>John @ 09:37am</h3> $('comment201').ancestors() // div.comments, #article2, body $('article2').down().nextSiblings() // p, div.comments <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  220. Traversing and aggregating $('article1').down() // <h2>Speaking!</h2> $('comment101').up() // div.comments $('comment202').previous()

    // #comment201 $('article1').next().down(1) // div.comments $('article2').down('h3', 1) // <h3>John @ 09:37am</h3> $('comment201').ancestors() // div.comments, #article2, body $('article2').down().nextSiblings() // p, div.comments $('article2').down('p').siblings() <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  221. Traversing and aggregating $('article1').down() // <h2>Speaking!</h2> $('comment101').up() // div.comments $('comment202').previous()

    // #comment201 $('article1').next().down(1) // div.comments $('article2').down('h3', 1) // <h3>John @ 09:37am</h3> $('comment201').ancestors() // div.comments, #article2, body $('article2').down().nextSiblings() // p, div.comments $('article2').down('p').siblings() // h2, div.comments <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  222. Playing with visibility <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div

    class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  223. Playing with visibility var hdr2 = $('article1').down('h2'); <body> <div id="article1">

    <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  224. Playing with visibility var hdr2 = $('article1').down('h2'); hdr2.visible() // true

    <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  225. Playing with visibility var hdr2 = $('article1').down('h2'); hdr2.visible() // true

    hdr2.hide() // <h2 style="display: none"> <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  226. Playing with visibility var hdr2 = $('article1').down('h2'); hdr2.visible() // true

    hdr2.hide() // <h2 style="display: none"> hdr2.visible() // false <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  227. Playing with visibility var hdr2 = $('article1').down('h2'); hdr2.visible() // true

    hdr2.hide() // <h2 style="display: none"> hdr2.visible() // false hdr2.toggle() // <h2> <body> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  228. Playing with attributes <form method="post" action="/signup"> ... <p> <input type="checkbox"

    id="chkTOS" name="tos"/> <label for="chkTOS" accesskey="T">I agree, wholeheartedly, to your wonderful terms of service. Love.</label> </p> ... </form>
  229. Playing with attributes var box = $('chkTOS'); // <input type="checkbox"

    id="chkTOS" .../> <form method="post" action="/signup"> ... <p> <input type="checkbox" id="chkTOS" name="tos"/> <label for="chkTOS" accesskey="T">I agree, wholeheartedly, to your wonderful terms of service. Love.</label> </p> ... </form>
  230. Playing with attributes var box = $('chkTOS'); // <input type="checkbox"

    id="chkTOS" .../> box.readAttribute('checked') <form method="post" action="/signup"> ... <p> <input type="checkbox" id="chkTOS" name="tos"/> <label for="chkTOS" accesskey="T">I agree, wholeheartedly, to your wonderful terms of service. Love.</label> </p> ... </form>
  231. Playing with attributes var box = $('chkTOS'); // <input type="checkbox"

    id="chkTOS" .../> box.readAttribute('checked') // null <form method="post" action="/signup"> ... <p> <input type="checkbox" id="chkTOS" name="tos"/> <label for="chkTOS" accesskey="T">I agree, wholeheartedly, to your wonderful terms of service. Love.</label> </p> ... </form>
  232. Playing with attributes var box = $('chkTOS'); // <input type="checkbox"

    id="chkTOS" .../> box.readAttribute('checked') // null box.writeAttribute('checked', true); <form method="post" action="/signup"> ... <p> <input type="checkbox" id="chkTOS" name="tos"/> <label for="chkTOS" accesskey="T">I agree, wholeheartedly, to your wonderful terms of service. Love.</label> </p> ... </form>
  233. Playing with attributes var box = $('chkTOS'); // <input type="checkbox"

    id="chkTOS" .../> box.readAttribute('checked') // null box.writeAttribute('checked', true); // <input type="checkbox" id="chkTOS" checked="checked" .../> <form method="post" action="/signup"> ... <p> <input type="checkbox" id="chkTOS" name="tos"/> <label for="chkTOS" accesskey="T">I agree, wholeheartedly, to your wonderful terms of service. Love.</label> </p> ... </form>
  234. Playing with attributes var box = $('chkTOS'); // <input type="checkbox"

    id="chkTOS" .../> box.readAttribute('checked') // null box.writeAttribute('checked', true); // <input type="checkbox" id="chkTOS" checked="checked" .../> box.readAttribute('checked') <form method="post" action="/signup"> ... <p> <input type="checkbox" id="chkTOS" name="tos"/> <label for="chkTOS" accesskey="T">I agree, wholeheartedly, to your wonderful terms of service. Love.</label> </p> ... </form>
  235. Playing with attributes var box = $('chkTOS'); // <input type="checkbox"

    id="chkTOS" .../> box.readAttribute('checked') // null box.writeAttribute('checked', true); // <input type="checkbox" id="chkTOS" checked="checked" .../> box.readAttribute('checked') // 'checked' <form method="post" action="/signup"> ... <p> <input type="checkbox" id="chkTOS" name="tos"/> <label for="chkTOS" accesskey="T">I agree, wholeheartedly, to your wonderful terms of service. Love.</label> </p> ... </form>
  236. Playing with attributes var box = $('chkTOS'); // <input type="checkbox"

    id="chkTOS" .../> box.readAttribute('checked') // null box.writeAttribute('checked', true); // <input type="checkbox" id="chkTOS" checked="checked" .../> box.readAttribute('checked') // 'checked' box.writeAttribute('checked', false) <form method="post" action="/signup"> ... <p> <input type="checkbox" id="chkTOS" name="tos"/> <label for="chkTOS" accesskey="T">I agree, wholeheartedly, to your wonderful terms of service. Love.</label> </p> ... </form>
  237. Playing with attributes var box = $('chkTOS'); // <input type="checkbox"

    id="chkTOS" .../> box.readAttribute('checked') // null box.writeAttribute('checked', true); // <input type="checkbox" id="chkTOS" checked="checked" .../> box.readAttribute('checked') // 'checked' box.writeAttribute('checked', false) // <input type="checkbox" id="chkTOS" .../> <form method="post" action="/signup"> ... <p> <input type="checkbox" id="chkTOS" name="tos"/> <label for="chkTOS" accesskey="T">I agree, wholeheartedly, to your wonderful terms of service. Love.</label> </p> ... </form>
  238. Playing with style <style type="text/css"> h2 { font-size: 1.2em; }

    </style> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> </div>
  239. Playing with style var hdr = $('article1').down('h2'); <style type="text/css"> h2

    { font-size: 1.2em; } </style> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> </div>
  240. Playing with style var hdr = $('article1').down('h2'); hdr.getStyle('font-size'); <style type="text/css">

    h2 { font-size: 1.2em; } </style> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> </div>
  241. Playing with style var hdr = $('article1').down('h2'); hdr.getStyle('font-size'); // '1.2em'

    <style type="text/css"> h2 { font-size: 1.2em; } </style> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> </div>
  242. Playing with style var hdr = $('article1').down('h2'); hdr.getStyle('font-size'); // '1.2em'

    hdr.setStyle({ 'font-size': '1.5em' }) <style type="text/css"> h2 { font-size: 1.2em; } </style> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> </div>
  243. Playing with style var hdr = $('article1').down('h2'); hdr.getStyle('font-size'); // '1.2em'

    hdr.setStyle({ 'font-size': '1.5em' }) // <h2 style="font-size: 1.5em"> <style type="text/css"> h2 { font-size: 1.2em; } </style> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> </div>
  244. Playing with style var hdr = $('article1').down('h2'); hdr.getStyle('font-size'); // '1.2em'

    hdr.setStyle({ 'font-size': '1.5em' }) // <h2 style="font-size: 1.5em"> hdr.setStyle({ fontSize: '1.5em', true }) <style type="text/css"> h2 { font-size: 1.2em; } </style> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> </div>
  245. Playing with style var hdr = $('article1').down('h2'); hdr.getStyle('font-size'); // '1.2em'

    hdr.setStyle({ 'font-size': '1.5em' }) // <h2 style="font-size: 1.5em"> hdr.setStyle({ fontSize: '1.5em', true }) // Same, but slightly faster... <style type="text/css"> h2 { font-size: 1.2em; } </style> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> </div>
  246. Playing with style var hdr = $('article1').down('h2'); hdr.getStyle('font-size'); // '1.2em'

    hdr.setStyle({ 'font-size': '1.5em' }) // <h2 style="font-size: 1.5em"> hdr.setStyle({ fontSize: '1.5em', true }) // Same, but slightly faster... hdr.setStyle({ fontSize: null, color: 'red' }) <style type="text/css"> h2 { font-size: 1.2em; } </style> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> </div>
  247. Playing with style var hdr = $('article1').down('h2'); hdr.getStyle('font-size'); // '1.2em'

    hdr.setStyle({ 'font-size': '1.5em' }) // <h2 style="font-size: 1.5em"> hdr.setStyle({ fontSize: '1.5em', true }) // Same, but slightly faster... hdr.setStyle({ fontSize: null, color: 'red' }) // <h2 style="color: red"> <style type="text/css"> h2 { font-size: 1.2em; } </style> <div id="article1"> <h2>Speaking!</h2> <p>Blah blah</p> </div>
  248. Dynamic contents! var hdr = $('article1').down('h2'); hdr.update('<span>TAE!</span>'); <body> <div id="article1">

    <h2>Speaking!</h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  249. Dynamic contents! var hdr = $('article1').down('h2'); hdr.update('<span>TAE!</span>'); <body> <div id="article1">

    <h2><span>TAE!</span></h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  250. Dynamic contents! var c1 = $('comment101'); c1.insert('<p>Yuck indeed!</p>'); <body> <div

    id="article1"> <h2><span>TAE!</span></h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <h3>John @ 12:35pm</h3> <p>yikes!</p> <p>Yuck indeed!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> ...
  251. Dynamic contents! var c1 = $('comment101'); c1.insert('<p>Yuck indeed!</p>'); c1.insert({ top:

    '<p>er, well...</p>' }); <body> <div id="article1"> <h2><span>TAE!</span></h2> <p>Blah blah</p> <div class="comments"> <div id="comment101"> <p>er, well...</p> <h3>John @ 12:35pm</h3> <p>yikes!</p> <p>Yuck indeed!</p> </div> </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> ...
  252. Dynamic contents! var c1 = $('comment101'); c1.insert('<p>Yuck indeed!</p>'); c1.insert({ top:

    '<p>er, well...</p>' }); c1.insert({ before: 'BEFORE', after: 'AFTER' }); <body> <div id="article1"> <h2><span>TAE!</span></h2> <p>Blah blah</p> <div class="comments"> BEFORE <div id="comment101"> <p>er, well...</p> <h3>John @ 12:35pm</h3> <p>yikes!</p> <p>Yuck indeed!</p> </div> AFTER </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> ...
  253. Dynamic contents! var c1 = $('comment101'); c1.insert('<p>Yuck indeed!</p>'); c1.insert({ top:

    '<p>er, well...</p>' }); c1.insert({ before: 'BEFORE', after: 'AFTER' }); c1.replace('<p>--DELETED--</p>'); <body> <div id="article1"> <h2><span>TAE!</span></h2> <p>Blah blah</p> <div class="comments"> BEFORE <p>--DELETED--</p> AFTER </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> </div> </div> </body>
  254. Explicit element creation var warning = new Element('img', { id:

    'warning', src: '/i/warn.png', title: 'Watch out!' }); $(document.body).insert({ top: warning }); <body> <img src="/i/warn.png" id="warning" title="Watch out!"/> <div id="article1"> <h2><span>TAE!</span></h2> <p>Blah blah</p> <div class="comments"> BEFORE <p>--DELETED--</p> AFTER </div> </div> <div id="article2"> <h2>Guess what?</h2> <p>Yada yada yada</p> <div class="comments"> <div id="comment201"> <h3>Mark @ 09:20am</h3> <p>cool!</p> </div> <div id="comment202"> <h3>John @ 09:37am</h3> <p>d'oh!</p> </div> ...
  255. Event handling

  256. The problems we face

  257. The problems we face • People keep using events the

    DOM0 way
  258. The problems we face • People keep using events the

    DOM0 way ‣ Inline handlers skip the event object in IE
  259. The problems we face • People keep using events the

    DOM0 way ‣ Inline handlers skip the event object in IE • IE vs. The Rest Of The World
  260. The problems we face • People keep using events the

    DOM0 way ‣ Inline handlers skip the event object in IE • IE vs. The Rest Of The World ‣ (De)registering event handlers
  261. The problems we face • People keep using events the

    DOM0 way ‣ Inline handlers skip the event object in IE • IE vs. The Rest Of The World ‣ (De)registering event handlers ‣ Getting / manipulating the event object
  262. The problems we face • People keep using events the

    DOM0 way ‣ Inline handlers skip the event object in IE • IE vs. The Rest Of The World ‣ (De)registering event handlers ‣ Getting / manipulating the event object ‣ Bubbling & capturing, key events, key codes...
  263. The problems we face • People keep using events the

    DOM0 way ‣ Inline handlers skip the event object in IE • IE vs. The Rest Of The World ‣ (De)registering event handlers ‣ Getting / manipulating the event object ‣ Bubbling & capturing, key events, key codes... • Not just IE: Safari/Opera b0rk sometimes
  264. Unifying the event code

  265. Unifying the event code • (De)registering handlers ‣ Event.observe, Event.stopObserving

  266. Unifying the event code • (De)registering handlers ‣ Event.observe, Event.stopObserving

    • Manipulating the event object ‣ Event.pointer(X|Y), Event.element, Event.stop, Event.KEY_xxx, etc.
  267. Unifying the event code • (De)registering handlers ‣ Event.observe, Event.stopObserving

    • Manipulating the event object ‣ Event.pointer(X|Y), Event.element, Event.stop, Event.KEY_xxx, etc. • Working around the tweaks ‣ e.g. keypress/keydown in Safari, submit...
  268. What it looks like function bindPopupLinks() { $$('a.popup').invoke('observe', 'click', handlePopupLinkClick);

    } function handlePopupLinkClick(e) { Event.stop(e); var lnk = Event.findElement(e, 'a'); var opts = 'width=500,height=400...'; window.open(lnk.href, 'wndPopupLink', opts); } // Prototype 1.6 should emulate DOMContentLoaded properly... Event.observe(window, 'DOMContentLoaded', bindPopupLinks);
  269. What it looks like function bindPopupLinks() { $$('a.popup').invoke('observe', 'click', handlePopupLinkClick);

    } function handlePopupLinkClick(e) { Event.stop(e); var lnk = Event.findElement(e, 'a'); var opts = 'width=500,height=400...'; window.open(lnk.href, 'wndPopupLink', opts); } // Prototype 1.6 should emulate DOMContentLoaded properly... Event.observe(window, 'DOMContentLoaded', bindPopupLinks);
  270. What it looks like function bindPopupLinks() { $$('a.popup').invoke('observe', 'click', handlePopupLinkClick);

    } function handlePopupLinkClick(e) { Event.stop(e); var lnk = Event.findElement(e, 'a'); var opts = 'width=500,height=400...'; window.open(lnk.href, 'wndPopupLink', opts); } // Prototype 1.6 should emulate DOMContentLoaded properly... Event.observe(window, 'DOMContentLoaded', bindPopupLinks);
  271. What it looks like function bindPopupLinks() { $$('a.popup').invoke('observe', 'click', handlePopupLinkClick);

    } function handlePopupLinkClick(e) { Event.stop(e); var lnk = Event.findElement(e, 'a'); var opts = 'width=500,height=400...'; window.open(lnk.href, 'wndPopupLink', opts); } // Prototype 1.6 should emulate DOMContentLoaded properly... Event.observe(window, 'DOMContentLoaded', bindPopupLinks);
  272. What it looks like function bindPopupLinks() { $$('a.popup').invoke('observe', 'click', handlePopupLinkClick);

    } function handlePopupLinkClick(e) { Event.stop(e); var lnk = Event.findElement(e, 'a'); var opts = 'width=500,height=400...'; window.open(lnk.href, 'wndPopupLink', opts); } // Prototype 1.6 should emulate DOMContentLoaded properly... Event.observe(window, 'DOMContentLoaded', bindPopupLinks);
  273. Going AJAX

  274. X = XML?

  275. X = XML? • Yeah, but let’s just drop the

    XML garbage
  276. X = XML? • Yeah, but let’s just drop the

    XML garbage ‣ What’s your cross to bear, SOAP with JS?
  277. X = XML? • Yeah, but let’s just drop the

    XML garbage ‣ What’s your cross to bear, SOAP with JS? • X = XHTML
  278. X = XML? • Yeah, but let’s just drop the

    XML garbage ‣ What’s your cross to bear, SOAP with JS? • X = XHTML ‣ I mean, we all send back XHTML anyway
  279. X = XML? • Yeah, but let’s just drop the

    XML garbage ‣ What’s your cross to bear, SOAP with JS? • X = XHTML ‣ I mean, we all send back XHTML anyway ‣ Unless it’s JSON or plain JS to be eval’d
  280. X = XML? • Yeah, but let’s just drop the

    XML garbage ‣ What’s your cross to bear, SOAP with JS? • X = XHTML ‣ I mean, we all send back XHTML anyway ‣ Unless it’s JSON or plain JS to be eval’d ‣ We’re all doing AHAH, knowing it or not!
  281. Making an AJAX request

  282. Making an AJAX request new Ajax.Request('/articles/1', { method: 'get', onComplete:

    function(xhr) { /* process xhr.responseText */ } });
  283. Making an AJAX request new Ajax.Request('/articles/1', { method: 'get', onComplete:

    function(xhr) { /* process xhr.responseText */ } }); • Plenty of options!
  284. Making an AJAX request new Ajax.Request('/articles/1', { method: 'get', onComplete:

    function(xhr) { /* process xhr.responseText */ } }); • Plenty of options! ‣ Method, callbacks, parameters, headers, encoding...
  285. Making an AJAX request new Ajax.Request('/articles/1', { method: 'get', onComplete:

    function(xhr) { /* process xhr.responseText */ } }); • Plenty of options! ‣ Method, callbacks, parameters, headers, encoding... new Ajax.Request('/signin', { parameters: { login: 'john', password: 'foobar' }, requestHeaders: { 'X-Auth-Mode': 'XHR' }, onFailure: function(xhr) { alert(xhr.responseText); }, onComplete: function(xhr) { $('loginForm').update(xhr.responseText); } });
  286. Returning pure JavaScript? Leave this to me. new Ajax.Request('/persist', {

    parameters: $('form').serialize() });
  287. Returning pure JavaScript? Leave this to me. new Ajax.Request('/persist', {

    parameters: $('form').serialize() }); $('indicator').hide(); $('items').insert('<li id=”item5”>...'); $('item5').highlight.defer(); ...
  288. Returning pure JavaScript? Leave this to me. new Ajax.Request('/persist', {

    parameters: $('form').serialize() }); $('indicator').hide(); $('items').insert('<li id=”item5”>...'); $('item5').highlight.defer(); ... eval(...)
  289. Submitting forms

  290. Submitting forms • At the core of AJAXifying a page...

  291. Submitting forms • At the core of AJAXifying a page...

    ‣ Using the form’s method and action attributes
  292. Submitting forms • At the core of AJAXifying a page...

    ‣ Using the form’s method and action attributes ‣ Serializing it according to HTML spec
  293. Submitting forms • At the core of AJAXifying a page...

    ‣ Using the form’s method and action attributes ‣ Serializing it according to HTML spec ‣ Returning JS? You’re done! Otherwise use onComplete.
  294. Submitting forms • At the core of AJAXifying a page...

    ‣ Using the form’s method and action attributes ‣ Serializing it according to HTML spec ‣ Returning JS? You’re done! Otherwise use onComplete. • In phase with accessibility / unobstrusive JS
  295. Submitting forms

  296. Submitting forms • The HTML:

  297. Submitting forms • The HTML: <form id="signinForm" method="post" action="/signin"> <p><input

    type="text" name="login" value="john"/></p> <p><input type="password" name="password" value="foobar"/></p> <p><input type="submit" value="Sign in!"/></p> </form>
  298. Submitting forms • The HTML: <form id="signinForm" method="post" action="/signin"> <p><input

    type="text" name="login" value="john"/></p> <p><input type="password" name="password" value="foobar"/></p> <p><input type="submit" value="Sign in!"/></p> </form> • The JS:
  299. Submitting forms • The HTML: <form id="signinForm" method="post" action="/signin"> <p><input

    type="text" name="login" value="john"/></p> <p><input type="password" name="password" value="foobar"/></p> <p><input type="submit" value="Sign in!"/></p> </form> • The JS: var form = $('signinForm'); form.observe('submit', function(e) { Event.stop(e); form.request(); });
  300. AJAX returning markup

  301. AJAX returning markup • It’s everywhere!

  302. AJAX returning markup • It’s everywhere! ‣ Using the form’s

    method and action attributes
  303. AJAX returning markup • It’s everywhere! ‣ Using the form’s

    method and action attributes • In essence: container.update(xhr.responseText)
  304. AJAX returning markup • It’s everywhere! ‣ Using the form’s

    method and action attributes • In essence: container.update(xhr.responseText) • Automagically done!
  305. AJAX returning markup • It’s everywhere! ‣ Using the form’s

    method and action attributes • In essence: container.update(xhr.responseText) • Automagically done! ‣ Quite a few cool options (success/failure branching, inserting instead of updating, <script> evaluation)
  306. AJAX returning markup • It’s everywhere! ‣ Using the form’s

    method and action attributes • In essence: container.update(xhr.responseText) • Automagically done! ‣ Quite a few cool options (success/failure branching, inserting instead of updating, <script> evaluation) ‣ Can even be periodical (and slow down if no changes)
  307. AJAX returning markup

  308. AJAX returning markup new Ajax.Updater('container', '/url');

  309. AJAX returning markup new Ajax.Updater('container', '/url'); new Ajax.Updater('todo1', '/todos/1/items', {

    insertion: 'bottom', parameters: $('itemForm').serialize() });
  310. AJAX returning markup new Ajax.Updater('container', '/url'); new Ajax.Updater('todo1', '/todos/1/items', {

    insertion: 'bottom', parameters: $('itemForm').serialize() }); new Ajax.Updater('todo1', '/todos/1', { method: 'get', evalScripts: true, parameters: $('todoForm').serialize() });
  311. AJAX returning markup new Ajax.Updater('container', '/url'); new Ajax.Updater('todo1', '/todos/1/items', {

    insertion: 'bottom', parameters: $('itemForm').serialize() }); new Ajax.Updater('todo1', '/todos/1', { method: 'get', evalScripts: true, parameters: $('todoForm').serialize() }); new Ajax.Updater({ success: 'todo1', failure: 'errors' }, '/todos/1', { parameters: $('todoForm').serialize() });
  312. AJAX returning markup new Ajax.Updater('container', '/url'); new Ajax.Updater('todo1', '/todos/1/items', {

    insertion: 'bottom', parameters: $('itemForm').serialize() }); new Ajax.Updater('todo1', '/todos/1', { method: 'get', evalScripts: true, parameters: $('todoForm').serialize() }); new Ajax.Updater({ success: 'todo1', failure: 'errors' }, '/todos/1', { parameters: $('todoForm').serialize() }); new Ajax.Updater({ success: 'todo1' }, '/todos/1', { parameters: $('todoForm').serialize() });
  313. Progressive enhancement: “Hijax”

  314. Progressive enhancement: “Hijax” • I’ll remind you that your markup

    should be
  315. Progressive enhancement: “Hijax” • I’ll remind you that your markup

    should be ‣ semantical
  316. Progressive enhancement: “Hijax” • I’ll remind you that your markup

    should be ‣ semantical ‣ self-sufficient (works w/o JS)
  317. Progressive enhancement: “Hijax” • I’ll remind you that your markup

    should be ‣ semantical ‣ self-sufficient (works w/o JS) • The recipe is simple:
  318. Progressive enhancement: “Hijax” • I’ll remind you that your markup

    should be ‣ semantical ‣ self-sufficient (works w/o JS) • The recipe is simple: ‣ Plan for AJAX early (controllers/views, etc.)
  319. Progressive enhancement: “Hijax” • I’ll remind you that your markup

    should be ‣ semantical ‣ self-sufficient (works w/o JS) • The recipe is simple: ‣ Plan for AJAX early (controllers/views, etc.) ‣ Implement it when the page works already
  320. Progressive enhancement: “Hijax” • I’ll remind you that your markup

    should be ‣ semantical ‣ self-sufficient (works w/o JS) • The recipe is simple: ‣ Plan for AJAX early (controllers/views, etc.) ‣ Implement it when the page works already ‣ Use unobstrusive JavaScript
  321. But wait! There’s more!

  322. But wait! There’s more! • Next up: Advanced Prototype

  323. But wait! There’s more! • Next up: Advanced Prototype ‣

    More Function-fu
  324. But wait! There’s more! • Next up: Advanced Prototype ‣

    More Function-fu ‣ Best coding practices (e.g. on Enumerable)
  325. But wait! There’s more! • Next up: Advanced Prototype ‣

    More Function-fu ‣ Best coding practices (e.g. on Enumerable) ‣ JSON love
  326. But wait! There’s more! • Next up: Advanced Prototype ‣

    More Function-fu ‣ Best coding practices (e.g. on Enumerable) ‣ JSON love ‣ Whipping up your own DOM extensions
  327. But wait! There’s more! • Next up: Advanced Prototype ‣

    More Function-fu ‣ Best coding practices (e.g. on Enumerable) ‣ JSON love ‣ Whipping up your own DOM extensions ‣ Creating your own classes
  328. But wait! There’s more! • Next up: Advanced Prototype ‣

    More Function-fu ‣ Best coding practices (e.g. on Enumerable) ‣ JSON love ‣ Whipping up your own DOM extensions ‣ Creating your own classes ‣ Sneak peek at Prototype 1.6, and more!
  329. Online resources

  330. Online resources • The excellent documentation site ‣ http://prototypejs.org ‣

    API reference, tutorials, downloads, blog... ‣ Soon: 3rd-party libs roster
  331. Online resources • The excellent documentation site ‣ http://prototypejs.org ‣

    API reference, tutorials, downloads, blog... ‣ Soon: 3rd-party libs roster • The official list (does script.aculo.us too) ‣ http://groups.google.com/rubyonrails-spinoffs
  332. Online resources • The excellent documentation site ‣ http://prototypejs.org ‣

    API reference, tutorials, downloads, blog... ‣ Soon: 3rd-party libs roster • The official list (does script.aculo.us too) ‣ http://groups.google.com/rubyonrails-spinoffs • IRC ‣ #prototype on Freenode
  333. Shameless plug

  334. Shameless plug

  335. Shameless plug • “The Bungee Book”

  336. Shameless plug • “The Bungee Book” • Available already (as

    beta) from the Pragmatic Bookshelf ‣ http://books.pragprog.com/titles/cppsu/
  337. Shameless plug • “The Bungee Book” • Available already (as

    beta) from the Pragmatic Bookshelf ‣ http://books.pragprog.com/titles/cppsu/ • 95% content-complete already
  338. Shameless plug • “The Bungee Book” • Available already (as

    beta) from the Pragmatic Bookshelf ‣ http://books.pragprog.com/titles/cppsu/ • 95% content-complete already • Up-to-date on the latest stuff
  339. Shameless plug • “The Bungee Book” • Available already (as

    beta) from the Pragmatic Bookshelf ‣ http://books.pragprog.com/titles/cppsu/ • 95% content-complete already • Up-to-date on the latest stuff • Pre-order the paper book too!
  340. Q&A

  341. Q&A What Say You?