Docs, guidelines and compromising photos
To make changes to these pages, checkout the wego.github.io project and the GitHub Pages docs.
Everyone has an opinion on how to write JavaScript. Here's ours.
_
.
"use strict";
at the start of all your functions.
A sample class.
// The following example is an example and not to be taken as actual
// working code.
// Wrap your stuff inside a closure to be `call(this)` later (which usually pass
// in the `window` context.
(function() {
// Read https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode
// This is mandatory - future pull requests without this will fail.
"use strict";
// You need to bind it a global `Wego` object to be used later.
var Map = Wego.Map = function(el, options) {
// Only optional arguments should be passed in as an `options` hash.
// What is defined as required or not is purely up to the creator of the code.
// Throw an error if required arguments are not available (should we?)
if (!el) {
throw new Error('Cannot create new instance of Map without an element');
}
// Prepares the `options` in case it's `undefined`
options = options || {};
// `this.$el` should usually be the parent container.
this.$el = $(el);
// Prefer `.data` over `.attr` in these cases because `.data` allows you
// to pass in a JSON object.
// Try to keep the `data` attributes namespace to this code class so
// multiple objects can be bound to one element.
// Optional arguments should have at least one `||` fallback
this._params = options.params || this.$el.data('map-params');
// There should always be 2 ways to define options in order:
// - The `data-*` attribute on the element itself (priority)
// - Passed in as a hash when calling the constructor
this._mapId = options.mapId || this.$el.data('map-id');
// Or something more than 1 `||` fallbacks if needed
this._instant = options.instant || this.$el.data('map-instant') || false;
// It's good to specify variables here if it's gonna be used more than once
// or be overridden by the caller
this._stateClass = options.stateClass || 'is-closed';
// Optionally your code should accept callbacks for others to use
// Prefix callbacks with `on`.
// `noop` is a function that does nothing when called, perfect fallback
// for callbacks.
this._onToggle = options.onToggle || $.noop;
// Call the initializer
this._initialize();
};
// We chose to put all (public/private) methods as prototypes instead of
// defining in constructors because of memory issues versus no real
// gains otherwise.
_.extend(Map.prototype, {
// Public methods should appear before private methods so it's easier
// to find when people want to use this class.
toggle: function() {
this._create();
this.$el.toggleClass(this._stateClass);
this.refill();
// This specific callback will be called here. Where you put the callbacks
// is up to you.
this._onToggle();
// If you want your methods to be chainable, add this. Could be useful.
return this;
},
// Since we use fake private methods, all private methods should be
// prefixed with a `_`. No one outside this class should be calling any
// methods from here on.
_initialize: function() {
// If a variable is used more than once, it's generally good to set a
// `var` for it to reduce scope lookup.
var mapId = this._mapId;
// Do stuffs here that must be called on init.
this._render();
this._bind();
},
// Since most scripts will require binding events to DOM or otherwise,
// generally we put them all here, which in turn calls all the other
// binding methods.
_bind: function() {
this._bindToggle();
this._bindClickExample();
},
_bindClickExample: function() {
var self = this;
this.$el
.off('click.map')
.on('click.map', '[data-map-toggle]', function(e) {
// Stop browser default behaviors
e.preventDefault();
// Can be used to stop pjax (if you need to)
e.stopPropagation();
// This can be used in jQuery context to act as both above
// but avoid using this because it can be confusing.
return false; // DO NOT DO THIS.
});
},
_bindToggle: function() {
var self = this;
this.$el
// Remember to give your event namespaces so they can be removed if
// needed without affecting other events.
.off('toggle.map')
// Also good to remove prior events before binding new one to avoid
// event stacking for no reasons.
.on('toggle.map', function(e) {
// Then calls the public method for toggling, up to you.
self.toggle();
});
},
});
// After a class has been initialized, usually any trigger of events is bound
// to the DOM the class is associated with, so we send the DOM an event it cannot
// refuse.
$(window).on('resize scroll load DOMContentLoaded', _.throttle(function() {
$('.map').trigger('render.map');
}, 1000));
// Remember to add this after using `use strict` because strict mode has no
// implicit global/`window`.
}).call(this);
Finally to use the above class:
<div class="elem" data-map-id="123"></div>
// should init with `_mapId = 123`
new Wego.Map($('.elem'));
// should init with `_mapId = 1234`
new Wego.Map($('.elem'), { mapId: 1234 });
// add a callback to be called when toggle is triggered
new Wego.Map($('.elem'), {
onToggle: function() { console.log('pew'); }
})
Place 1 space before the leading brace.
// bad
function test(){
console.log('test');
}
// good
function test() {
console.log('test');
}
// bad
dog.set('attr',{
age: '1 year',
breed: 'Bernese Mountain Dog'
});
// good
dog.set('attr', {
age: '1 year',
breed: 'Bernese Mountain Dog'
});
Use indentation when making long method chains.
// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();
// good
$('#items')
.find('.selected')
.highlight()
.end()
.find('.open')
.updateCount();
// bad
var leds = stage.selectAll('.led').data(data).enter().append("svg:svg").class('led', true)
.attr('width', (radius + margin) * 2).append("svg:g")
.attr("transform", "translate(" + (radius + margin) + "," + (radius + margin) + ")")
.call(tron.led);
// good
var leds = stage.selectAll('.led')
.data(data)
.enter().append("svg:svg")
.class('led', true)
.attr('width', (radius + margin) * 2)
.append("svg:g")
.attr("transform", "translate(" + (radius + margin) + "," + (radius + margin) + ")")
.call(tron.led);
this
, use self
.===
and !==
over ==
and !=
.Always use var
to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace.
// bad
superPower = new SuperPower();
// good
var superPower = new SuperPower();
Use the literal syntax for object creation.
// bad
var item = new Object();
// good
var item = {};
Use the literal syntax for array creation.
// bad
var items = new Array();
// good
var items = [];
Prefix jQuery object variables with a $
.
// bad
var sidebar = $('.sidebar');
// good
var $sidebar = $('.sidebar');
Cache jQuery lookups.
// bad
function setSidebar() {
$('.sidebar').hide();
// ...stuff...
$('.sidebar').css({
'background-color': 'pink'
});
}
// good
function setSidebar() {
var $sidebar = $('.sidebar');
$sidebar.hide();
// ...stuff...
$sidebar.css({
'background-color': 'pink'
});
}
Use find
with scoped jQuery object queries.
// bad
$('.sidebar', 'ul').hide();
// bad
$('.sidebar').find('ul').hide();
// good
$('.sidebar ul').hide();
// good
$('.sidebar > ul').hide();
// good (slower)
$sidebar.find('ul');
// good (faster)
$($sidebar[0]).find('ul');
Bind event listeners to the closest non-dynamic parents.
document
or document.body
for DOM-related because of performance.When using jQuery 1.7+, use $.fn.on
instead of $.fn.bind
, $.fn.delegate
and $.fn.live
.
Use e.preventDefault()
instead of return false
. If want to prevent event bubbling, use e.stopPropagation()
.
// bad
$('a').click(function(){
return false;
});
// good
$('a').click(function(e){
e.preventDefault();
});
Namespace your events so it can be turned on/off without affecting other similar events.
var $elem = $('.something');
// bad, how to stop listening to "do three" without undoing all others?
$elem.on('click', function(e) { // do one });
$elem.on('click', function(e) { // do two });
$elem.on('click', function(e) { // do three });
// better
$elem.on('click.one', function(e) { // do one });
$elem.on('click.two', function(e) { // do two });
$elem.on('click.three', function(e) { // do three });
$elem.off('click.three'); // ta-da!
Some of the guidelines here are directly taken from Airbnb's JavaScript Style Guide.