Skip to content

Instantly share code, notes, and snippets.

@gregnostic
Last active August 29, 2015 14:02
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gregnostic/3cc18f91aa152c05b47c to your computer and use it in GitHub Desktop.
Save gregnostic/3cc18f91aa152c05b47c to your computer and use it in GitHub Desktop.
JavaScript in Drupal
/**
* @file
* Always start with a brief, one-line description of the script.
*
* If more than one line is needed to describe the script, provide more detailed
* comment text following a blank comment line.
*/
// Use an IIFE (immediately invoked function expression) to encapsulate your
// script from other scripts loaded on the page and to prevent pollution of the
// global namespace.
//
// Always begin the IIFE with a semicolon to prevent unintended consequences
// with script aggregation and compression. Always include the 'undefined'
// parameter for undefined comparisons.
;(function (window, document, $, undefined) {
// Declare all variables used in a function at the top of the function body.
// Variables are scoped to their containing function (or object), not to block
// where they were first declared. There shouldn't be many variables here
// besides the following module variable.
// Implement the Revealing Module pattern. You can call the module whatever
// you'd like to call it. The module should always be referred to throughout
// the entire script by the name you declare here.
//
// The Revealing Module pattern can be seen as "fragile" because the module's
// public methods can't be overridden without breaking the internal function
// references. In theory, this is a bad thing. In practice, the fact that all
// of this is going on within an IIFE means that you'll almost never extend
// your modules in that way because they aren't available outside of the IIFE.
//
// Providing the name of the module for what could be an anonymous function
// seems redundant, but this helps with debugging because any errors will
// reference the module by name.
var myModule = (function myModule() {
// Because of the nature of closures, variables declared at the function
// scope will be private by default, but they may be publicly exposed later.
// Never initialize values here; only declare them.
var privateVariable,
publicVariable;
// Declare all functions as proper functions. These functions have access
// to all module variables and functions. This syntax is simpler and cleaner
// than the equivalent "var privateFunction = function () {}" syntax and is
// functionally identical. It also makes it clear that these functions are
// meant to be called within the module just like normal functions are. In
// the scope of the module, there's nothing at all special about them.
function privateFunction(content) {
// Instead of building HTML structures in the middle of code, use the
// Drupal.theme function or other templating system (e.g. Twig, Mustache).
return Drupal.theme('ExampleThemeFunction', content);
}
// If you need to process DOM nodes as they're added to the document, send
// the context as a parameter.
function publicFunction(context) {
// ...
}
// The init function is the entry point into the module.
function init() {
// Set up everything necessary for this module.
privateVariable = true;
publicVariable = 'some value';
// Actual functionality should be handled in functions.
console.log(privateFunction(publicVariable));
}
// Return an object. All object members will be publicly accessible. Nothing
// new should be defined here. The job of this object is to expose specific
// variable and function members, not to create new functionality. Public
// member names do not need to match their private counterparts, but it's a
// good idea to limit aliasing to avoid confusion.
//
// External name on the left, internal name on the right.
return {
exposedVariable: publicVariable,
process: publicFunction,
init: init
};
}());
// One behavior per script file. Each script should be responsible for doing
// just one thing, and in the simplest way possible. Rely on libraries where
// possible for broader functionality.
Drupal.behaviors.myBehaviorName = {
attach: function (context, settings) {
// The 'once' function is only necessary if you need code to execute code
// when Drupal fires attach functions.
$('body').once('my-behavior-name', function () {
// Initialize the module.
myModule.init();
});
// Anything outside the jQuery.once callback will be called during each
// attach cycle. You should submit the context for each time this code is
// run so the module is aware of what's happening on the page.
myModule.process(context);
}
}
// Put all theme functions at the end of the IIFE in alphabetical order.
Drupal.theme.prototype.ExampleThemeFunction = function (content) {
return '<div class="example">' + content + '</div>';
};
// Always put the invoking parentheses inside the wrapping parentheses. They can
// go inside or outside the wrapping parentheses, but for the sake of a single,
// consistent convention, put them on the inside.
}(this, this.document, jQuery));
// Further Reading:
//
// Immediately-Invoked Function Expression (IIFE):
// http://benalman.com/news/2010/11/immediately-invoked-function-expression/
// Again With the Module Pattern – Reveal Something to the World:
// http://christianheilmann.com/2007/08/22/again-with-the-module-pattern-reveal-something-to-the-world/
// JavaScript Programming Patterns:
// http://www.klauskomenda.com/code/javascript-programming-patterns/
// Learning JavaScript Design Patterns:
// http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/
//
// P.S. Always leave a newline at the end of the file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment