Spacing
Comments
Equality
Blocks
Function Calls
Arrays & Objects
Assignment
Type Checks
Strings
(Just plain bad code)
function validateContactForm() { var result = ""; if($('#contact_form #name').val()=="") { result = false; return false; } if($("#contact_form").find('#address')[0].value!="")result=true; return result; } function doesJQElementExistInDOM(jQueryElement) { if(jQueryElement.length) return true; else return false; } function show() { for (var i = 0; i < arguments.length; i++) { jq(arguments[i]).show(); } } function hide() { for (var i = 0; i < arguments.length; i++) { jq(arguments[i]).hide(); } }
(Collection methods should be called on a jQuery object)
function enumerate( elems, start ) { for ( var i = 0; i < elems.length; i++ ) { elems.eq( i ).prepend( "" + ( i + start ) + " " ); } }; // This is no fun! enumerate( $("li"), 1 ); // Wouldn't you much rather do this? $("li").enumerate( 1 );
// Create a new jQuery collection method. $.fn.enumerate = function() { // Code goes here. }; // This doesn't error, but it does nothing and returns undefined. $("li").enumerate(); // Error: cannot call method 'css' of undefined! $("li").enumerate().css( "color", "red" ); // A simple test. var elems = $("li"); $.fn.enumerate = function() { // What, exactly, is `this`? I have a nagging suspicion... console.log( this === elems ); }; // Logs true! elems.enumerate(); // Create a chainable jQuery collection method. $.fn.enumerate = function() { return this; }; // Does nothing, but returns the same jQuery object it was called on. $("li").enumerate(); // Because of this, it's chainable! $("li").enumerate().css( "color", "red" );
(Plugins implicitly iterate… by explicitly iterating)
// No explicit iteration in the plugin... $.fn.enumerate = function( start ){ this.prepend( "" + start + " " ); return this; }; // ...means no implicit iteration when it's used. $("li").enumerate( 1 ); // Explicit iteration in the plugin... $.fn.enumerate = function( start ){ for ( var i = 0; i < this.length; i++ ) { this.eq( i ).prepend( "" + ( i + start ) + " " ); } return this; }; // ...means implicit iteration when it's used! $("li").enumerate( 1 ); // But you should explicitly iterate using jQuery's .each(). $.fn.enumerate = function( start ){ this.each(function(i){ $(this).prepend( "" + ( i + start ) + " " ); }); return this; }; // Roll the return up to remove the extra, unnecessary line, and you // have the basic "Chainable jQuery Collection Method" pattern: $.fn.enumerate = function( start ){ return this.each(function(i){ $(this).prepend( "" + ( i + start ) + " " ); }); };
How do you make a method “end”-able?
.end
Method// In brief: $("ul").find( "li" ).addClass( "fancy" ).end().addClass( "selected" ); // Select all UL elements. $("ul") // From there, find all LI descendants. .find( "li" ) // Add a class to each selected LI element. .addClass( "fancy" ) // Revert back to the previous collection. .end() // Add a class to each selected UL element. .addClass( "selected" );
.pushStack
Method// The most basic "end"-able collection method. $.fn.listitems = function() { var elems = this.find( "li" ); return this.pushStack( elems ); }; // Just like before! $("ul") .listitems() .addClass( "fancy" ) .end() .addClass( "selected" ); // An "end"-able collection method with an optional selector. $.fn.listitems = function( selector ){ var elems = this.find( "li" ); if ( selector ) { elems = elems.filter( selector ); } return this.pushStack( elems, "listitems", selector || "" ); }; // Fancy! $("ul") .listitems( ":first-child" ) .addClass( "fancy" ) .end() .addClass( "selected" );
// Our basic, chainable, "enumerate" method. $.fn.enumerate = function( start ) { return this.each(function(i){ $(this).prepend( "" + ( i + start ) + " " ); }); }; // Enumerate the listitems, effectively "setting" values. $("li").enumerate( 1 );
// Only return the appropriate value from the first selected element. $.fn.getEnumerateStartingValueOMGLongName = function() { var val = this.eq( 0 ).children( "b" ).eq( 0 ).text(); return Number( val ); }; // Because this doesn't return a jQuery object, it's not chainable! $("li").getEnumerateStartingValueOMGLongName();
// A jQuery collection method that both "sets" and "gets." $.fn.enumerate = function( start ) { if ( typeof start !== "undefined" ) { // Since `start` value was provided, enumerate and return // the initial jQuery object to allow chaining. return this.each(function(i){ $(this).prepend( "" + ( i + start ) + " " ); }); } else { // Since no `start` value was provided, function as a // getter, returing the appropriate value from the first // selected element. var val = this.eq( 0 ).children( "b" ).eq( 0 ).text(); return Number( val ); } };
// This is ok if it's just for you. $.fn.enumerate = function( start ){ return this.each(function(i){ $(this).prepend( "" + ( i + start ) + " " ); }); }; // If you're sharing with others, you need to wrap your code // in an IIFE (Immediately-Invoked Function Expression). (function($){ $.fn.enumerate = function( start ){ return this.each(function(i){ $(this).prepend( "" + ( i + start ) + " " ); }); }; })(jQuery); // Closures are great, because they allow you a private scope in // which you can store "private" variables and functions. /*! * jQuery Tiny Pub/Sub - v0.3pre - 11/4/2010 * http://benalman.com/ * * Copyright (c) 2010 "Cowboy" Ben Alman * Dual licensed under the MIT and GPL licenses. * http://benalman.com/about/license/ */ (function($){ var o = $({}); $.subscribe = function(){ o.bind.apply( o, arguments ); }; $.unsubscribe = function(){ o.unbind.apply( o, arguments ); }; $.publish = function(){ o.trigger.apply( o, arguments ); }; })(jQuery);
// What's wrong with this "jQuery plugin"? Hint: it's not // the code itself. (function($){ $.log = function( msg ) { if ( $.log.enabled && window.console ) { console.log( msg ); } }; $.log.enabled = true; })(jQuery); // Don't attach arbitrary methods to $ unless they're REALLY // jQuery plugins. var log = (function(){ function fn( msg ) { if ( log.enabled && window.console ) { console.log( msg ); } }; fn.enabled = true; return fn; })();
// This method will never be called on a collection of elements, // it's just a utility method. $.deparam = function( str ) { var obj = {}; $.each( str.split( "&" ), function(i,pair){ var nv = pair.split( "=" ); obj[ nv[0] ] = nv[ 1 ]; }); return obj; }; $.deparam( "a=1&b=2&c=3" ); // { a: "1", b: "2", c: "3" }
// For jQuery utility methods, this works. $.myPlugin = function() { // Code goes here. }; $.myPlugin.submethod = function() { // Code goes here. }; $.myPlugin(); $.myPlugin.submethod(); // For jQuery collection methods, this doesn't work. $.fn.myPlugin = function() { // Code goes here. }; $.fn.myPlugin.submethod = function() { // Code goes here. }; $("li").myPlugin(); // While this works great... $("li").myPlugin.submethod(); // This ain't gonna happen. // This kind of thing is also no good. It's technically possible // (with a lot of work) but it's confusing. For example, how do // you keep track of chaining? What does .end() do? No good. $("li").myPlugin().submethod(); // Of course you can do this (and a whole lot more), using the // jQuery UI Widget Factory, and it works great. $("li").myPlugin( "submethod", options ); // But for jQuery collection methods, this is usually sufficient. $.fn.myPluginSubmethod = function() { // Code goes here. }; $("li").myPluginSubmethod();