Upload
ryan-weaver
View
15.094
Download
0
Tags:
Embed Size (px)
DESCRIPTION
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
Citation preview
JAVASCRIPT BEST PRACTICES & BACKBONE.JS FOR THE PHP DEVELOPER
Ryan Weaver@weaverryan
Thursday, May 24, 12
7 tips for writing JavaScript like a jerk
+What to take and leave in
Backbone.js
Thursday, May 24, 12
Who is this dude?• Co-author of the Symfony2 Docs
• KnpLabs US - Symfony consulting, training, Kumbaya
• Writer for KnpUniversity.comscreencasts
• Fiancee of the much more talented @leannapelham -----> June 9th, 2012!
http://www.knplabs.com/enhttp://www.github.com/weaverryan@weaverryan
Thursday, May 24, 12
Chapter 1: Java$criptQuery
Look mom, I’m a JavaScript professional
@weaverryanThursday, May 24, 12
1. jQuery(document).ready(...)2. 1500 lines, with deeply nested anonymous functions3. Profit! $$$
@weaverryanThursday, May 24, 12
@weaverryan
Too soon?... sry fb, luv u kthxbye
Thursday, May 24, 12
The brains behind this presentation...
Thursday, May 24, 12
source:http://www.geeky-gadgets.com/super-mario-bros-matryoshka-dolls/
Thursday, May 24, 12
Thursday, May 24, 12
<div class="mario box"> .mario <div class="luigi box"> .luigi <div class="kick-butt">...</div> <a href="#" class="peach box"> .peach </a> </div></div>
Thursday, May 24, 12
jQuery(document).ready(function() { var $luigi = $('.event-details .luigi');
$luigi.click(function() { $(this).addClass('active'); return false; });});
Let’s kick some arse!
Thursday, May 24, 12
DEMO!!!!!
0 - The starting point
http://bit.ly/php-js-demo
Thursday, May 24, 12
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
‣ Click either .luigi or .peach‣ the .luigi wrapper gets the active class
Thursday, May 24, 12
Mario’s bad JavaScript tip #1:
Ignore the jQuery “event” object, it’s probably stupid...
@weaverryan
http://bit.ly/php-js-demo01 - The jQuery Event
Thursday, May 24, 12
Events happen
Thursday, May 24, 12
Click!
Event
1) You click!2) An event travels up the tree
Thursday, May 24, 12
Browser eventversus
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
$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
$luigi.click(function(event) { // ...});
Our listener is registeredon .luigi
Thursday, May 24, 12
If you click .peach:event.target: .peachevent.currentTarget: .luigi
Thursday, May 24, 12
If you click .luigi:event.target: .luigievent.currentTarget: .luigi
Thursday, May 24, 12
event.currentTarget
==
this
* unless you screw with scope, which we’ll see!
Thursday, May 24, 12
$luigi.click(function(event) { // these are the same! $(event.currentTarget) .addClass('active'); $luigi.addClass('active');});
Thursday, May 24, 12
Preventing the damn “#” on the URL
$luigi.click(function() { $(this).addClass('active'); return false;});
Thursday, May 24, 12
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
$luigi.click(function(event) { event.preventDefault();
// ...});
... but if you like your coworkers...
The event keeps traveling up the DOM
Thursday, May 24, 12
Bad JavaScript moral #1:
jQuery’s event object is a dangerous source of useful
information and control
@weaverryanThursday, May 24, 12
Mario bad JavaScript tip #2:
Avoid using objects: they threaten to organize your code...
and are creepy...
@weaverryan
http://bit.ly/php-js-demo02 - Basic jQuery Objects
Thursday, May 24, 12
A PHP object
@weaverryanThursday, May 24, 12
<?php
class MagicBoxes{ public function initializeClick($wrapper) { // ... }}
$magicBoxes = new MagicBoxes();$magicBoxes->initializeClick('something');
Thursday, May 24, 12
A JavaScript object
@weaverryanThursday, May 24, 12
var MagicBoxes = { someProperty: 0,
initializeClick: function($container) { // ... }};
MagicBoxes.initializeClick(something);
Thursday, May 24, 12
Just like with PHP, you can choose to organize your code
into objects and functions
@weaverryanThursday, May 24, 12
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
Princess Pro-tip
Be careful with objects, they can
increase readability, which threatens job
security
Thursday, May 24, 12
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
// hola! I’m just a local variablevar MagicBoxes = { // ...};
// I’m a global variable, available anywherewindow.MagicBoxes = {
};
Object Scope
Thursday, May 24, 12
window.foo = 'foo-window';console.log(foo);// prints "foo-window"
bar = 'bar-global';console.log(window.bar);// prints "bar-global"
the “window”
Thursday, May 24, 12
Bad JavaScript moral #2:
Using global objects risks organizing your code into
separate, distinct units
@weaverryanThursday, May 24, 12
Mario’s bad JavaScript tip #3:
JavaScript’s “prototype” object model is too scary to use
@weaverryan
http://bit.ly/php-js-demo03 - Intro to the JS Prototype
Thursday, May 24, 12
Everything is an object
Thursday, May 24, 12
var object1 = { fooProperty: 'foo!'}
console.log(object1.fooProperty);// prints “foo!”
An object with a property
Thursday, May 24, 12
var alsoAnObject = function() { return 'foo-return!'};alsoAnObject.barProperty = 'bar!';
console.log(alsoAnObject.barProperty);// prints “bar!”
console.log(alsoAnObject());// prints “foo-return!”
Also an object with a property
Thursday, May 24, 12
In PHP, we can create a class and then instantiate many
instances
@weaverryanThursday, May 24, 12
<?php
class MagicBoxes{ public function __construct($option) { // ... }}
$magicBoxes1 = new MagicBoxes('foo');
$magicBoxes2 = new MagicBoxes('bar');Thursday, May 24, 12
How can we do this in JavaScript?
@weaverryanThursday, May 24, 12
Imagine if we could do this craziness in PHP
@weaverryanThursday, May 24, 12
$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
ImaginationLand PHP code
Thursday, May 24, 12
Yep, that’s how it works in JavaScript!
@weaverryanThursday, May 24, 12
#1: Create a function
window.MagicBoxes = function($container) { this.$el = $container; this.initialize();};
Thursday, May 24, 12
#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
#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
#3: Instantiate your new object
jQuery(document).ready(function() { var $mario = $('.mario-world'); var magicBoxApp = new MagicBoxes($mario);});
Thursday, May 24, 12
#4: Beers!
@weaverryanThursday, May 24, 12
Princess Pro-tip
Your speaker is a liar and a thief.
Using the prototype is for sissies!
Thursday, May 24, 12
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
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
Bowser final word
Seriously, this is why I fight against you guys
Thursday, May 24, 12
Bowser final word
Your ignorance may seem blissful, but you work as a
detriment towards the larger cause of evolving
and learning asa community
Thursday, May 24, 12
Bowser final word
Thank God Garrison Locke will be teaching us Object-
oriented JavaScript tomorrow at 11:30 AM.
Thursday, May 24, 12
Bowser final word
He’s a much better speaker than this guy
Thursday, May 24, 12
Ryan does damage control on his presentation
Dude, I’m right here
Thursday, May 24, 12
Purple is a manly color
You really like that shirt...
Thursday, May 24, 12
Bad JavaScript moral #3:
Don’t use jQuery’s prototype or go to Garrison Locke’s
presentation tomorrow if you want unreadable JavaScript
@weaverryanThursday, May 24, 12
Mario’s bad JavaScript tip #4:
“this” probably always means “this”... don’t ask questions
@weaverryan
http://bit.ly/php-js-demo04 - Scoping Concerns
Thursday, May 24, 12
$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);
ImaginationLand PHP code
Thursday, May 24, 12
With JavaScript objects, “this” may not always be what you
think it is
@weaverryanThursday, May 24, 12
window.Boxes = function($container) { this.$el = $container;
var $luigi = this.$el.find('.luigi'); $luigi.on('click', this._luigiClick);};
Boxes.prototype._luigiClick = function(event) { event.preventDefault(); this.luigiFights($(this));};
Boxes.prototype.luigiFights = function($luigi) { $luigi.toggleClass('active');};
Thursday, May 24, 12
Boxes.prototype._luigiClick = function(event) { event.preventDefault(); this.luigiFights($(this));};
Is it our Boxes object?
Or is it the DOM element that triggered the jQuery event?
Thursday, May 24, 12
Boxes.prototype._luigiClick = function(event) { event.preventDefault(); this.luigiFights($(this));};
“this” is actually the DOM element that triggered the jQuery event
so “this” will not work pun intended...
Thursday, May 24, 12
window.Boxes = function($container) { this.$el = $container;
var $luigi = this.$el.find('.luigi'); $luigi.on( 'click', $.proxy(this._luigiClick, this) );};
Boxes.prototype._luigiClick = function(event) { event.preventDefault(); this.makeLuigiFight($(event.currentTarget));};
Thursday, May 24, 12
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
Boxes.prototype._luigiClick = function(event) { event.preventDefault(); this.makeLuigiFight($(event.currentTarget));};
event.currentTarget returns the element that was
registered on this event (formerly “this”)
Thursday, May 24, 12
Mario’s bad JavaScript tip #5:
Avoid jQuery.extend so that you can repeat yourself as much as
possible: DRY*
@weaverryan
* definitely repeat yourselfhttp://bit.ly/php-js-demo
05 - jQuery extendsThursday, May 24, 12
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
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
Writing bad JavaScript tip #6:
Avoid using “delegate” events, and instead constantly worry
about re-attaching events to new elements
@weaverryan
http://bit.ly/php-js-demo06 - Delegate events
Thursday, May 24, 12
this.$el.on( 'click', '.luigi', $.proxy(this._luigiClick, this));
this.$el.find('.luigi').on( 'click', $.proxy(this._luigiClick, this));
Can you see the difference?
Thursday, May 24, 12
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
this.$el.on( 'click', '.luigi', $.proxy(this._luigiClick, this));
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
Thursday, May 24, 12
jQuery.live?
$('.foo').live('click', ...);
really just means this
$('body').on('click', '.foo', ...);
Thursday, May 24, 12
Mario’s bad JavaScript tip #7:
Self-executing JavaScript blocks are just plain scary looking
@weaverryan
http://bit.ly/php-js-demo07 - Self-executing blocks
Thursday, May 24, 12
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
window.MagicBoxes = (function($) {
return { // do a bunch of craziness };})(jQuery);
MagicBoxes.someMethod();
JavaScript Real Life
Thursday, May 24, 12
window.MagicBoxes = (function($) {
return { // do a bunch of craziness };})(jQuery);
MagicBoxes.someMethod();
This is just a function...
Thursday, May 24, 12
window.MagicBoxes = (function($) {
return { // do a bunch of craziness };})(jQuery);
MagicBoxes.someMethod();
Then we execute it, and pass in an argument
Thursday, May 24, 12
window.MagicBoxes = (function($) {
return { // do a bunch of craziness };})(jQuery);
MagicBoxes.someMethod();
The function does any craziness it wants, but returns something
Thursday, May 24, 12
window.MagicBoxes = (function($) {
return { // do a bunch of craziness };})(jQuery);
MagicBoxes.someMethod();
which we assign to a variable and then use
Thursday, May 24, 12
And that’s it!
‣ Makes isolated chunks of code
‣ Used by most JavaScript libraries to isolate their code from yours
Thursday, May 24, 12
Backbone.js!
@weaverryanThursday, May 24, 12
Mario’s bad JavaScript tip #8:
Don’t use, or over-use Backbone.js
@weaverryan
http://bit.ly/php-js-demo08 - A Backbone View
Thursday, May 24, 12
@weaverryan
‣ Set of tools for creating heavy front-end applications:
* views* models* router
What is Backbone?
Thursday, May 24, 12
Views
A formalized method for creating a JavaScript object that “manages” a
DOM element
Thursday, May 24, 12
Views
We’ve been building an object that’s very similar to a Backbone view
Thursday, May 24, 12
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
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')});
That sure is a simple way to bind to events. And thanks to “delegate” events, when the
DOM updates, the events still fire on new elements!
Thursday, May 24, 12
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')});
initialize() is automatically called by Backbone.
Thursday, May 24, 12
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')});
_.bindAll is from underscore.js - it does the same job as jQuery.proxy: guarantees that “this” is
this object in that function
Thursday, May 24, 12
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')});
We attach a real DOM element to the view by
passing it into a pre-made constructor as “el”. In the object, it’s available via
this.el
Thursday, May 24, 12
Models
A formalized object that just holds key-value data on it
Thursday, May 24, 12
var Character = Backbone.Model.extend();
var mario = new Character({ 'name': 'Mario'});var peach = new Character({ 'name': 'Peach', 'status': 'captured'});
// change some datapeach.set({ 'status': 'rescued'});
Thursday, May 24, 12
peach.on('change', function(changedModel) { var name = changedModel.get('name'); console.log(name + ' was updated!');});
// will cause "Peach was updated" to logpeach.set({ 'status': 'rescued'});
Events make them wonderful
Thursday, May 24, 12
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
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
Backbone is a great solution
Thursday, May 24, 12
But do you have a problem?
Thursday, May 24, 12
@weaverryan
‣ Easy event binding
‣ An object-oriented structure that’s setup for you already
‣ “Patterns” to follow as you get comfortable
Views: Easy win
Thursday, May 24, 12
@weaverryan
‣ There are 2 types of Views:
1) Views applied to existing elements in the DOM (like our example)
2) Views that are given an empty element, and then render using client-side templates and model data
Views: Who Renders?
Easy Win
Depends
Thursday, May 24, 12
@weaverryan
‣ A highly interactive app that communicates to a PHP API that is never responsible for rendering any HTML
When to use Models + Views
Thursday, May 24, 12
@weaverryan
‣ An app where PHP renders HTML
‣ An app where the API isn’t robust
When *not* to use Models + Views
Thursday, May 24, 12
@weaverryan
‣ 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
Duplication
Thursday, May 24, 12
@weaverryan
You can potentially duplicate a lot of work both on the client and server sides:
‣ validation‣ models‣ templates
Duplication
Thursday, May 24, 12
@weaverryan
‣ 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 :)
Find your Comfort Zone
Thursday, May 24, 12
Thanks...
Ryan Weaver@weaverryan
Thursday, May 24, 12
... and we love you!
Ryan Weaver@weaverryan
Ryan Weaver@weaverryan
http://joind.in/talk/view/6508
Thursday, May 24, 12