diff --git a/page/code-organization/beware-anonymous-functions.md b/page/code-organization/beware-anonymous-functions.md index 8ce056b9..5890b408 100644 --- a/page/code-organization/beware-anonymous-functions.md +++ b/page/code-organization/beware-anonymous-functions.md @@ -2,7 +2,7 @@ title: Beware Anonymous Functions level: beginner source: http://jqfundamentals.com/legacy -attribution: +attribution: - jQuery Fundamentals --- @@ -13,44 +13,33 @@ your handlers and callbacks. // BAD $( document ).ready(function() { - $("#magic").click(function( event ) { + $( "#magic" ).click(function( event ) { + $( "#yayeffects" ).slideUp(function() { + // ... + }); + }); - $("#yayeffects").slideUp(function() { - - // ... - - }); - - }); - - $("#happiness").load( url + " #unicorns", function() { - - // ... - - }); + $( "#happiness" ).load( url + " #unicorns", function() { + // ... + }); }); // BETTER var PI = { - onReady : function() { - - $("#magic").click( PI.candyMtn ); - - $("#happiness").load( PI.url + " #unicorns", PI.unicornCb ); - - }, - - candyMtn : function( event ) { - - $("#yayeffects").slideUp( PI.slideCb ); + onReady: function() { + $( "#magic" ).click( PI.candyMtn ); + $( "#happiness" ).load( PI.url + " #unicorns", PI.unicornCb ); + }, - }, + candyMtn: function( event ) { + $( "#yayeffects" ).slideUp( PI.slideCb ); + }, - slideCb : function() { ... }, + slideCb: function() { ... }, - unicornCb : function() { ... } + unicornCb: function() { ... } }; diff --git a/page/code-organization/concepts.md b/page/code-organization/concepts.md index 612a479b..4de26973 100644 --- a/page/code-organization/concepts.md +++ b/page/code-organization/concepts.md @@ -2,7 +2,7 @@ title: Code Organization Concepts level: beginner source: http://jqfundamentals.com/legacy -attribution: +attribution: - jQuery Fundamentals --- @@ -51,16 +51,21 @@ options, and easing the path to reuse and refactoring. ``` // An object literal var myFeature = { - myProperty: "hello", - myMethod: function() { - console.log( myFeature.myProperty ); - }, - init: function( settings ) { - myFeature.settings = settings; - }, - readSettings : function() { - console.log( myFeature.settings ); - } + + myProperty: "hello", + + myMethod: function() { + console.log( myFeature.myProperty ); + }, + + init: function( settings ) { + myFeature.settings = settings; + }, + + readSettings: function() { + console.log( myFeature.settings ); + } + }; myFeature.myProperty === "hello"; // true @@ -68,7 +73,7 @@ myFeature.myProperty === "hello"; // true myFeature.myMethod(); // "hello" myFeature.init({ - foo: "bar" + foo: "bar" }); myFeature.readSettings(); // { foo: "bar" } @@ -84,28 +89,18 @@ How would we apply this pattern to jQuery code? Let's say that we had this code written in the traditional jQuery style: ``` -// clicking on a list item loads some content -// using the list item's ID and hides content -// in sibling list items +// clicking on a list item loads some content using the +// list item's ID, and hides content in sibling list items $( document ).ready(function() { - $("#myFeature li").append("
").click(function() { - - var $this = $( this ); - - var $div = $this.find("div"); - - $div.load( "foo.php?item=" + $this.attr("id"), function() { - - $div.show(); - - $this.siblings().find("div").hide(); - - } - - ); - - }); + $( "#myFeature li" ).append( "
" ).click(function() { + var $this = $( this ); + var $div = $this.find( "div" ); + $div.load( "foo.php?item=" + $this.attr( "id" ), function() { + $div.show(); + $this.siblings().find( "div" ).hide(); + }); + }); }); ``` @@ -121,76 +116,53 @@ functionality later. // Using an object literal for a jQuery feature var myFeature = { - init : function( settings ) { - - myFeature.config = { - $items : $("#myFeature li"), - $container : $("
"), - urlBase : "/foo.php?item=" - }; - - // allow overriding the default config - $.extend( myFeature.config, settings ); - - myFeature.setup(); - - }, - - setup : function() { - - myFeature.config.$items.each( myFeature.createContainer ).click( myFeature.showItem ); - - }, - - createContainer : function() { - - var $i = $( this ); - - var $c = myFeature.config.$container.clone().appendTo( $i ); - - $i.data( "container", $c ); - - }, - - buildUrl : function() { - - return myFeature.config.urlBase + myFeature.$currentItem.attr("id"); - - }, - - showItem : function() { - - var myFeature.$currentItem = $( this ); - - myFeature.getContent( myFeature.showContent ); - - }, - - getContent : function( callback ) { - - var url = myFeature.buildUrl(); - - myFeature.$currentItem.data("container").load( url, callback ); - - }, - - showContent : function() { - - myFeature.$currentItem.data("container").show(); - - myFeature.hideContent(); - - }, - - hideContent : function() { - - myFeature.$currentItem.siblings().each(function() { - - $( this ).data("container").hide(); - - }); - - } + init: function( settings ) { + myFeature.config = { + $items: $( "#myFeature li" ), + $container: $( "
" ), + urlBase: "/foo.php?item=" + }; + + // allow overriding the default config + $.extend( myFeature.config, settings ); + + myFeature.setup(); + }, + + setup: function() { + myFeature.config.$items.each( myFeature.createContainer ).click( myFeature.showItem ); + }, + + createContainer: function() { + var $i = $( this ); + var $c = myFeature.config.$container.clone().appendTo( $i ); + $i.data( "container", $c ); + }, + + buildUrl: function() { + return myFeature.config.urlBase + myFeature.$currentItem.attr( "id" ); + }, + + showItem: function() { + var myFeature.$currentItem = $( this ); + myFeature.getContent( myFeature.showContent ); + }, + + getContent: function( callback ) { + var url = myFeature.buildUrl(); + myFeature.$currentItem.data( "container" ).load( url, callback ); + }, + + showContent: function() { + myFeature.$currentItem.data( "container" ).show(); + myFeature.hideContent(); + }, + + hideContent: function() { + myFeature.$currentItem.siblings().each(function() { + $( this ).data( "container" ).hide(); + }); + } }; @@ -198,7 +170,7 @@ $( document ).ready( myFeature.init ); ``` The first thing you'll notice is that this approach is obviously far longer -than the original -- again, if this were the extent of our application, using an +than the original — again, if this were the extent of our application, using an object literal would likely be overkill. Assuming it's not the extent of our application, though, we've gained several things: @@ -227,31 +199,30 @@ desired. // The module pattern var feature = (function() { - // private variables and functions - var privateThing = "secret"; - var publicThing = "not secret"; - var changePrivateThing = function() { - privateThing = "super secret"; - }; + // private variables and functions + var privateThing = "secret"; + var publicThing = "not secret"; - var sayPrivateThing = function() { - console.log( privateThing ); - changePrivateThing(); - }; + var changePrivateThing = function() { + privateThing = "super secret"; + }; - // public API - return { - publicThing: publicThing, - sayPrivateThing: sayPrivateThing - }; + var sayPrivateThing = function() { + console.log( privateThing ); + changePrivateThing(); + }; + + // public API + return { + publicThing: publicThing, + sayPrivateThing: sayPrivateThing + }; })(); feature.publicThing; // "not secret" - -// logs "secret" and changes the value -// of privateThing +// logs "secret" and changes the value of privateThing feature.sayPrivateThing(); ``` @@ -276,47 +247,56 @@ of the module, `showItemByIndex()`. // Using the module pattern for a jQuery feature $( document ).ready(function() { - var feature = (function() { - - var $items = $("#myFeature li"); - var $container = $("
"); - var $currentItem = null; - var urlBase = "/foo.php?item="; - var createContainer = function() { - var $i = $( this ); - var $c = $container.clone().appendTo( $i ); - $i.data( "container", $c ); - }, - buildUrl = function() { - return urlBase + $currentItem.attr("id"); - }, - showItem = function() { - $currentItem = $( this ); - getContent( showContent ); - }, - showItemByIndex = function( idx ) { - $.proxy( showItem, $items.get( idx ) ); - }, - getContent = function( callback ) { - $currentItem.data("container").load( buildUrl(), callback ); - }, - showContent = function() { - $currentItem.data("container").show(); - hideContent(); - }, - hideContent = function() { - $currentItem.siblings().each(function() { - $( this ).data("container").hide(); - }); - }; - $items.each( createContainer ).click( showItem ); - - return { - showItemByIndex: showItemByIndex - }; - - })(); - - feature.showItemByIndex( 0 ); + var feature = (function() { + + var $items = $( "#myFeature li" ); + var $container = $( "
" ); + var $currentItem = null; + var urlBase = "/foo.php?item="; + + var createContainer = function() { + var $i = $( this ); + var $c = $container.clone().appendTo( $i ); + $i.data( "container", $c ); + }, + + buildUrl = function() { + return urlBase + $currentItem.attr( "id" ); + }, + + showItem = function() { + $currentItem = $( this ); + getContent( showContent ); + }, + + showItemByIndex = function( idx ) { + $.proxy( showItem, $items.get( idx ) ); + }, + + getContent = function( callback ) { + $currentItem.data( "container" ).load( buildUrl(), callback ); + }, + + showContent = function() { + $currentItem.data( "container" ).show(); + hideContent(); + }, + + hideContent = function() { + $currentItem.siblings().each(function() { + $( this ).data( "container" ).hide(); + }); + }; + + $items.each( createContainer ).click( showItem ); + + return { + showItemByIndex: showItemByIndex + }; + + })(); + + feature.showItemByIndex( 0 ); + }); ``` diff --git a/page/code-organization/deferreds.md b/page/code-organization/deferreds.md index 5e10b597..5317fa53 100644 --- a/page/code-organization/deferreds.md +++ b/page/code-organization/deferreds.md @@ -2,7 +2,7 @@ title: Deferreds level: advanced source: http://msdn.microsoft.com/en-us/magazine/gg723713.aspx -attribution: +attribution: - Julian Aubourg - Addy Osmani - Andree Hansson @@ -18,7 +18,7 @@ object: they will be called once the request has actually completed. ##Promises -In its most basic form, a 'promise' is a model that provides a solution +In its most basic form, a "promise" is a model that provides a solution for the concept of deferred (or future) results in software engineering. The main idea behind it is something we've already covered: rather than executing a call which may result in blocking, we instead return a @@ -30,10 +30,10 @@ common problem that's faced is having an unknown knowledge of the API server's latency at a given time so it's possible that other parts of your application may be blocked from running until a result from it is returned. Deferreds provide a better solution to this problem, one which -is void of 'blocking' effects and completely decoupled. +is void of "blocking" effects and completely decoupled. The [Promise/A](http://wiki.commonjs.org/wiki/Promises/A) proposal -defines a method called 'then' that can be used to register callbacks to +defines a method called "then" that can be used to register callbacks to a promise and, thus, get the future result when it is available. The pseudo-code for dealing with a third party API that returns a promise may look like: @@ -42,68 +42,68 @@ may look like: promise = callToAPI( arg1, arg2, ...); promise.then(function( futureValue ) { - /* handle futureValue */ + /* handle futureValue */ }); - + promise.then(function( futureValue ) { - /* do something else */ + /* do something else */ }); ``` Furthermore, a promise can actually end up being in two different states: -- resolved: in which case data is available -- rejected: in which case something went wrong and no value is +- Resolved: in which case data is available +- Rejected: in which case something went wrong and no value is available -Thankfully, the 'then' method accepts two parameters: one for when the +Thankfully, the "then" method accepts two parameters: one for when the promise was resolved, another for when the promise was rejected. If we get back to pseudo-code, we may do things like: ``` -promise.then( function( futureValue ) { - /* we got a value */ -} , function() { - /* something went wrong */ -} ); +promise.then(function( futureValue ) { + /* we got a value */ +}, function() { + /* something went wrong */ +}); ``` In the case of certain applications, it is necessary to have several results returned before your application can continue at all (for example, displaying a dynamic set of options on a screen before a user is able to select the option that interests them). Where this is the -case, a method called 'when' exists, which can be used to perform some +case, a method called "when" exists, which can be used to perform some action once all the promises have been fully fulfilled: ``` when( - promise1, - promise2, - ... + promise1, + promise2, + ... ).then(function( futureValue1, futureValue2, ... ) { - /* all promises have completed and are resolved */ + /* all promises have completed and are resolved */ }); ``` A good example is a scenario where you may have multiple concurrent animations that are being run. Without keeping track of each callback firing on completion, it can be difficult to truly establish once all -your animations have finished running. Using promises and 'when' however +your animations have finished running. Using promises and "when" however this is very straightforward as each of your animations can effectively -say 'we promise to let you know once we're done'. The compounded result +say "we promise to let you know once we're done". The compounded result of this means it's a trivial process to execute a single callback once the animations are done. For example: ``` -var promise1 = $("#id1").animate().promise(); -var promise2 = $("#id2").animate().promise(); +var promise1 = $( "#id1" ).animate().promise(); +var promise2 = $( "#id2" ).animate().promise(); when( - promise1, - promise2 -).then(function(){ - /* once both animations have completed - we can then run our additional logic */ + promise1, + promise2 +).then(function() { + /* once both animations have completed + we can then run our additional logic */ }); ``` diff --git a/page/code-organization/deferreds/examples.md b/page/code-organization/deferreds/examples.md index 5ab68e62..31ddce78 100644 --- a/page/code-organization/deferreds/examples.md +++ b/page/code-organization/deferreds/examples.md @@ -2,7 +2,7 @@ title: Deferred examples level: advanced source: http://msdn.microsoft.com/en-us/magazine/gg723713.aspx -attribution: +attribution: - Julian Aubourg - Addy Osmani - Andree Hansson @@ -21,32 +21,32 @@ abstract away asynchronous behaviour and decouple our code. When it comes to asynchronous tasks, caching can be a bit demanding since you have to make sure a task is only performed once for a given key. As a consequence, the code has to somehow keep track of inbound -tasks. +tasks. ``` $.cachedGetScript( url, callback1 ); $.cachedGetScript( url, callback2 ); ``` -The caching mechanism has to make sure the url is only requested once +The caching mechanism has to make sure the URL is only requested once even if the script isn't in cache yet. This shows some logic -to keep track of callbacks bound to a given url in order for the cache +to keep track of callbacks bound to a given URL in order for the cache system to properly handle both complete and inbound requests. ``` var cachedScriptPromises = {}; $.cachedGetScript = function( url, callback ) { - if ( !cachedScriptPromises[ url ] ) { - cachedScriptPromises[ url ] = $.Deferred(function( defer ) { - $.getScript( url ).then( defer.resolve, defer.reject ); - }).promise(); - } - return cachedScriptPromises[ url ].done( callback ); + if ( !cachedScriptPromises[ url ] ) { + cachedScriptPromises[ url ] = $.Deferred(function( defer ) { + $.getScript( url ).then( defer.resolve, defer.reject ); + }).promise(); + } + return cachedScriptPromises[ url ].done( callback ); }; ``` -One promise is cached per url. If there is no promise for the given url yet, +One promise is cached per URL. If there is no promise for the given URL yet, then a deferred is created and the request is issued. If it already exists, however, the callback is attached to the existing deferred. The big advantage of this solution is that it will handle both complete and inbound requests @@ -66,28 +66,28 @@ when a key isn't in the cache yet: ``` $.createCache = function( requestFunction ) { - var cache = {}; - return function( key, callback ) { - if ( !cache[ key ] ) { - cache[ key ] = $.Deferred(function( defer ) { - requestFunction( defer, key ); - }).promise(); - } - return cache[ key ].done( callback ); - }; + var cache = {}; + return function( key, callback ) { + if ( !cache[ key ] ) { + cache[ key ] = $.Deferred(function( defer ) { + requestFunction( defer, key ); + }).promise(); + } + return cache[ key ].done( callback ); + }; } ``` -Now that the request logic is abstracted away, cachedGetScript can be rewritten +Now that the request logic is abstracted away, `cachedGetScript` can be rewritten as follows: ``` $.cachedGetScript = $.createCache(function( defer, url ) { - $.getScript( url ).then( defer.resolve, defer.reject ); + $.getScript( url ).then( defer.resolve, defer.reject ); }); ``` -This will work because every call to createCache will create a new cache +This will work because every call to `createCache` will create a new cache repository and return a new cache-retrieval function. #### Image loading @@ -96,16 +96,16 @@ A cache can be used to ensure that the same image is not loaded multiple times. ``` $.loadImage = $.createCache(function( defer, url ) { - var image = new Image(); - function cleanUp() { - image.onload = image.onerror = null; - } - defer.then( cleanUp, cleanUp ); - image.onload = function() { - defer.resolve( url ); - }; - image.onerror = defer.reject; - image.src = url; + var image = new Image(); + function cleanUp() { + image.onload = image.onerror = null; + } + defer.then( cleanUp, cleanUp ); + image.onload = function() { + defer.resolve( url ); + }; + image.onerror = defer.reject; + image.src = url; }); ``` @@ -116,7 +116,7 @@ $.loadImage( "my-image.png" ).done( callback1 ); $.loadImage( "my-image.png" ).done( callback2 ); ``` -will work regardless of whether my-image.png has already been loaded or +will work regardless of whether `my-image.png` has already been loaded or not, or if it is actually in the process of being loaded. #### Caching Data API responses @@ -126,15 +126,15 @@ page are also perfect candidates. For instance, the following: ``` $.searchTwitter = $.createCache(function( defer, query ) { - $.ajax({ - url: "http://search.twitter.com/search.json", - data: { - q: query - }, - dataType: "jsonp", - success: defer.resolve, - error: defer.reject - }); + $.ajax({ + url: "http://search.twitter.com/search.json", + data: { + q: query + }, + dataType: "jsonp", + success: defer.resolve, + error: defer.reject + }); }); ``` @@ -154,32 +154,32 @@ also be used for timing purposes. For instance, you may need to perform an action on the page after a given amount of time so as to attract the user's attention to a specific feature they may not be aware of or deal with a timeout (for a quiz -question for instance). While setTimeout is good for most use-cases it +question for instance). While `setTimeout` is good for most use-cases it doesn't handle the situation when the timer is asked for later, even after it has theoretically expired. We can handle that with the following caching system: ``` var readyTime; - + $(function() { - readyTime = jQuery.now(); + readyTime = jQuery.now(); }); - + $.afterDOMReady = $.createCache(function( defer, delay ) { - delay = delay || 0; - $(function() { - var delta = $.now() - readyTime; - if ( delta >= delay ) { - defer.resolve(); - } else { - setTimeout( defer.resolve, delay - delta ); - } - }); + delay = delay || 0; + $(function() { + var delta = $.now() - readyTime; + if ( delta >= delay ) { + defer.resolve(); + } else { + setTimeout( defer.resolve, delay - delta ); + } + }); }); ``` -The new afterDOMReady helper method provides proper timing after the DOM +The new `afterDOMReady` helper method provides proper timing after the DOM is ready while ensuring the bare minimum of timers will be used. If the delay is already expired, any callback will be called right away. @@ -196,13 +196,13 @@ dealing with such a situation, one usually end up with code like this: ``` var buttonClicked = false; - + $( "#myButton" ).click(function() { - if ( !buttonClicked ) { - buttonClicked = true; - initializeData(); - showPanel(); - } + if ( !buttonClicked ) { + buttonClicked = true; + initializeData(); + showPanel(); + } }); ``` @@ -211,14 +211,14 @@ opened: ``` if ( buttonClicked ) { - /* perform specific action */ + /* perform specific action */ } ``` This is a very coupled solution. If you want to add some other action, you have to edit the bind code or just duplicate it all. If you don't, -your only option is to test for buttonClicked and you may lose that new -action because the buttonClicked variable may be false and your new code +your only option is to test for `buttonClicked` and you may lose that new +action because the `buttonClicked` variable may be false and your new code may never be executed. We can do much better using deferreds (for simplification sake, the @@ -228,24 +228,24 @@ multiple event types): ``` $.fn.bindOnce = function( event, callback ) { - var element = $( this[ 0 ] ), - defer = element.data( "bind_once_defer_" + event ); - if ( !defer ) { - defer = $.Deferred(); - function deferCallback() { - element.unbind( event, deferCallback ); - defer.resolveWith( this, arguments ); - } - element.bind( event, deferCallback ) - element.data( "bind_once_defer_" + event , defer ); - } - return defer.done( callback ).promise(); + var element = $( this[ 0 ] ), + defer = element.data( "bind_once_defer_" + event ); + if ( !defer ) { + defer = $.Deferred(); + function deferCallback() { + element.unbind( event, deferCallback ); + defer.resolveWith( this, arguments ); + } + element.bind( event, deferCallback ) + element.data( "bind_once_defer_" + event , defer ); + } + return defer.done( callback ).promise(); }; ``` The code works as follows: -- check if the element already has a deferred attached for the given +- Check if the element already has a deferred attached for the given event - if not, create it and make it so it is resolved when the event is fired the first time around @@ -258,7 +258,7 @@ But let's define a helper method first: ``` $.fn.firstClick = function( callback ) { - return this.bindOnce( "click", callback ); + return this.bindOnce( "click", callback ); }; ``` @@ -266,7 +266,7 @@ Then the logic can be re-factored as follows: ``` var openPanel = $( "#myButton" ).firstClick(); - + openPanel.done( initializeData ); openPanel.done( showPanel ); ``` @@ -275,7 +275,7 @@ If an action should be performed only when a panel is opened later on: ``` openPanel.done(function() { - /* perform specific action */ + /* perform specific action */ }); ``` @@ -291,73 +291,72 @@ mix them together. #### Requesting panel content on first click and opening said panel Following is the code for a button that, when clicked, opens a panel. -It requests its content over the wire and then fades the content in. Using +It requests its content over the wire and then fades the content in. Using the helpers defined earlier, it could be defined as: ``` $( "#myButton" ).firstClick(function() { - var panel = $( "#myPanel" ); - $.when( - $.get( "panel.html" ), - panel.slideDownPromise() - ).done(function( ajaxResponse ) { - panel.html( ajaxResponse[ 0 ] ).fadeIn(); - }); + var panel = $( "#myPanel" ); + $.when( + $.get( "panel.html" ), + panel.slideDownPromise() + ).done(function( ajaxResponse ) { + panel.html( ajaxResponse[ 0 ] ).fadeIn(); + }); }); ``` #### Loading images in a panel on first click and opening said panel -Another possible goal is to have the panel fade in, only after the button +Another possible goal is to have the panel fade in, only after the button has been clicked and after all of the images have been loaded. -The html code for this would look something like: +The HTML code for this would look something like: ```
- - - - + + + +
``` -We use the data-src attribute to keep track of the real image location. +We use the `data-src` attribute to keep track of the real image location. The code to handle our use case using our promise helpers is as follows: ``` $( "#myButton" ).firstClick(function() { - - var panel = $( "#myPanel" ), - promises = []; - - $( "img", panel ).each(function() { - var image = $( this ), - src = element.attr( "data-src" ); - if ( src ) { - promises.push( - $.loadImage( src ).then( function() { - image.attr( "src", src ); - }, function() { - image.attr( "src", "error.png" ); - } ) - ); - } - }); - - promises.push( - panel.slideDownPromise() - ); - - $.when.apply( null, promises ).done(function() { - panel.fadeIn(); - }); + var panel = $( "#myPanel" ), + promises = []; + + $( "img", panel ).each(function() { + var image = $( this ), + src = element.attr( "data-src" ); + if ( src ) { + promises.push( + $.loadImage( src ).then(function() { + image.attr( "src", src ); + }, function() { + image.attr( "src", "error.png" ); + }) + ); + } + }); + + promises.push( + panel.slideDownPromise() + ); + + $.when.apply( null, promises ).done(function() { + panel.fadeIn(); + }); }); ``` -The trick here is to keep track of all the loadImage promises. We later -join them with the panel slideDown animation using $.when. So when the -button is first clicked, the panel will slideDown and the images will +The trick here is to keep track of all the `loadImage` promises. We later +join them with the panel `slideDown` animation using `$.when`. So when the +button is first clicked, the panel will slide down and the images will start loading. Once the panel has finished sliding down and all the images have been loaded, then, and only then, will the panel fade in. @@ -367,37 +366,36 @@ In order to implement deferred image display on the entire page, the following format in HTML can be used. ``` - - - - + + + + ``` What it says is pretty straight-forward: -- load image1.png and show it immediately for the third image and +- Load `image1.png` and show it immediately for the third image and after one second for the first one -- load image2.png and show it after one second for the second image +- Load `image2.png` and show it after one second for the second image and after two seconds for the fourth image - ``` $( "img" ).each(function() { - var element = $( this ), - src = element.attr( "data-src" ), - after = element.attr( "data-after" ); - if ( src ) { - $.when( - $.loadImage( src ), - $.afterDOMReady( after ) - ).then(function() { - element.attr( "src", src ); - }, function() { - element.attr( "src", "error.png" ); - } ).done(function() { - element.fadeIn(); - }); - } + var element = $( this ), + src = element.attr( "data-src" ), + after = element.attr( "data-after" ); + if ( src ) { + $.when( + $.loadImage( src ), + $.afterDOMReady( after ) + ).then(function() { + element.attr( "src", src ); + }, function() { + element.attr( "src", "error.png" ); + }).done(function() { + element.fadeIn(); + }); + } }); ``` @@ -405,20 +403,20 @@ In order to delay the loading of the images themselves: ``` $( "img" ).each(function() { - var element = $( this ), - src = element.attr( "data-src" ), - after = element.attr( "data-after" ); - if ( src ) { - $.afterDOMReady( after, function() { - $.loadImage( src ).then(function() { - element.attr( "src", src ); - }, function() { - element.attr( "src", "error.png" ); - } ).done(function() { - element.fadeIn(); - }); - } ); - } + var element = $( this ), + src = element.attr( "data-src" ), + after = element.attr( "data-after" ); + if ( src ) { + $.afterDOMReady( after, function() { + $.loadImage( src ).then(function() { + element.attr( "src", src ); + }, function() { + element.attr( "src", "error.png" ); + }).done(function() { + element.fadeIn(); + }); + }); + } }); ``` diff --git a/page/code-organization/deferreds/jquery-deferreds.md b/page/code-organization/deferreds/jquery-deferreds.md index e37c5a19..9b59237e 100644 --- a/page/code-organization/deferreds/jquery-deferreds.md +++ b/page/code-organization/deferreds/jquery-deferreds.md @@ -2,7 +2,7 @@ title: jQuery Deferreds level: advanced source: http://msdn.microsoft.com/en-us/magazine/gg723713.aspx -attribution: +attribution: - Julian Aubourg - Addy Osmani - Andree Hansson @@ -11,10 +11,10 @@ attribution: ##jQuery Deferreds Deferreds were added as a part of a large rewrite of the ajax module, -led by Julian Auborg following the CommonJS Promises/A design. Whilst 1.5 and +led by Julian Aubourg following the CommonJS Promises/A design. Whilst 1.5 and above include deferred capabilities, former versions of jQuery had -jQuery.ajax() accept callbacks that would be invoked upon completion or -error of the request, but suffered from heavy coupling - the same +`jQuery.ajax()` accept callbacks that would be invoked upon completion or +error of the request, but suffered from heavy coupling — the same principle that would drive developers using other languages or toolkits to opt for deferred execution. @@ -26,41 +26,41 @@ noting that jQuery's Deferred object supports having multiple callbacks bound to the outcome of particular tasks (and not just one) where the task itself can either be synchronous or asynchronous. -At the heart of jQuery's implementation is jQuery.Deferred - a chainable +At the heart of jQuery's implementation is `jQuery.Deferred` — a chainable constructor which is able to create new deferred objects that can check for the existence of a promise to establish whether the object can be observed. It can also invoke callback queues and pass on the success of synchronous and asynchronous functions. It's quite essential to note -that the default state of any Deferred object is unresolved. Callbacks -which may be added to it through .then() or .fail() are queued up and get +that the default state of any Deferred object is unresolved. Callbacks +which may be added to it through `.then()` or `.fail()` are queued up and get executed later on in the process. You are able to use Deferred objects in conjunction with the promise concept of -when(), implemented in jQuery as $.when() to wait for all of the Deferred -object's requests to complete executing (ie. for all of the promises to be -fulfilled). In technical terms, $.when() is effectively a way to execute +when(), implemented in jQuery as `$.when()` to wait for all of the Deferred +object's requests to complete executing (i.e. for all of the promises to be +fulfilled). In technical terms, `$.when()` is effectively a way to execute callbacks based on any number of promises that represent asynchronous events. -An example of $.when() accepting multiple arguments can be seen below in -conjunction with .then(): +An example of `$.when()` accepting multiple arguments can be seen below in +conjunction with `.then()`: ``` function successFunc(){ - console.log( "success!" ); -} + console.log( "success!" ); +} function failureFunc(){ - console.log( "failure!" ); + console.log( "failure!" ); } $.when( - $.ajax( "/main.php" ), - $.ajax( "/modules.php" ), - $.ajax( "/lists.php" ) + $.ajax( "/main.php" ), + $.ajax( "/modules.php" ), + $.ajax( "/lists.php" ) ).then( successFunc, failureFunc ); ``` -The $.when() implementation offered in jQuery is quite interesting as it not +The `$.when()` implementation offered in jQuery is quite interesting as it not only interprets deferred objects, but when passed arguments that are not deferreds, it treats these as if they were resolved deferreds and executes any callbacks (doneCallbacks) right away. It is also worth noting that jQuery's @@ -71,42 +71,41 @@ also be used to add callbacks to the deferred's queues. We will now take a look at a code example that utilizes many of the deferred features mentioned in the table presented earlier. Here is a very basic application that consumes (1) an external news feed and (2) a reactions feed -for pulling in the latest comments via $.get() (which will return a promise). -The application also has a function (prepareInterface()) which returns a +for pulling in the latest comments via `$.get()` (which will return a promise). +The application also has a function (`prepareInterface()`) which returns a promise to complete animating our containers for both the news and reactions. - ``` function getLatestNews() { - return $.get( "latestNews.php", function(data){ - console.log( "news data received" ); - $( ".news" ).html(data); - } ); + return $.get( "latestNews.php", function( data ) { + console.log( "news data received" ); + $( ".news" ).html( data ); + }); } function getLatestReactions() { - return $.get( "latestReactions.php", function(data){ - console.log( "reactions data received" ); - $( ".reactions" ).html(data); - } ); + return $.get( "latestReactions.php", function( data ) { + console.log( "reactions data received" ); + $( ".reactions" ).html( data ); + }); } function prepareInterface() { - return $.Deferred(function( dfd ) { - var latest = $( ".news, .reactions" ); - latest.slideDown( 500, dfd.resolve ); - latest.addClass( "active" ); - }).promise(); + return $.Deferred(function( dfd ) { + var latest = $( ".news, .reactions" ); + latest.slideDown( 500, dfd.resolve ); + latest.addClass( "active" ); + }).promise(); } $.when( - getLatestNews(), - getLatestReactions(), - prepareInterface() + getLatestNews(), + getLatestReactions(), + prepareInterface() ).then(function(){ - console.log( "fire after requests succeed" ); + console.log( "fire after requests succeed" ); }).fail(function(){ - console.log( "something went wrong!" ); + console.log( "something went wrong!" ); }); ``` diff --git a/page/code-organization/dont-repeat-yourself.md b/page/code-organization/dont-repeat-yourself.md index 320575dd..71881083 100644 --- a/page/code-organization/dont-repeat-yourself.md +++ b/page/code-organization/dont-repeat-yourself.md @@ -1,7 +1,7 @@ --- title: Keep Things DRY level: beginner -attribution: +attribution: source: http://jqfundamentals.com/legacy - jQuery Fundamentals --- @@ -9,34 +9,24 @@ Don't repeat yourself; if you're repeating yourself, you're doing it wrong. ``` // BAD -if ( $eventfade.data("currently") !== "showing" ) { - - $eventfade.stop(); - +if ( $eventfade.data( "currently" ) !== "showing" ) { + $eventfade.stop(); } -if ( $eventhover.data("currently") !== "showing" ) { - - $eventhover.stop(); - +if ( $eventhover.data( "currently" ) !== "showing" ) { + $eventhover.stop(); } -if ( $spans.data("currently") !== "showing" ) { - - $spans.stop(); - +if ( $spans.data( "currently" ) !== "showing" ) { + $spans.stop(); } // GOOD!! var $elems = [ $eventfade, $eventhover, $spans ]; $.each( $elems, function( i, elem ) { - - if ( elem.data("currently") !== "showing" ) { - - elem.stop(); - - } - + if ( elem.data( "currently" ) !== "showing" ) { + elem.stop(); + } }); ``` diff --git a/page/code-organization/feature-browser-detection.md b/page/code-organization/feature-browser-detection.md index 58f5d095..67d7040d 100644 --- a/page/code-organization/feature-browser-detection.md +++ b/page/code-organization/feature-browser-detection.md @@ -5,7 +5,7 @@ level: beginner ### Can I Use This Browser Feature? -There are a couple of common ways to check whether or not a particular feature is supported by a user's browser: +There are a couple of common ways to check whether or not a particular feature is supported by a user's browser: * Browser Detection * Specific Feature Detection @@ -26,7 +26,7 @@ While this seems to be an easy solution, there are several problems: #### Other browsers other than your target may have the same issue. -If we target a specific browser for different functionality, we implicitly exclude any browser we did not account for. This is also not future-proof. If the browser we target receives a bug fix or change, we may not be able to discern between a 'working' and 'non-working' UA string. We may also need to update our test for each new release. This isn't a maintainable solution. +If we target a specific browser for different functionality, we implicitly exclude any browser we did not account for. This is also not future-proof. If the browser we target receives a bug fix or change, we may not be able to discern between a "working" and "non-working" UA string. We may also need to update our test for each new release. This isn't a maintainable solution. #### User Agents are unreliable. @@ -51,7 +51,7 @@ Now how would you go about doing that? There are several ways to go about feature detection: * Straight JavaScript -* $.support +* `$.support` * A Helper Library #### Straight JavaScript @@ -61,16 +61,12 @@ Let's take a look at how to check whether or not a `` element exists in ``` // We want to show a graph in browsers that support canvas, // but a data table in browsers that don't. -var elem = document.createElement("canvas"); - -if ( elem.getContext && elem.getContext("2d") ) { - - showGraph(); +var elem = document.createElement( "canvas" ); +if ( elem.getContext && elem.getContext( "2d" ) ) { + showGraph(); } else { - - showTable(); - + showTable(); } ``` @@ -96,13 +92,9 @@ For example, utilizing Modernizr, we are able to do the same canvas detection te ``` if ( Modernizr.canvas ) { - - showGraphWithCanvas(); - + showGraphWithCanvas(); } else { - - showTable(); - + showTable(); } ``` @@ -112,13 +104,13 @@ That's it. Easy. So, while the Modernizr syntax is great, it can end up being quite cumbersome to have several conditionals. Secondly, we're sending the code for both conditions to every browser, regardless if we'll need it or not. -The Modernizr object exposes a `load()` method that many prefer over the syntax mentioned previously. This is due to the another library that Modernizr now uses internally: [yepnope](http://yepnopejs.com/). Testing for canvas can now become something like this: +The `Modernizr` object exposes a `load()` method that many prefer over the syntax mentioned previously. This is due to the another library that Modernizr now uses internally: [yepnope](http://yepnopejs.com/). Testing for canvas can now become something like this: ``` Modernizr.load({ - test: Modernizr.canvas, - yep : "canvas.js", - nope: "canvas-polyfill.js" + test: Modernizr.canvas, + yep: "canvas.js", + nope: "canvas-polyfill.js" }); ``` @@ -130,11 +122,11 @@ Additionally, Modernizr has a [production build configurator](http://modernizr.c #### Feature Detection Tools -- [modernizr](http://modernizr.com/) - conditionally check to see if a specific feature is available in a browser -- [html5please](http://html5please.com/) - use the new and shiny responsibly -- [html5please api](http://api.html5please.com/) - an API you can query to see how good (or bad) support for a specific feature is. -- [caniuse](http://caniuse.com/) - browser compatibility tables for HTML5, CSS3, SVG, etc… -- [yepnope](http://yepnopejs.com/) - conditional polyfill loader +- [modernizr](http://modernizr.com/) — Conditionally check to see if a specific feature is available in a browser. +- [html5please](http://html5please.com/) — Use the new and shiny responsibly. +- [html5please api](http://api.html5please.com/) — An API you can query to see how good (or bad) support for a specific feature is. +- [caniuse](http://caniuse.com/) — Browser compatibility tables for HTML5, CSS3, SVG, etc. +- [yepnope](http://yepnopejs.com/) — Conditional polyfill loader. #### Helpful Articles