Time to talk your JavaScript to the next level. Join us as we take a normal jQuery document.ready block and start to break it into objects, use the prototype model, investigate scope, and self-executing JavaScript blocks.
We'll be guided in our journey by Mario and his friends, as well as a full-demo and source code, complete with a poor-man's animation of Luigi kicking ass with a fireball. We'll also run through the basics of Backbone.js and which parts to use and avoid based on your application.
And because you're a PHP developer, we'll see how JavaScript object-oriented principles would look like if they were written in PHP.
http://weaverryan.github.com/php-js-playground/
https://github.com/weaverryan/php-js-playground
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
JavaScript Best Practices, Backbone.js, and Mario for the PHP Develop
1. JAVASCRIPT BEST
PRACTICES & BACKBONE.JS
FOR THE PHP DEVELOPER
Ryan Weaver
@weaverryan
Thursday, May 24, 12
2. 7 tips for writing JavaScript
like a jerk
+
What to take and leave in
Backbone.js
Thursday, May 24, 12
3. Who is this dude?
• Co-author of the Symfony2 Docs
• KnpLabs US - Symfony
consulting, training, Kumbaya
• Writer for KnpUniversity.com
screencasts
• Fiancee of the much more
talented @leannapelham
-----> June 9th, 2012!
http://www.knplabs.com/en
@weaverryan http://www.github.com/weaverryan
Thursday, May 24, 12
11. Let’s kick some arse!
jQuery(document).ready(function() {
var $luigi = $('.event-details .luigi');
$luigi.click(function() {
$(this).addClass('active');
return false;
});
});
Thursday, May 24, 12
12. DEMO!!!!!
http://bit.ly/php-js-demo
0 - The starting point
Thursday, May 24, 12
13. Princess Pro-tip
Instead of listening
to this guy drone on,
just find, fork and
play with the code
yourself!
http://bit.ly/php-js-play
Thursday, May 24, 12
14. ‣ Click either .luigi or .peach
‣ the .luigi wrapper gets the active class
Thursday, May 24, 12
15. Mario’s bad JavaScript tip #1:
Ignore the jQuery “event” object,
it’s probably stupid...
http://bit.ly/php-js-demo
01 - The jQuery Event
@weaverryan
Thursday, May 24, 12
17. Event
Click! 1) You click!
2) An event travels up the tree
Thursday, May 24, 12
18. Browser event
versus
jQuery event
‣ The browser event contains all the
information about what happened
‣the jQuery event cleans up cross-
browser compatibility ugliness
Thursday, May 24, 12
19. $luigi.click(function(event) {
// ...
});
‣ event.target: The actual element that
received the click
‣ event.currentTarget: The element that
this listener was attached to
Thursday, May 24, 12
21. If you click .peach:
event.target: .peach
event.currentTarget: .luigi
Thursday, May 24, 12
22. If you click .luigi:
event.target: .luigi
event.currentTarget: .luigi
Thursday, May 24, 12
23. event.currentTarget
==
this
* unless you screw with scope, which we’ll see!
Thursday, May 24, 12
24. $luigi.click(function(event) {
// these are the same!
$(event.currentTarget)
.addClass('active');
$luigi.addClass('active');
});
Thursday, May 24, 12
25. Preventing the damn
“#” on the URL
$luigi.click(function() {
$(this).addClass('active');
return false;
});
Thursday, May 24, 12
26. Princess Pro-tip
return false stops
propagation, and is a
great way to screw
with your teammate’s
events
http://fuelyourcoding.com/jquery-events-stop-misusing-return-false/
Thursday, May 24, 12
27. ... but if you like your
coworkers...
$luigi.click(function(event) {
event.preventDefault();
// ...
});
The event keeps traveling up
the DOM
Thursday, May 24, 12
28. Bad JavaScript moral #1:
jQuery’s event object is a
dangerous source of useful
information and control
@weaverryan
Thursday, May 24, 12
29. Mario bad JavaScript tip #2:
Avoid using objects: they
threaten to organize your code...
and are creepy...
http://bit.ly/php-js-demo
02 - Basic jQuery Objects
@weaverryan
Thursday, May 24, 12
31. <?php
class MagicBoxes
{
public function initializeClick($wrapper)
{
// ...
}
}
$magicBoxes = new MagicBoxes();
$magicBoxes->initializeClick('something');
Thursday, May 24, 12
33. var MagicBoxes = {
someProperty: 0,
initializeClick: function($container) {
// ...
}
};
MagicBoxes.initializeClick(something);
Thursday, May 24, 12
34. Just like with PHP, you can
choose to organize your code
into objects and functions
@weaverryan
Thursday, May 24, 12
35. var MagicBoxes = {
initializeClick: function($container) {
$container.find('.luigi')
.click(this._handleLuigiClick);
},
_handleLuigiClick: function(event) {
event.preventDefault();
$luigi.addClass('active');
}
};
jQuery(document).ready(function() {
var $wrap = $('.mario-world');
MagicBoxes.initializeClick($wrap);
});
Thursday, May 24, 12
36. Princess Pro-tip
Be careful with
objects, they can
increase readability,
which threatens job
security
Thursday, May 24, 12
37. Objects: Advantages
‣ All of the logic of this “mini-app” is
wrapped up inside a named object
‣ The jQuery document.ready is skinny:
contains simple, descriptive calls to the
object
‣ The object methods are reusable
Thursday, May 24, 12
38. Object Scope
// hola! I’m just a local variable
var MagicBoxes = {
// ...
};
// I’m a global variable, available anywhere
window.MagicBoxes = {
};
Thursday, May 24, 12
39. the “window”
window.foo = 'foo-window';
console.log(foo);
// prints "foo-window"
bar = 'bar-global';
console.log(window.bar);
// prints "bar-global"
Thursday, May 24, 12
40. Bad JavaScript moral #2:
Using global objects risks
organizing your code into
separate, distinct units
@weaverryan
Thursday, May 24, 12
41. Mario’s bad JavaScript tip #3:
JavaScript’s “prototype” object
model is too scary to use
http://bit.ly/php-js-demo
03 - Intro to the JS Prototype
@weaverryan
Thursday, May 24, 12
43. An object with a property
var object1 = {
fooProperty: 'foo!'
}
console.log(object1.fooProperty);
// prints “foo!”
Thursday, May 24, 12
44. Also an object with a property
var alsoAnObject = function() {
return 'foo-return!'
};
alsoAnObject.barProperty = 'bar!';
console.log(alsoAnObject.barProperty);
// prints “bar!”
console.log(alsoAnObject());
// prints “foo-return!”
Thursday, May 24, 12
45. In PHP, we can create a class
and then instantiate many
instances
@weaverryan
Thursday, May 24, 12
46. <?php
class MagicBoxes
{
public function __construct($option)
{
// ...
}
}
$magicBoxes1 = new MagicBoxes('foo');
$magicBoxes2 = new MagicBoxes('bar');
Thursday, May 24, 12
47. How can we do this in
JavaScript?
@weaverryan
Thursday, May 24, 12
48. Imagine if we could do this
craziness in PHP
@weaverryan
Thursday, May 24, 12
49. ImaginationLand PHP code
$magicBox = function($var) {
$this->var = $var;
$this->initialize();
};
$magicBox->prototype->initialize = function() {
var_dump($this->var);
};
$magicBoxObj = new $magicBox('something');
// causes "something" to be printed
Thursday, May 24, 12
50. Yep, that’s how it works in
JavaScript!
@weaverryan
Thursday, May 24, 12
51. #1: Create a function
window.MagicBoxes = function($container) {
this.$el = $container;
this.initialize();
};
Thursday, May 24, 12
52. #2: Add things to your future
object
MagicBoxes.prototype.initialize = function()
{
var $luigi = this.$el.find('.luigi');
$luigi.click(function(event) {
event.preventDefault();
$(this).toggleClass('active');
});
};
Thursday, May 24, 12
53. #2: Add things to your future
object
MagicBoxes.prototype.initialize = function()
The “prototype” is a magic place where
you stick “future” things that will become a
part of the eventual new object
Thursday, May 24, 12
54. #3: Instantiate your new object
jQuery(document).ready(function() {
var $mario = $('.mario-world');
var magicBoxApp = new MagicBoxes($mario);
});
Thursday, May 24, 12
56. Princess Pro-tip
Your speaker is a
liar and a thief.
Using the prototype
is for sissies!
Thursday, May 24, 12
57. Bowser rebuttal
Don’t listen to her!
A gentleman and scholar named
Garrison Locke is going to
teach us the prototypical
object model of JavaScript
tomorrow at 11:30 AM
Thursday, May 24, 12
58. Peach come-back
No you didn’t! Mario and I
both agree that using objects
in JavaScript is all weird!
Viva the 1500 line jQuery
document.ready!
Thursday, May 24, 12
59. Bowser final word
Seriously, this is why I
fight against you guys
Thursday, May 24, 12
60. Bowser final word
Your ignorance may seem
blissful, but you work as a
detriment towards the
larger cause of evolving
and learning as
a community
Thursday, May 24, 12
61. Bowser final word
Thank God Garrison Locke
will be teaching us Object-
oriented JavaScript
tomorrow at 11:30 AM.
Thursday, May 24, 12
62. Bowser final word
He’s a much better speaker
than this guy
Thursday, May 24, 12
63. Ryan does damage control
on his presentation
Dude, I’m right here
Thursday, May 24, 12
64. Purple is a manly color
You really like that
shirt...
Thursday, May 24, 12
65. Bad JavaScript moral #3:
Don’t use jQuery’s prototype or
go to Garrison Locke’s
presentation tomorrow if you
want unreadable JavaScript
@weaverryan
Thursday, May 24, 12
66. Mario’s bad JavaScript tip #4:
“this” probably always means
“this”... don’t ask questions
http://bit.ly/php-js-demo
04 - Scoping Concerns
@weaverryan
Thursday, May 24, 12
67. ImaginationLand PHP code
$magicBox = new MagicBoxes('foo');
$foo = new Foo();
// imaginary PHP function
// this calls $magicBox->doSomething()
// BUT, forces $this to actual be $foo inside
// that object. Madness!
call_func($magicBox, 'doSomething', $foo);
Thursday, May 24, 12
68. With JavaScript objects, “this”
may not always be what you
think it is
@weaverryan
Thursday, May 24, 12
70. Is it our Boxes object?
Boxes.prototype._luigiClick = function(event) {
event.preventDefault();
this.luigiFights($(this));
};
Or is it the DOM element that
triggered the jQuery event?
Thursday, May 24, 12
71. “this” is actually the DOM element
that triggered the jQuery event
Boxes.prototype._luigiClick = function(event) {
event.preventDefault();
this.luigiFights($(this));
};
so “this” will not work pun intended...
Thursday, May 24, 12
73. window.Boxes = function($container) {
this.$el = $container;
var $luigi = this.$el.find('.luigi');
$luigi.on(
'click',
$.proxy(this._luigiClick, this)
);
};
$.proxy forces _luigiClick
to have “this” as this
object when called
Thursday, May 24, 12
74. event.currentTarget returns
the element that was
registered on this event
(formerly “this”)
Boxes.prototype._luigiClick = function(event) {
event.preventDefault();
this.makeLuigiFight($(event.currentTarget));
};
Thursday, May 24, 12
75. Mario’s bad JavaScript tip #5:
Avoid jQuery.extend so that you
can repeat yourself as much as
possible: DRY*
* definitely repeat yourself
http://bit.ly/php-js-demo
@weaverryan 05 - jQuery extends
Thursday, May 24, 12
76. PHP ImaginationLand
// foo has printFoo() method on it
$foo = new Foo();
// bar has a printBar() method on it
$bar = new Bar();
// now $fooBar has both methods!
$fooBar = extend($foo, $bar);
$fooBar->printFoo();
$fooBar->printBar();
Thursday, May 24, 12
77. var foo = {
foo: function() {
return 'foo-string';
}
};
var bar = {
bar: function() {
return 'bar-string';
}
};
var fooBar = $.extend(foo, bar);
console.log(fooBar.foo(), fooBar.bar());
Thursday, May 24, 12
78. Writing bad JavaScript tip #6:
Avoid using “delegate” events,
and instead constantly worry
about re-attaching events to new
elements
http://bit.ly/php-js-demo
06 - Delegate events
@weaverryan
Thursday, May 24, 12
79. Can you see the difference?
this.$el.find('.luigi').on(
'click',
$.proxy(this._luigiClick, this)
);
this.$el.on(
'click',
'.luigi',
$.proxy(this._luigiClick, this)
);
Thursday, May 24, 12
80. this.$el.find('.luigi')on(
'click',
$.proxy(this._luigiClick, this)
);
Since the event is
registered specifically on
“.luigi”, if any new
“.luigi” elements are
added, they will not
response to this event
Thursday, May 24, 12
81. The event is registered on
the wrapper, but looks for
“.luigi”. If new “.luigi”
elements are added to the
wrapper, they will
automatically trigger the
event
this.$el.on(
'click',
'.luigi',
$.proxy(this._luigiClick, this)
);
Thursday, May 24, 12
82. jQuery.live?
$('.foo').live('click', ...);
really just means this
$('body').on('click', '.foo', ...);
Thursday, May 24, 12
83. Mario’s bad JavaScript tip #7:
Self-executing JavaScript blocks
are just plain scary looking
http://bit.ly/php-js-demo
07 - Self-executing blocks
@weaverryan
Thursday, May 24, 12
84. PHP Imaginationland
$dbConn = ''; // initialize this
$ourPreparedObject = (function($db) {
$a = 5;
$b = 10;
$db->execute('...');
// ... a lot more complex stuff
return $someObject;
})($dbConn);
$ourPreparedObject->callSomething();
Thursday, May 24, 12
85. JavaScript Real Life
window.MagicBoxes = (function($) {
return {
// do a bunch of craziness
};
})(jQuery);
MagicBoxes.someMethod();
Thursday, May 24, 12
86. This is just a function...
window.MagicBoxes = (function($) {
return {
// do a bunch of craziness
};
})(jQuery);
MagicBoxes.someMethod();
Thursday, May 24, 12
87. Then we execute it, and pass in an argument
window.MagicBoxes = (function($) {
return {
// do a bunch of craziness
};
})(jQuery);
MagicBoxes.someMethod();
Thursday, May 24, 12
88. The function does any craziness it
wants, but returns something
window.MagicBoxes = (function($) {
return {
// do a bunch of craziness
};
})(jQuery);
MagicBoxes.someMethod();
Thursday, May 24, 12
89. which we assign to a variable and then use
window.MagicBoxes = (function($) {
return {
// do a bunch of craziness
};
})(jQuery);
MagicBoxes.someMethod();
Thursday, May 24, 12
90. And that’s it!
‣ Makes isolated chunks of code
‣ Used by most JavaScript libraries to
isolate their code from yours
Thursday, May 24, 12
92. Mario’s bad JavaScript tip #8:
Don’t use, or over-use
Backbone.js
http://bit.ly/php-js-demo
08 - A Backbone View
@weaverryan
Thursday, May 24, 12
93. What is Backbone?
‣ Set of tools for creating heavy front-
end applications:
* views
* models
* router
@weaverryan
Thursday, May 24, 12
94. Views
A formalized method for creating a
JavaScript object that “manages” a
DOM element
Thursday, May 24, 12
95. Views
We’ve been building an object that’s
very similar to a Backbone view
Thursday, May 24, 12
96. var MagicBoxes = Backbone.View.extend({
events: {
'click .luigi': '_luigiClick'
},
initialize: function() {
_.bindAll(this, '_luigiClick')
},
_luigiClick: function(event) {
event.preventDefault();
$(event.currentTarget)
.toggleClass('active');
}
});
var magicBoxApp = new MagicBoxes({
el: $('.mario-world')
});
Thursday, May 24, 12
97. var MagicBoxes = Backbone.View.extend({
events: {
'click .luigi': '_luigiClick'
},
initialize: function() {
_.bindAll(this, '_luigiClick')
}, That sure is a simple way to
bind to events. And thanks to
_luigiClick: function(event) {
“delegate” events, when the
event.preventDefault();
DOM updates, the events still
$(event.currentTarget)
fire on new elements!
.toggleClass('active');
}
});
var magicBoxApp = new MagicBoxes({
el: $('.mario-world')
});
Thursday, May 24, 12
98. var MagicBoxes = Backbone.View.extend({
events: {
'click .luigi': '_luigiClick'
},
initialize: function() {
_.bindAll(this, '_luigiClick')
},
_luigiClick: function(event) {
event.preventDefault();
$(event.currentTarget) automatically
initialize() is
.toggleClass('active');
called by Backbone.
}
});
var magicBoxApp = new MagicBoxes({
el: $('.mario-world')
});
Thursday, May 24, 12
99. var MagicBoxes = Backbone.View.extend({
events: {
'click .luigi': '_luigiClick'
},
initialize: function() {
_.bindAll(this, '_luigiClick')
},
_luigiClick: function(event) {
_.bindAll is from
event.preventDefault();
underscore.js - it does the
$(event.currentTarget)
same job as jQuery.proxy:
.toggleClass('active');
} guarantees that “this” is
}); this object in that function
var magicBoxApp = new MagicBoxes({
el: $('.mario-world')
});
Thursday, May 24, 12
100. var MagicBoxes = Backbone.View.extend({
events: {
'click .luigi': '_luigiClick'
},
initialize: function() {
_.bindAll(this, '_luigiClick') DOM
We attach a real
}, element to the view by
passing it into a pre-made
_luigiClick: function(event) {
constructor as “el”. In the
event.preventDefault();
object, it’s available via
$(event.currentTarget)
this.el
.toggleClass('active');
}
});
var magicBoxApp = new MagicBoxes({
el: $('.mario-world')
});
Thursday, May 24, 12
101. Models
A formalized object that just holds
key-value data on it
Thursday, May 24, 12
102. var Character = Backbone.Model.extend();
var mario = new Character({
'name': 'Mario'
});
var peach = new Character({
'name': 'Peach',
'status': 'captured'
});
// change some data
peach.set({
'status': 'rescued'
});
Thursday, May 24, 12
103. Events make them wonderful
peach.on('change', function(changedModel) {
var name = changedModel.get('name');
console.log(name + ' was updated!');
});
// will cause "Peach was updated" to log
peach.set({
'status': 'rescued'
});
Thursday, May 24, 12
104. var Character = Backbone.Model.extend();
var App = Backbone.View.extend({
initialize: function() {
_.bindAll(this, '_updateName');
this.model.on('change', this._updateName);
this._updateName();
},
_updateName: function() {
var name = this.model.get('name');
this.$('.name').html(name);
}
});
var mario = new Character({
'name': 'Mario'
});
var app = new App({
el: $('.mario-world'),
model: mario
});
Thursday, May 24, 12
105. var Character = Backbone.Model.extend();
var App = Backbone.View.extend({
initialize: function() {
_.bindAll(this, '_updateName');
this.model.on('change', this._updateName);
this._updateName();
},
_updateName: function() {
var name = this.model.get('name');
this.$('.name').html(name);
}
});
When the model changes, we
can update the DOM anywhere
that we’re listening for
that change
Thursday, May 24, 12
107. But do you have a problem?
Thursday, May 24, 12
108. Views: Easy win
‣ Easy event binding
‣ An object-oriented structure that’s
setup for you already
‣ “Patterns” to follow as you get
comfortable
@weaverryan
Thursday, May 24, 12
109. Views: Who Renders?
‣ There are 2 types of Views:
Easy 1) Views applied to existing elements
Win
in the DOM (like our example)
2) Views that are given an empty
Depends
element, and then render using client-
side templates and model data
@weaverryan
Thursday, May 24, 12
110. When to use Models + Views
‣ A highly interactive app that
communicates to a PHP API that is
never responsible for rendering any
HTML
@weaverryan
Thursday, May 24, 12
111. When *not* to use Models + Views
‣ An app where PHP renders HTML
‣ An app where the API isn’t robust
@weaverryan
Thursday, May 24, 12
112. Duplication
‣ If your PHP application renders
HTML and you also try to use
Backbone Views that render HTML,
you’ll need duplicate templates
‣ Do one or the other
@weaverryan
Thursday, May 24, 12
113. Duplication
You can potentially duplicate a lot of
work both on the client and server
sides:
‣ validation
‣ models
‣ templates
@weaverryan
Thursday, May 24, 12
114. Find your Comfort Zone
‣ Not really comfortable with a fully-
client side application? Use
Backbone views attached to existing
DOM element
‣ Feel pretty awesome about doing
everything in the browser? Dive in :)
@weaverryan
Thursday, May 24, 12
115. Thanks...
Ryan Weaver
@weaverryan
Thursday, May 24, 12
116. ... and we love you!
http://joind.in/talk/view/6508
Ryan Weaver
@weaverryan
Ryan Weaver
@weaverryan
Thursday, May 24, 12