Newer posts are loading.
You are at the newest post.
Click here to check if anything new just came in.

February 20 2014

12:34
Tags: Coding

February 17 2014

14:00

February 13 2014

11:12

Creating A Client-Side Shopping Cart


  

Session storage is a new feature introduced by the W3C’s “Web Storage” specification. It’s supported in Internet Explorer 8+, Firefox, Chrome, Safari and Opera Desktop (for a complete list, please consult “Can I Use”). In this series of articles, we’ll cover in depth a practical implementation of session storage by creating a complete e-commerce shopping cart with the sessionStorage object and jQuery.

Bear in mind that, in these articles, I’m not going to propose a new technique to replace existing server-side techniques, but rather just a proof of concept of session storage.

Session Storage: A Quick Reminder

We use sessions to store data and share such data across several pages. Usually, a user would pick a product, and we’d save the product’s name along with the chosen quantity and price.

Then, the user would fill out a form with their personal information, and we’d save it in the current session before the end of the process, which is typically the checkout page and the subsequent redirection to the payment gateway (for example, PayPal).

How are shopping carts built? PHP, for instance, makes frequent use of associative arrays to create the basic structure of a shopping cart. Associative arrays enable PHP Web developers to keep session data structured and organized.

JavaScript sessions work differently. Generally, a session expires when the user closes their browser (but bear in mind that the concept of “closing a browser” is not clear on mobile devices). When a session expires, all data stored in the session storage of a Web browser is removed. There’s no need to explicitly initialize a session because in JavaScript a session takes the form of the global sessionStorage object and is always present. It’s up to us to write data into the current session.

Session data comes in the form of key-value pairs, and the value of each key may contain only strings. To write data, we can use the sessionStorage.setItem( name, value ) method:


sessionStorage.setItem( "total", 120 );

In this case, the key named total now contains the value 120 as a string, although we’ve used an integer in our call to the .setItem() method. This value will be available until the session expires, unless we use sessionStorage.removeItem( "total" ) to remove the named key or we call sessionStorage.clear() to entirely remove all keys and values from the session storage.

Note that when a key doesn’t exist in session storage, its value is always null. Then, when we remove a key from session storage and try again to get its value, we’d simply get null.

As you may have guessed, our key now is always available, even as the user navigates the pages of our website. To get its value, we simply write the following:


var total = sessionStorage.getItem( "total" );
console.log( total ); // '120', a string

We can also update its value by using sessionStorage.setItem() again with a new value:


var total = parseInt( sessionStorage.getItem( "total" ) );
var quantity = 2;
var updatedTotal = total * quantity;
sessionStorage.setItem( "total", updatedTotal ); // '240', a string

Now, the key named total has a value of 240 with our last update. Why did we call parseInt()? This is a simple technique to convert a numerical string into a true number, ensuring that our calculation will be consistent. Remember that all values in session storage are strings, and our calculations must be between only numbers.

But wait! What about objects? Objects may be stored in session storage by first turning them into JSON strings (with JSON.stringify()) and then back into JavaScript objects (with JSON.parse()):


var cart = {
	item: "Product 1",
	price: 35.50,
	qty: 2
};
var jsonStr = JSON.stringify( cart );
sessionStorage.setItem( "cart", jsonStr );
// now the cart is {"item":"Product 1","price":35.50,"qty":2}
var cartValue = sessionStorage.getItem( "cart" );
var cartObj = JSON.parse( cartValue );
// original object

To update our object, we simply extend it and then repeat the procedure above.

Security Considerations

Security is important. If we read the security notes of the W3C’s specification, then we’d be aware of the security risks of even a client-side technology such as Web storage.

The US Computer Emergency Readiness Team’s technical paper on website security (PDF) clearly states:

“Every community organization, corporation, business, or government agency relies on an outward-facing website to provide information about themselves, announce an event, or sell a product or service. Consequently, public-facing websites are often the most targeted attack vectors for malicious activity.”

Even if a browser session ends when the browser itself is closed, malicious attacks can still take place, especially if the browser has been compromised by certain exploits. Moreover, compromised websites can often be used to spread malware that targets particular browsers.

For this reason, make sure your website is safe before relying on any technique to store data in the browser. Keeping a website safe is beyond the scope of this article, but by simply following security best practices, you should be able to benefit from Web storage without worrying too much about its security implications.

Our Sample Project: Winery

Our sample project is an online store that sells wine. It’s a simple e-commerce website whose only complication is in how its shipping charges are calculated.

In short, wines are sold in packages of six bottles. This means that the total quantity of bottles sold must always be in multiples of six. Shipping charges are calculated, then, according to the total quantity of bottles sold.

Our store will rely on PayPal, so we’ll have to create a Business account in PayPal Sandbox to test our code.

The user may add and remove products from their shopping cart, update the cart, change the quantity of each product, and empty the cart. They have to fill a form with their contact information, specifying whether their billing address is the same as their shipping address.

Before being redirected to PayPal, the user will see a summary page with their personal data, their cart, and the cart’s total price plus shipping charges.

After completing their purchase, the user should be redirected back to our website. This is the only step of the process that we can’t handle only with JavaScript. PayPal will send back various data over an HTTP request that has to be processed with a server-side language (such as PHP). If you need more information to get started with this kind of processing, please consult PayPal’s tutorial.

HTML Structure

Our project is made up of the following sections:

  • index.html
    This contains the list from which users may add products to their shopping cart, specifying the quantity for each product.
  • cart.html
    This is the shopping cart page where users may update or empty their cart. Alternatively, they can go back to the main page to continue shopping or proceed to the checkout page.
  • checkout.html
    On this page, users fill out a form with their personal information — specifically, their billing and shipping addresses.
  • order.html
    This page contains a brief summary of the user’s order plus the PayPal form. Once a user submits the form, they will be redirected to PayPal’s landing page.

We’ll go over the markup for this project in the following sections.

index.html

The main components of this page are the forms that enable the user to add products to their shopping cart.


<div class="product-description" data-name="Wine #1" data-price="5">
	<h3 class="product-name">Wine #1</h3>
		<p class="product-price">&euro; 5</p>
		<form class="add-to-cart" action="cart.html" method="post">
			<div>
				<label for="qty-1">Quantity</label>
				<input type="text" name="qty-1" id="qty-1" class="qty" value="1" />
			</div>
			<p><input type="submit" value="Add to cart" class="btn" /></p>
		</form>
</div>

The data attributes used here for storing product names and prices can be accessed via jQuery using the .data() and $.data() methods.

cart.html

Our shopping cart page is made up of three components: a table with the product’s information, an element that displays the subtotal, and a list
of cart actions.


<form id="shopping-cart" action="cart.html" method="post">
	<table class="shopping-cart">
		<thead>
			<tr>
				<th scope="col">Item</th>
				<th scope="col">Qty</th>
				<th scope="col">Price</th>
			</tr>
		</thead>
		<tbody></tbody>
	</table>
	<p id="sub-total">
		<strong>Sub Total</strong>: <span id="stotal"></span>
	</p>
	<ul id="shopping-cart-actions">
		<li>
			<input type="submit" name="update" id="update-cart" class="btn" value="Update Cart" />
		</li>
		<li>
			<input type="submit" name="delete" id="empty-cart" class="btn" value="Empty Cart" />
		</li>
		<li>
			<a href="index.html" class="btn">Continue Shopping</a>
		</li>
		<li>
			<a href="checkout.html" class="btn">Go To Checkout</a>
		</li>
	</ul>
</form>

The table contained in this page is empty, and we’ll fill it with data via JavaScript. The element that displays the subtotal works just as a placeholder for JavaScript. The first two actions, “Update Cart” and “Empty Cart,” will be handled by JavaScript, while the latter two actions are just plain links to the product’s list page and the checkout page, respectively.

checkout.html

This page has four components:

  • a table that shows the ordered items (the same table shown earlier in the shopping cart section), plus the final price and shipping charges;
  • a form in which the user must fill in their billing details;
  • a form with shipping information;
  • a checkbox to enable the user to specify that their billing details are the same as their shipping details.

<table id="checkout-cart" class="shopping-cart">
	<thead>
		<tr>
			<th scope="col">Item</th>
			<th scope="col">Qty</th>
			<th scope="col">Price</th>
		</tr>
	</thead>
	<tbody>

	</tbody>
</table>

<div id="pricing">
	<p id="shipping">
		<strong>Shipping</strong>: <span id="sshipping"></span>
	</p>
			
	<p id="sub-total">
		<strong>Total</strong>: <span id="stotal"></span>
	</p>
</div>
		 
<form action="order.html" method="post" id="checkout-order-form">
	<h2>Your Details</h2>
		<fieldset id="fieldset-billing">
			<legend>Billing</legend>
				<!-- Name, Email, City, Address, ZIP Code, Country (select box) -->

<div>
	<label for="name">Name</label>
	<input type="text" name="name" id="name" data-type="string" data-message="This field may not be empty" />
</div>

<div>
	<label for="email">Email</label>
	<input type="text" name="email" id="email" data-type="expression" data-message="Not a valid email address" />
</div>

<div>
	<label for="city">City</label>
	<input type="text" name="city" id="city" data-type="string" data-message="This field may not be empty" />
</div>

<div>
	<label for="address">Address</label>
		<input type="text" name="address" id="address" data-type="string" data-message="This field may not be empty" />
</div>

<div>
	<label for="zip">ZIP Code</label>
	<input type="text" name="zip" id="zip" data-type="string" data-message="This field may not be empty" />
</div>

<div>
	<label for="country">Country</label>
		<select name="country" id="country" data-type="string" data-message="This field may not be empty">
			<option value="">Select</option>
			<option value="US">USA</option>
			<option value="IT">Italy</option>
		</select>
</div>
</fieldset>

<div id="shipping-same">Same as Billing <input type="checkbox" id="same-as-billing" value=""/></div>

<fieldset id="fieldset-shipping">
<legend>Shipping</legend>
	<!-- Same fields as billing -->
</fieldset>

<p><input type="submit" id="submit-order" value="Submit" class="btn" /></p>

</form>

Data attributes are used here for validation. The data-type attribute specifies the type of data we’re validating, and data-message contains the error message to be shown in case of failure.

I didn’t use the email validation built into Web browsers just for the sake of simplicity, but you could use it if you want.

order.html

This final page contains a brief recap of the user’s order, their details and the PayPal form.


<h1>Your Order</h1>

<table id="checkout-cart" class="shopping-cart">
	<thead>
		<tr>
			<th scope="col">Item</th>
			<th scope="col">Qty</th>
			<th scope="col">Price</th>
		</tr>
	</thead>
	<tbody>
	</tbody>
</table>

<div id="pricing">
	<p id="shipping">
		<strong>Shipping</strong>: <span id="sshipping"></span>
	</p>

	<p id="sub-total">
		<strong>Total</strong>: <span id="stotal"></span>
	</p>
</div>

<div id="user-details">
	<h2>Your Data</h2>
		<div id="user-details-content"></div>
</div>

		 
<form id="paypal-form" action="" method="post">
	<input type="hidden" name="cmd" value="_cart" />
	<input type="hidden" name="upload" value="1" />
	<input type="hidden" name="business" value="" />

	<input type="hidden" name="currency_code" value="" />
	<input type="submit" id="paypal-btn" class="btn" value="Pay with PayPal" />
</form>

The PayPal form and other elements of this page are initially empty, except for those fields that don’t need to be generated dynamically.

JavaScript Code

The CSS layout of this project will have no actual influence on the goal we want to achieve. Even if we disabled CSS entirely, the project would continue to function, thanks to the strong relationship between the HTML’s structure and the JavaScript’s behavior.

We’ll use an object-oriented approach because of the complexity of our goals. Our object will be based on a simple constructional pattern and will use both private and public methods.

Object Structure

Our object has a very simple structure. The constructor function both initializes the top-level element that wraps our DOM’s entire structure and invokes the initialization method.


(function( $ ) {
	$.Shop = function( element ) {
		this.$element = $( element ); // top-level element
		this.init();
	};

	$.Shop.prototype = {
		init: function() {
			// initializes properties and methods
		}
	};

	$(function() {
		var shop = new $.Shop( "#site" ); // object's instance
	});

})( jQuery );

The object’s instance is created when the DOM is ready. We can test that everything has worked fine as follows:


$(function() {
	var shop = new $.Shop( "#site" );
	console.log( shop.$element );
});

This outputs the following:


x.fn.x.init[1]
	0: div#site
	context: document
	length: 1
	selector: "#site"

Now that we know our object has been instantiated correctly, we can define its properties.

Object Properties

The properties of our object break down into two categories: first, the properties for handling calculations, forms and validation, and secondly, the references to HTML elements.


$.Shop.prototype = {
	init: function() {
		// Properties

			this.cartPrefix = "winery-"; // prefix string to be prepended to the cart's name in session storage
			this.cartName = this.cartPrefix + "cart"; // cart's name in session storage
			this.shippingRates = this.cartPrefix + "shipping-rates"; // shipping rates key in session storage
			this.total = this.cartPrefix + "total"; // total key in the session storage
			this.storage = sessionStorage; // shortcut to sessionStorage object


			this.$formAddToCart = this.$element.find( "form.add-to-cart" ); // forms for adding items to the cart
			this.$formCart = this.$element.find( "#shopping-cart" ); // Shopping cart form
			this.$checkoutCart = this.$element.find( "#checkout-cart" ); // checkout form cart
			this.$checkoutOrderForm = this.$element.find( "#checkout-order-form" ); // checkout user details form
			this.$shipping = this.$element.find( "#sshipping" ); // element that displays the shipping rates
			this.$subTotal = this.$element.find( "#stotal" ); // element that displays the subtotal charges
			this.$shoppingCartActions = this.$element.find( "#shopping-cart-actions" ); // cart actions links
			this.$updateCartBtn = this.$shoppingCartActions.find( "#update-cart" ); // update cart button
			this.$emptyCartBtn = this.$shoppingCartActions.find( "#empty-cart" ); // empty cart button
			this.$userDetails = this.$element.find( "#user-details-content" ); // element that displays the user's information
			this.$paypalForm = this.$element.find( "#paypal-form" ); // PayPal form


			this.currency = "&euro;"; // HTML entity of the currency to be displayed in layout
			this.currencyString = "€"; // currency symbol as text string
			this.paypalCurrency = "EUR"; // PayPal's currency code
			this.paypalBusinessEmail = "yourbusiness@email.com"; // your PayPal Business account email address
			this.paypalURL = "https://www.sandbox.paypal.com/cgi-bin/webscr"; // URL of the PayPal form

			// object containing patterns for form validation
			this.requiredFields = {
				expression: {
					value: /^([\w-\.]+)@((?:[\w]+\.)+)([a-z]){2,4}$/
				},

				str: {
					value: ""
				}

			};

			// public methods invocation
	}
};

Let’s go over these properties one by one.

Storage and other properties:

  • cartPrefix
    A prefix to be prepended to the cart’s name key in session storage
  • cartName
    The cart’s name key in session storage (combines the cartPrefix string with the cart string)
  • shippingRates
    The shipping rate key in session storage
  • total
    The total’s key in session storage
  • storage
    Shortcut to the sessionStorage object.
  • currency
    An HTML entity used to display the current currency in the layout
  • currencyString
    The current currency symbol used in the element’s text
  • paypalCurrency
    PayPal’s currency text code
  • paypalBusinessEmail
    The email address of your PayPal Business account
  • paypalURL
    The URL of PayPal’s form (defaults to the URL of PayPal Sandbox)
  • requiredFields
    An object containing the patterns and rules for form validation

References to elements:

  • $formAddToCart
    The forms for adding products to the shopping cart
  • $formCart
    The shopping cart form
  • $checkoutCart
    The checkout’s shopping cart form
  • $checkoutOrderForm
    The checkout’s form where users input their personal information
  • $shipping
    The element that contains and displays shipping rates
  • $subTotal
    The element that contains and displays the total charges
  • $shoppingCartActions
    The elements that contain the actions related to the shopping cart
  • $updateCartBtn
    The button to update the shopping cart
  • $emptyCartBtn
    The button for emptying the cart
  • $userDetails
    The element that contains and displays the information entered by the user
  • $paypalForm
    PayPal’s form

All of the elements are prefixed with the $ sign, meaning that they’re jQuery objects. But not all of these elements are available on all pages. To check whether a jQuery element exists, simply test its length property:


if( $element.length ) {
	// the element exists
}

Another approach, not used in our project, is to add a particular ID or class to the body element and perform actions conditionally:


var $body = $( "body" ),
	page = $body.attr( "id" );

	switch( page ) {
		case "product-list":
			// actions for handling products
			break;
		case "shopping-cart":
			// actions for handling the shopping cart
			break;
		case "checkout":
			// actions for handling the checkout's page
			break;
		default:
			break;
	}

Object Methods

The actions of our code take place in our object’s methods, which, in turn, can be divided into public and private methods. Private methods operate in the background, so to speak, and help the public methods perform their tasks. These methods are prefixed with an underscore and are never used directly.

Public methods, meanwhile, operate directly on page elements and data, and they’re unprefixed. We’ve already seen the init() method, which simply initializes properties and other public methods in the object’s constructor function. The other methods will be explained below.

Private Methods (Helpers)

The first private method, _emptyCart(), simply empties the current session storage in the browser:


$.Shop.prototype = {
	// empties session storage

	_emptyCart: function() {
		this.storage.clear();
	}
};

To format a number by a set number of decimal places, we implement the _formatNumber() method:


/* Format a number by decimal places
 * @param num Number the number to be formatted
 * @param places Number the decimal places
 * @returns n Number the formatted number
*/


_formatNumber: function( num, places ) {
	var n = num.toFixed( places );
	return n;
}

This method makes use of JavaScript’s toFixed() method of the Number object. Its role in our project is to properly format prices.

Because not all of the prices in our pages are contained in data attributes, we need a specialized method to extract the numeric portion of a string from text nodes. This method is named _extractPrice():


/* Extract the numeric portion from a string
 * @param element Object the jQuery element that contains the relevant string
 * @returns price String the numeric string
 */


_extractPrice: function( element ) {
	var self = this;
	var text = element.text();
	var price = text.replace( self.currencyString, "" ).replace( " ", "" );
	return price;
}

Above, self is a reference to the $.Shop object, and we’ll need it every time we want to access a property or a method of our object without worrying much about scope.

You can bulletproof this method by adding a further routine that strips out all trailing white space:


var text = $.trim( element.text() );

Bear in mind that jQuery’s $.trim() method removes all new lines, spaces (including non-breaking spaces) and tabs from the beginning and end of a string. If these white space characters occur in the middle of a string, they are preserved.

Then, we need two methods to convert strings into numbers and numbers into strings. This is necessary to perform calculations and to display the results on our pages.


/* Converts a numeric string into a number
 * @param numStr String the numeric string to be converted
 * @returns num Number the number, or false if the string cannot be converted
 */

_convertString: function( numStr ) {
	var num;
	if( /^[-+]?[0-9]+\.[0-9]+$/.test( numStr ) ) {
		num = parseFloat( numStr );
	} else if( /^\d+$/.test( numStr ) ) {
		num = parseInt( numStr );
	} else {
		num = Number( numStr );
	}

	if( !isNaN( num ) ) {
		return num;
	} else {
		console.warn( numStr + " cannot be converted into a number" );
		return false;
	}
},

/* Converts a number to a string
 * @param n Number the number to be converted
 * @returns str String the string returned
 */

_convertNumber: function( n ) {
	var str = n.toString();
	return str;
}

Above, _convertString() runs the following tests:

  1. Does the string have a decimal format? If so, it uses the parseFloat() function.
  2. Does the string have an integer format? If so, it uses the parseInt() function.
  3. If the format of the string cannot be detected, it uses the Number() constructor.
  4. If the result is a number (tested with the isNaN() function), it returns the number. Otherwise, it outputs a warning to the JavaScript console and returns false.

By contrast, _convertNumber() simply invokes the toString() method to convert a number into a string.

The next step is to define two methods to convert a JavaScript object into a JSON string and a JSON string back into a JavaScript object:


/* Converts a JSON string to a JavaScript object
 * @param str String the JSON string
 * @returns obj Object the JavaScript object
 */

_toJSONObject: function( str ) {
	var obj = JSON.parse( str );
	return obj;
},

/* Converts a JavaScript object to a JSON string
 * @param obj Object the JavaScript object
 * @returns str String the JSON string
 */


_toJSONString: function( obj ) {
	var str = JSON.stringify( obj );
	return str;
}

The first method makes use of the JSON.parse() method, while the latter invokes the JSON.stringify() method (see Mozilla Developer Network’s article on “Using Native JSON”).

Why do we need these methods? Because our cart will also store the information related to each product using the following data format (spaces added for legibility):

Key Value winery-cart { "items": [ { "product": "Wine #1", "qty": 5, "price": 5 } ] }

The winery-cart key contains a JSON string that represents an array of objects (i.e. items) in which each object shows the relevant information about a product added by the user — namely, the product’s name, the quantity and the price.

It’s pretty obvious that we also now need a specialized method to add items to this particular key in session storage:


/* Add an object to the cart as a JSON string
 * @param values Object the object to be added to the cart
 * @returns void
 */


_addToCart: function( values ) {
	var cart = this.storage.getItem( this.cartName );
	var cartObject = this._toJSONObject( cart );
	var cartCopy = cartObject;
	var items = cartCopy.items;
	items.push( values );

	this.storage.setItem( this.cartName, this._toJSONString( cartCopy ) );
}

This method gets the cart’s key from session storage, converts it to a JavaScript object and adds a new object as a JSON string to the cart’s array. The newly added object has the following format:


this._addToCart({
	product: "Test",
	qty: 1,
	price: 2
});

Now, our cart key will look like this:

Key Value winery-cart { "items": [ { "product": "Wine #1", "qty": 5, "price": 5 }, { "product": "Test", "qty": 1, "price": 2 } ] }

Shipping is calculated according to the overall number of products added to the cart, not the quantity of each individual product:


/* Custom shipping rates calculated based on total quantity of items in cart
 * @param qty Number the total quantity of items
 * @returns shipping Number the shipping rates
 */

_calculateShipping: function( qty ) {
	var shipping = 0;
	if( qty >= 6 ) {
		shipping = 10;
	}
	if( qty >= 12 && qty <= 30 ) {
		shipping = 20;	
	}

	if( qty >= 30 && qty <= 60 ) {
		shipping = 30;	
	}

	if( qty > 60 ) {
		shipping = 0;
	}

	return shipping;

}

You can replace this method’s routines with your own. In this case, shipping charges are calculated based on specific amounts.

We also need to validate the checkout form where users insert their personal information. The following method takes into account the special visibility toggle by which the user may specify that their billing information is the same as their shipping information.


/* Validates the checkout form
 * @param form Object the jQuery element of the checkout form
 * @returns valid Boolean true for success, false for failure
 */


_validateForm: function( form ) {
		var self = this;
		var fields = self.requiredFields;
		var $visibleSet = form.find( "fieldset:visible" );
		var valid = true;

		form.find( ".message" ).remove();
			
	$visibleSet.each(function() {

		$( this ).find( ":input" ).each(function() {
		var $input = $( this );
		var type = $input.data( "type" );
		var msg = $input.data( "message" );
				
		if( type == "string" ) {
			if( $input.val() == fields.str.value ) {
				$( "<span class='message'/>" ).text( msg ).
				insertBefore( $input );

				valid = false;
			}
		} else {
			if( !fields.expression.value.test( $input.val() ) ) {
				$( "<span class='message'/>" ).text( msg ).
				insertBefore( $input );

				valid = false;
			}
		}

	});
	});

	return valid;
}

When validation messages are added upon the form being submitted, we need to clear these messages before going any further. In this case, we take into account only the fields contained in a fieldset element that is still visible after the user has checked the visibility toggle.

Validation takes place by checking whether the current field requires a simple string comparison (data-type="string") or a regular expression test (data-type="expression"). Our tests are based on the requiredFields property. If there’s an error, we’ll show a message by using the data-message attribute of each field.

Note that the validation routines used above have been inserted just for demonstration purposes, and they have several flaws. For better validation, I recommend a dedicated jQuery plugin, such as jQuery Validation.

Last but not least is registering the information that the user has entered in the checkout form:


/* Save the data entered by the user in the checkout form
 * @param form Object the jQuery element of the checkout form
 * @returns void
 */


_saveFormData: function( form ) {
	var self = this;
	var $visibleSet = form.find( "fieldset:visible" );
			
	$visibleSet.each(function() {
		var $set = $( this );
		if( $set.is( "#fieldset-billing" ) ) {
			var name = $( "#name", $set ).val();
			var email = $( "#email", $set ).val();
			var city = $( "#city", $set ).val();
			var address = $( "#address", $set ).val();
			var zip = $( "#zip", $set ).val();
			var country = $( "#country", $set ).val();

			self.storage.setItem( "billing-name", name );
			self.storage.setItem( "billing-email", email );
			self.storage.setItem( "billing-city", city );
			self.storage.setItem( "billing-address", address );
			self.storage.setItem( "billing-zip", zip );
			self.storage.setItem( "billing-country", country );
		} else {
			var sName = $( "#sname", $set ).val();
			var sEmail = $( "#semail", $set ).val();
			var sCity = $( "#scity", $set ).val();
			var sAddress = $( "#saddress", $set ).val();
			var sZip = $( "#szip", $set ).val();
			var sCountry = $( "#scountry", $set ).val();

			self.storage.setItem( "shipping-name", sName );
			self.storage.setItem( "shipping-email", sEmail );
			self.storage.setItem( "shipping-city", sCity );
			self.storage.setItem( "shipping-address", sAddress );
			self.storage.setItem( "shipping-zip", sZip );
			self.storage.setItem( "shipping-country", sCountry );

		}
	});
}

Again, this method takes into account the visibility of the fields based on the user’s choice. Once the form has been submitted, our session storage may have the following details added to it:

Key Value billing-name John Doe billing-email jdoe@localhost billing-city New York billing-address Street 1 billing-zip 1234 billing-country USA

Public Methods

Our public methods are invoked in the initialization method (init()). The first thing to do is create the initial keys and values in session storage.


// Creates the cart keys in session storage

createCart: function() {
	if( this.storage.getItem( this.cartName ) == null ) {

		var cart = {};
		cart.items = [];
			
		this.storage.setItem( this.cartName, this._toJSONString( cart ) );
		this.storage.setItem( this.shippingRates, "0" );
		this.storage.setItem( this.total, "0" );
	}
}

The first check tests whether our values have already been added to session storage. We need this test because we could actually overwrite our values if we run this method every time a document has finished loading.

Now, our session storage looks like this:

Key Value winery-cart {“items”:[]} winery-shipping-rates 0 winery-total 0

Now, we need to handle the forms where the user may add products to their shopping cart:


// Adds items to shopping cart

handleAddToCartForm: function() {
	var self = this;
	self.$formAddToCart.each(function() {
		var $form = $( this );
		var $product = $form.parent();
		var price = self._convertString( $product.data( "price" ) );
		var name =  $product.data( "name" );

		$form.on( "submit", function() {
			var qty = self._convertString( $form.find( ".qty" ).val() );
			var subTotal = qty * price;
			var total = self._convertString( self.storage.getItem( self.total ) );
			var sTotal = total + subTotal;
			self.storage.setItem( self.total, sTotal );
			self._addToCart({
				product: name,
				price: price,
				qty: qty
			});
			var shipping = self._convertString( self.storage.getItem( self.shippingRates ) );
			var shippingRates = self._calculateShipping( qty );
			var totalShipping = shipping + shippingRates;

			self.storage.setItem( self.shippingRates, totalShipping );
		});
	});
}

Every time a user submits one of these forms, we have to read the product quantity specified by the user and multiply it by the unit price. Then, we need to read the total’s key contained in session storage and update its value accordingly. Having done this, we call the _addToCart() method to store the product’s details in storage. The quantity specified will also be used to calculate the shipping rate by comparing its value to the value already stored.

Suppose that a user chooses the first product, Wine #1, whose price is €5.00, and specifies a quantity of 5. The session storage would look like this once the form has been submitted:

Key Value winery-cart {“items”:[{"product":"Wine #1","price":5,"qty":5}]} winery-shipping-rates 0 winery-total 25

Suppose the same user goes back to the product list and chooses Wine #2, whose price is €8.00, and specifies a quantity of 2:

Key Value winery-cart {“items”:[{"product":"Wine #1","price":5,"qty":5},{"product":"Wine #2","price":8,"qty":2}]} winery-shipping-rates 0 winery-total 41

Finally, our eager user returns again to the product list, chooses Wine #3, whose price is €11.00, and specifies a quantity of 6:

Key Value winery-cart {“items”:[{"product":"Wine #1","price":5,"qty":5},{"product":"Wine #2","price":8,"qty":2},{"product":"Wine #3","price":11,"qty":6}]} winery-shipping-rates 10 winery-total 107

At this point, we need to accurately display the cart when the user goes to the shopping cart page or checkout page:


// Displays the shopping cart

displayCart: function() {
	if( this.$formCart.length ) {
		var cart = this._toJSONObject( this.storage.getItem( this.cartName ) );
		var items = cart.items;
		var $tableCart = this.$formCart.find( ".shopping-cart" );
		var $tableCartBody = $tableCart.find( "tbody" );


		for( var i = 0; i < items.length; ++i ) {
			var item = items[i];
			var product = item.product;
			var price = this.currency + " " + item.price;
			var qty = item.qty;
			var html = "<tr><td class='pname'>" + product + "</td>" + "<td class='pqty'><input type='text' value='" + qty + "' class='qty'/></td>" + "<td class='pprice'>" + price + "</td></tr>";

			$tableCartBody.html( $tableCartBody.html() + html );
		}

		var total = this.storage.getItem( this.total );
		this.$subTotal[0].innerHTML = this.currency + " " + total;
	} else if( this.$checkoutCart.length ) {
		var checkoutCart = this._toJSONObject( this.storage.getItem( this.cartName ) );
		var cartItems = checkoutCart.items;
		var $cartBody = this.$checkoutCart.find( "tbody" );

		for( var j = 0; j < cartItems.length; ++j ) {
			var cartItem = cartItems[j];
			var cartProduct = cartItem.product;
			var cartPrice = this.currency + " " + cartItem.price;
			var cartQty = cartItem.qty;
			var cartHTML = "<tr><td class='pname'>" + cartProduct + "</td>" + "<td class='pqty'>" + cartQty + "</td>" + "<td class='pprice'>" + cartPrice + "</td></tr>";

			$cartBody.html( $cartBody.html() + cartHTML );
		}

		var cartTotal = this.storage.getItem( this.total );
		var cartShipping = this.storage.getItem( this.shippingRates );
		var subTot = this._convertString( cartTotal ) + this._convertString( cartShipping );

		this.$subTotal[0].innerHTML = this.currency + " " + this._convertNumber( subTot );
		this.$shipping[0].innerHTML = this.currency + " " + cartShipping;
			
	}
}

If the cart’s table is on the shopping cart page, then this method iterates over the array of objects contained in the winery-cart key and populates the table by adding a text field to allow users to modify the quantity of each product. For the sake of simplicity, I didn’t include an action to remove an item from the cart, but that procedure is pretty simple:

  1. Get the items array, contained in session storage.
  2. Get the product’s name, contained in the td element with the pname class.
  3. Create a new array by filtering out the item with the product’s name, obtained in step 2 (you can use $.grep()).
  4. Save the new array in the winery-cart key.
  5. Update the total and shipping charge values.

var items = [
	{
		product: "Test",
		qty: 1,
		price: 5
	},
	{
		product: "Foo",
		qty: 5,
		price: 10
	},
	{
		product: "Bar",
		qty: 2,
		price: 8
	}
];


items = $.grep( items, function( item ) {
	return item.product !== "Test";

});

console.log( items ); 

/*
	Array[2]
		0: Object
			price: 10
			product: "Foo"
			qty: 5
		1: Object
			price: 8
			product: "Bar"
			qty: 2
*/

Then, we need a method that updates the cart with a new quantity value for each product:


// Updates the cart

updateCart: function() {
		var self = this;
	if( self.$updateCartBtn.length ) {
		self.$updateCartBtn.on( "click", function() {
			var $rows = self.$formCart.find( "tbody tr" );
			var cart = self.storage.getItem( self.cartName );
			var shippingRates = self.storage.getItem( self.shippingRates );
			var total = self.storage.getItem( self.total );

			var updatedTotal = 0;
			var totalQty = 0;
			var updatedCart = {};
			updatedCart.items = [];

			$rows.each(function() {
				var $row = $( this );
				var pname = $.trim( $row.find( ".pname" ).text() );
				var pqty = self._convertString( $row.find( ".pqty > .qty" ).val() );
				var pprice = self._convertString( self._extractPrice( $row.find( ".pprice" ) ) );

				var cartObj = {
					product: pname,
					price: pprice,
					qty: pqty
				};

				updatedCart.items.push( cartObj );

				var subTotal = pqty * pprice;
				updatedTotal += subTotal;
				totalQty += pqty;
			});
				
			self.storage.setItem( self.total, self._convertNumber( updatedTotal ) );
			self.storage.setItem( self.shippingRates, self._convertNumber( self._calculateShipping( totalQty ) ) );
			self.storage.setItem( self.cartName, self._toJSONString( updatedCart ) );

		});
	}
}

Our method loops through all of the relevant table cells of the cart and builds a new object to be inserted in the winery-cart key. It also recalculates the total price and shipping charge by taking into account the newly inserted values of the quantity fields.

Suppose that a user changes the quantity of Wine #2 from 2 to 6:

Key Value winery-cart {“items”:[{"product":"Wine #1","price":5,"qty":5},{"product":"Wine #2","price":8,"qty":6},{"product":"Wine #3","price":11,"qty":6}]} winery-shipping-rates 20 winery-total 139

If the user wants to empty their cart and start over, we simply have to add the following action:


// Empties the cart by calling the _emptyCart() method
// @see $.Shop._emptyCart()

emptyCart: function() {
	var self = this;
	if( self.$emptyCartBtn.length ) {
		self.$emptyCartBtn.on( "click", function() {
			self._emptyCart();
		});
	}
}

Now, session storage has been emptied entirely, and the user may start making purchases again. However, if they decide to finalize their order instead, then we need to handle the checkout form when they insert their personal information.


// Handles the checkout form by adding a validation routine and saving user’s info in session storage

handleCheckoutOrderForm: function() {
	var self = this;
	if( self.$checkoutOrderForm.length ) {
		var $sameAsBilling = $( "#same-as-billing" );
		$sameAsBilling.on( "change", function() {
			var $check = $( this );
			if( $check.prop( "checked" ) ) {
				$( "#fieldset-shipping" ).slideUp( "normal" );
			} else {
				$( "#fieldset-shipping" ).slideDown( "normal" );
			}
		});

		self.$checkoutOrderForm.on( "submit", function() {
			var $form = $( this );
			var valid = self._validateForm( $form );
	
			if( !valid ) {
				return valid;
			} else {
				self._saveFormData( $form );
			}
		});
	}
}

The first thing we need to do is hide the shipping fields if the user checks the toggle that specifies that their billing information is the same as their shipping information. We use the change event, combined with jQuery’s .prop() method. (If you’re curious about the difference between .prop() and .attr(), StackOverflow has a good discussion of it.)

Then, we validate the form by returning a false value in case of errors, thus preventing the form from being submitted. If validation succeeds, we save the user’s data in storage. For example:

Key Value winery-cart {“items”:[{"product":"Wine #1","price":5,"qty":5},{"product":"Wine #2","price":8,"qty":6},{"product":"Wine #3","price":11,"qty":6}]} winery-shipping-rates 20 winery-total 139 billing-name John Doe billing-email jdoe@localhost billing-city New York billing-address Street 1 billing-zip 1234 billing-country USA

The final step is the page with the PayPal form. First, we need to display the user’s information gathered on the checkout page:


// Displays the user's information

displayUserDetails: function() {
	if( this.$userDetails.length ) {
		if( this.storage.getItem( "shipping-name" ) == null ) {
			var name = this.storage.getItem( "billing-name" );
			var email = this.storage.getItem( "billing-email" );
			var city = this.storage.getItem( "billing-city" );
			var address = this.storage.getItem( "billing-address" );
			var zip = this.storage.getItem( "billing-zip" );
			var country = this.storage.getItem( "billing-country" );

			var html = "<div class='detail'>";
				html += "<h2>Billing and Shipping</h2>";
				html += "<ul>";
				html += "<li>" + name + "</li>";
				html += "<li>" + email + "</li>";
				html += "<li>" + city + "</li>";
				html += "<li>" + address + "</li>";
				html += "<li>" + zip + "</li>";
				html += "<li>" + country + "</li>";
				html += "</ul></div>";

			this.$userDetails[0].innerHTML = html;
		} else {
			var name = this.storage.getItem( "billing-name" );
			var email = this.storage.getItem( "billing-email" );
			var city = this.storage.getItem( "billing-city" );
			var address = this.storage.getItem( "billing-address" );
			var zip = this.storage.getItem( "billing-zip" );
			var country = this.storage.getItem( "billing-country" );

			var sName = this.storage.getItem( "shipping-name" );
			var sEmail = this.storage.getItem( "shipping-email" );
			var sCity = this.storage.getItem( "shipping-city" );
			var sAddress = this.storage.getItem( "shipping-address" );
			var sZip = this.storage.getItem( "shipping-zip" );
			var sCountry = this.storage.getItem( "shipping-country" );

			var html = "<div class='detail'>";
				html += "<h2>Billing</h2>";
				html += "<ul>";
				html += "<li>" + name + "</li>";
				html += "<li>" + email + "</li>";
				html += "<li>" + city + "</li>";
				html += "<li>" + address + "</li>";
				html += "<li>" + zip + "</li>";
				html += "<li>" + country + "</li>";
				html += "</ul></div>";

				html += "<div class='detail right'>";
				html += "<h2>Shipping</h2>";
				html += "<ul>";
				html += "<li>" + sName + "</li>";
				html += "<li>" + sEmail + "</li>";
				html += "<li>" + sCity + "</li>";
				html += "<li>" + sAddress + "</li>";
				html += "<li>" + sZip + "</li>";
				html += "<li>" + sCountry + "</li>";
				html += "</ul></div>";

			this.$userDetails[0].innerHTML = html;	

		}
	}
}

Our method first checks whether the user has inputted either billing or shipping information or both. Then, it simply builds an HTML fragment by getting the user’s data from session storage.

Finally, the user may buy the products by submitting the PayPal form. The form redirects them to PayPal, but the fields need to be filled in properly before the form can be submitted.


// Appends the required hidden values to PayPal's form before submitting

populatePayPalForm: function() {
	var self = this;
	if( self.$paypalForm.length ) {
		var $form = self.$paypalForm;
		var cart = self._toJSONObject( self.storage.getItem( self.cartName ) );
		var shipping = self.storage.getItem( self.shippingRates );
		var numShipping = self._convertString( shipping );
		var cartItems = cart.items; 
		var singShipping = Math.floor( numShipping / cartItems.length );

		$form.attr( "action", self.paypalURL );
		$form.find( "input[name='business']" ).val( self.paypalBusinessEmail );
		$form.find( "input[name='currency_code']" ).val( self.paypalCurrency );

		for( var i = 0; i < cartItems.length; ++i ) {
			var cartItem = cartItems[i];
			var n = i + 1;
			var name = cartItem.product;
			var price = cartItem.price;
			var qty = cartItem.qty;

			$( "<div/>" ).html( "<input type='hidden' name='quantity_" + n + "' value='" + qty + "'/>" ).
			insertBefore( "#paypal-btn" );
			$( "<div/>" ).html( "<input type='hidden' name='item_name_" + n + "' value='" + name + "'/>" ).
			insertBefore( "#paypal-btn" );
			$( "<div/>" ).html( "<input type='hidden' name='item_number_" + n + "' value='SKU " + name + "'/>" ).
			insertBefore( "#paypal-btn" );
			$( "<div/>" ).html( "<input type='hidden' name='amount_" + n + "' value='" + self._formatNumber( price, 2 ) + "'/>" ).
			insertBefore( "#paypal-btn" );
			$( "<div/>" ).html( "<input type='hidden' name='shipping_" + n + "' value='" + self._formatNumber( singShipping, 2 ) + "'/>" ).
			insertBefore( "#paypal-btn" );

		}



	}
}

First, we get some important information from session storage — namely, the shipping rate and the total number of items in the cart. We divide the total shipping amount by the number of items to get the shipping rate for each item.

Then, we set the URL for the action attribute of the form, together with our business email and currency code (taken from the paypalBusinessEmail and paypalCurrency properties, respectively).

Finally, we loop through the items of our cart, and we append to the form several hidden input elements containing the quantities, the names of the products, the number of items for each product, the prices (amounts), and the unit shipping rates.

The monetary values are formatted as 00,00. Explaining all of the possible values of a PayPal form and the various types of PayPal forms goes well beyond the scope of this article, If you want to go deeper, I recommend the following reading:

Preview And Source Code

The following video shows the result. I’ve omitted PayPal’s landing page to protect my account’s data, but you can see it as a screenshot.

Get the code from the GitHub repository. Just change the paypalBusinessEmail property of the $.Shop object to your PayPal Sandbox email account.

Other Resources

(al, ea)


© Gabriele Romanato for Smashing Magazine, 2014.

Tags: Coding

February 12 2014

10:24

How To Build A CLI Tool With Node.js And PhantomJS


  

In this article, we’ll go over the concepts and techniques required to build a command line tool using Node.js and PhantomJS. Building a command line tool enables you to automate a process that would otherwise take a lot longer.

Command line tools are built in a myriad of languages, but the one we’ll focus on is Node.js.

What We’ll Cover

  • Secret sauce
  • Installing Node.js and npm
  • Process
  • Automation
  • PhantomJS
  • Squirrel
  • How it works
  • The code
  • Packaging
  • Publishing
  • Conclusion

Secret Sauce

For those short on time, I’ve condensed the core process into three steps. This is the secret sauce to convert your Node.js script into a fully functioning command line tool. But do stick around to see what else I have to show you.

  1. In your package.json file, include the following settings:
    • "preferGlobal": "true"
    • "bin": { "name-of-command": "path-to-script.js" }
  2. Add #!/usr/bin/env node to path-to-script.js.
  3. To test your new command (name-of-command), use npm link.

The rest of the process is just deciding what functionality to implement.

Installing Node.js And npm

To install Node.js, you have a few options:

Note that npm is installed as part of Node.js; there is no separate installation.

To test that Node.js and npm are installed correctly, run the following commands in your terminal:

  • node --version
  • npm --version

Process

Let’s consider a sample process: generating an Application Cache manifest file.

In case you are unfamiliar with AppCache, it enables you to take your application offline by specifying pages and resources to cache in the event that the user loses their Internet connection or tries to access your application later offline.

Typically, you would create an appcache.manifest file, where you would configure the offline settings.

We won’t go into much detail about AppCache itself because that would distract us from the purpose of this article. Nevertheless, below are the lines for a sample file:


CACHE MANIFEST

CACHE:
foo.jpg
index.html
offline.html
styles.css
behaviours.js

NETWORK:
*

FALLBACK:
/ /offline.html

As you can see, we’ve specified the following:

  • a JPG image,
  • two HTML files,
  • a CSS file,
  • a JavaScript file.

These are the resources that we want to cache in case the user goes offline.

We’ve also specified that all other items requested by the user should require a network to be accessed.

Finally, we’ve stated that any file that should be cached but isn’t yet should redirect the user to a file named offline.html.

Automation

Having to manually look up all of the images, style sheets, scripts and other pages linked from a Web page would be tedious. Thus, we’re trying to automate the process of generating an AppCache manifest file.

We could do this by writing some Node.js code along with some additional tools, but that wouldn’t be very easy (even for the person writing the script), because we would need to open the code and tell it which Web page to interrogate.

We also want other people to have the benefit of this tool, without their needing to download a folder full of code, change certain lines of code and run commands to run the scripts.

This is why a command line tool would help.

PhantomJS

First, we want to figure out how to solve this problem.

We’ll use a tool named PhantomJS, which is a headless (i.e. chromeless) browser.

Specifically, it’s a headless WebKit browser, which provides a JavaScript API that we can tap into and that lets us do things such as open Web pages and analyze their network requests. (It does many other things, but those are the two fundamental aspects we’re interested in.)

We can use a Node.js module to load PhantomJS and interact with its API. We can then convert our code into a command line tool with relative ease using Node.js’s package manager, npm, and a package.json file.

Squirrel

Luckily, I’ve already done the work for you. It’s an open-source project named Squirrel.

To install it, run the command npm install -g squirrel-js.

Once it’s installed, you can use it by running the command squirrel [url]. For example, squirrel bbc.co.uk/news.

This would generate (in the current directory) an appcache.manifest file populated with all relevant page resources.

How It Works

I started Squirrel by first writing the relevant Node.js and PhantomJS code to incorporate the functionality I was after.

Then, I added a script that bootstraps that code and allows me to take arguments that configure how the code runs.

I ended up with two scripts:

The first script sets up the work:

  • We specify the environment in which we want the script to execute (in this case, Node.js).
  • Parse the arguments passed by the user.
  • Read an internal (i.e. dummy) appcache.manifest file.
  • Open a shell child process, call PhantomJS and pass it the script that we want it to execute (in this case, appcache.js) and the dummy manifest file.
  • When the second script finishes its work (collating the Web page data), return to this first script and display some statistical information to the user and generate the manifest file.

The second script processes the Web page that the user has requested:

  • We take in the dummy manifest file.
  • Create listeners for the page resources that are requested.
  • Set the viewport size.
  • Open the Web page and store the resources.
  • Get all links from the page (by executing JavaScript code directly in the Web page).
  • Convert the contents of the manifest file and inject the resources found, and then return that as a JSON file.

The Code

Now that you understand what the code does, let’s review it. I’ll show the code in its entirely, and then we’ll go through it piecemeal.

squirrel.js


#!/usr/bin/env node

var userArguments = process.argv.slice(2); // Copies arguments list but removes first two options (script exec type & exec location)

if (userArguments.length > 1) {
    throw new Error('Only one argument may be specified (the URL for which you want to generate the AppCache.)');
}

var fs               = require('fs');
var shell            = require('child_process').execFile;
var phantomjs        = require('phantomjs').path;
var scriptToExecute  = __dirname + '/appcache.js';
var manifest         = __dirname + '/../appcache.manifest';
var url              = userArguments[0];
var manifestContent;
var data;

fs.readFile(manifest, bootstrap);

function bootstrap(err, contentAsBuffer) {
    if (err) throw err;

    manifestContent = contentAsBuffer.toString('utf8');

    shell(phantomjs, [scriptToExecute, url, manifestContent], function(err, stdout, stderr) {
        if (err) throw err;

        // Sometimes an error in the loaded page's JavaScript doesn't get picked up or thrown,
        // but the error comes in via stdout and causes JSON parsing to break
        try {
            data = JSON.parse(stdout);
        } catch(err) {
            log('Whoops! It seems there was an error? You\'ll find the stack trace below.');
            error(err);
        }

        displayStatistics();
        createManifestFile();
    });
}

function displayStatistics() {
    log(''); // Adds extra line of spacing when displaying the results
    log('Links: '      + data.links);
    log('Images: '     + data.images);
    log('CSS: '        + data.css);
    log('JavaScript: ' + data.javascript);
}

function createManifestFile() {
    fs.writeFile(process.cwd() + '/appcache.manifest', data.manifestContent, function(err) {
        if (err) throw err;

        log('\nManifest file created');
    });
}

function log(message) {
    process.stdout.write(message + '\n');
}

function error(err) {
    process.stderr.write(err);
}

The first line, #!/usr/bin/env node, is critical to the script being used in the shell. We have to tell the shell what process should handle the script.

Next, we have to retrieve the arguments passed to the command. If we run squirrel bbc.co.uk/news, then process.argv would be an array containing the following:

  • the script execution type (node);
  • the script being executed (squirrel.js);
  • any other arguments (in this instance, only one, bbc.co.uk/news).

Ignore the first two arguments, and store the user-specific arguments so that we can reference them later:


var userArguments = process.argv.slice(2);

Our script only knows how to handle a single argument (which is the page URL to load). The following line isn’t really needed because we’ll ignore any more than one argument, but it’s useful for the code to have clear intent, so we’ll throw an error if more than one argument is passed.


if (userArguments.length > 1) {
    throw new Error('Only one argument may be specified (the URL for which you want to generate the AppCache.)');
}

Because we’re using PhantomJS, we’ll need to open up a shell and call the phantomjs command:


var shell = require('child_process').execFile;

We’ll also need to reference the bin directory, where the PhantomJS executable is stored:


var phantomjs = require('phantomjs').path;

Next, store a reference to the script that we want PhantomJS to execute, as well as the dummy manifest file.


var scriptToExecute = __dirname + '/appcache.js';
var manifest        = __dirname + '/../appcache.manifest';
var url             = userArguments[0];

Because the PhantomJS script that we’ll be executing needs a reference to the dummy manifest file, we’ll asynchronously read the contents of the file and then pass it on to a bootstrap function:


fs.readFile(manifest, bootstrap);

Our bootstrap function does exactly what you would expect: start our application (in this case, by opening the shell and calling PhantomJS). You’ll also notice that Node.js passes the contents of the manifest as a buffer, which we need to convert back into a string:


function bootstrap(err, contentAsBuffer) {
    if (err) throw err;

    manifestContent = contentAsBuffer.toString('utf8');

    shell(phantomjs, [scriptToExecute, url, manifestContent], function(err, stdout, stderr) {
        // code...
    });
}

At this point in the execution of the code, we are in the appcache.js file. Let’s move over there now.

appcache.js

The purpose of appcache.js is to get information from the user-requested page and pass it back to squirrel.js for processing.

Again, I’ll show the script in its entirety, and then we’ll break it down. (Don’t worry, we won’t go over each line — only the important parts.)


var unique     = require('lodash.uniq');
var system     = require('system');
var fs         = require('fs');
var page       = require('webpage').create();
var args       = system.args;
var manifest   = args[2];
var css        = [];
var images     = [];
var javascript = [];
var links;
var url;
var path;

bootstrap();
pageSetUp();
openPage();

function bootstrap() {
    if (urlProvided()) {
        url = cleanUrl(args[1]);
    } else {
        var error = new Error('Sorry, a valid URL could not be recognized');
            error.additional = 'Valid URL example: bbc.co.uk/news';

        throw error;

        phantom.exit();
    }

    if (bbcNews()) {
        // We want to serve the responsive code base.
        phantom.addCookie({
            'name'  : 'ckps_d',
            'value' : 'm',
            'domain': '.bbc.co.uk'
        });
    }
}

function pageSetUp() {
    page.onResourceRequested = function(request) {
        if (/\.(?:png|jpeg|jpg|gif)$/i.test(request.url)) {
            images.push(request.url);
        }

        if (/\.(?:js)$/i.test(request.url)) {
            javascript.push(request.url);
        }

        if (/\.(?:css)$/i.test(request.url)) {
            css.push(request.url);
        }
    };

    page.onError = function(msg, trace) {
        console.log('Error :', msg);

        trace.forEach(function(item) {
            console.log('Trace:  ', item.file, ':', item.line);
        });
    }

    page.viewportSize = { width: 1920, height: 800 };
}

function openPage() {
    page.open(url, function(status) {
        links      = unique(getLinks());
        images     = unique(images);
        css        = unique(css);
        javascript = unique(javascript);

        populateManifest();

        // Anything written to stdout is actually passed back to our Node script callback
        console.log(JSON.stringify({
            links           : links.length,
            images          : images.length,
            css             : css.length,
            javascript      : javascript.length,
            manifestContent : manifest
        }));

        phantom.exit();
    });
}

function urlProvided() {
    return args.length > 1 && /(?:www\.)?[a-z-z1-9]+\./i.test(args[1]);
}

function cleanUrl(providedUrl) {
    // If no http or https found at the start of the URL...
    if (/^(?!https?:\/\/)[\w\d]/i.test(providedUrl)) {
        return 'http://' + providedUrl + '/';
    }
}

function bbcNews(){
    if (/bbc.co.uk\/news/i.test(url)) {
        return true;
    }
}

function getLinks() {
    var results = page.evaluate(function() {
        return Array.prototype.slice.call(document.getElementsByTagName('a')).map(function(item) {
            return item.href;
        });
    });

    return results;
}

function writeVersion() {
    manifest = manifest.replace(/# Timestamp: \d+/i, '# Timestamp: ' + (new Date()).getTime());
}

function writeListContentFor(str, type) {
    manifest = manifest.replace(new RegExp('(# ' + str + ')\\n[\\s\\S]+?\\n\\n', 'igm'), function(match, cg) {
        return cg + '\n' + type.join('\n') + '\n\n';
    });
}

function populateManifest() {
    writeVersion();

    writeListContentFor('Images', images);
    writeListContentFor('Internal HTML documents', links);
    writeListContentFor('Style Sheets', css);
    writeListContentFor('JavaScript', javascript);
}

We begin by using PhantomJS’ API to create a new Web page:


var page = require('webpage').create();

Next, we’ll check that a URL was provided and, if so, clean it into the format required (for example, by giving it an http protocol). Otherwise, we’ll throw an error and stop PhantomJS:


if (urlProvided()) {
    url = cleanUrl(args[1]);
} else {
    var error = new Error('Sorry, a valid URL could not be recognized');
    error.additional = 'Valid URL example: bbc.co.uk/news';

    throw error;
    phantom.exit();
}

We also put in a check to see whether the URL passed was for bbc.co.uk/news and, if so, use PhantomJS to set a cookie that enables the responsive version of the website to load (the purpose being merely to demonstrate some of PhantomJS’ useful APIs, such as addCookie):


if (bbcNews()) {
    phantom.addCookie({
        'name'  : 'ckps_d',
        'value' : 'm',
        'domain': '.bbc.co.uk'
    });
}

For PhantomJS to be able to analyze the network data (so that we can track the style sheets, JavaScript and images being requested by the page), we need to use special PhantomJS handlers to interpret the requests:


page.onResourceRequested = function(request) {
    if (/\.(?:png|jpeg|jpg|gif)$/i.test(request.url)) {
        images.push(request.url);
    }

    if (/\.(?:js)$/i.test(request.url)) {
        javascript.push(request.url);
    }

    if (/\.(?:css)$/i.test(request.url)) {
        css.push(request.url);
    }
};

We’ll also use another PhantomJS API feature that enables us to determine the size of the browser window:


page.viewportSize = { width: 1920, height: 800 };

We then tell PhantomJS to open the specified Web page. Once the page is open (i.e. the load event has fired), a callback is executed:


page.open(url, function(status) {
    // code...
});

In the callback, we store the resources that were found, and we call a function that replaces the contents of our string (the dummy manifest) with a list of each set of resources:


page.open(url, function(status) {
    links      = unique(getLinks());
    images     = unique(images);
    css        = unique(css);
    javascript = unique(javascript);

    populateManifest();

    // Remaining code...
});

Finally, we create a data object to hold statistics about the resources being requested, convert it to a JSON string, and log it using the console API.

Once this is done, we tell PhantomJS to exit (otherwise the process would stall):


page.open(url, function(status) {
    // Previous code...

    console.log(JSON.stringify({
        links           : links.length,
        images          : images.length,
        css             : css.length,
        javascript      : javascript.length,
        manifestContent : manifest
    }));

    phantom.exit();
});

Reviewing the code above, you might wonder how we get the data back to our squirrel.js script? Take another look at the console.log. The code has an odd side effect, which is that any code logged by PhantomJS is passed back to our shell callback (originally executed in squirrel.js).

Let’s revisit our squirrel.js script now.

Back to squirrel.js


shell(phantomjs, [scriptToExecute, url, manifestContent], function(err, stdout, stderr) {
    if (err) throw err;

    try {
        data = JSON.parse(stdout);
    } catch(err) {
        log('Whoops! It seems there was an error? You\'ll find the stack trace below.');
        error(err);
    }

    displayStatistics();
    createManifestFile();
});

The callback function is run when the PhantomJS script finishes executing. It is passed any errors that may have occurred and, if there are, then we throw the error:

if (err) throw err;

The other arguments are the standard output and error arguments provided by the shell. In this case, the standard output would be our JSON string, which we console.log’ed from appcache.js. We parse the JSON string and convert it back into an object so that we can present the data to the user who has run the squirrel command.

As a side note, we wrap this conversion in a try/catch clause to protect against Web pages that cause a JavaScript error to occur (the error is picked up by stdout, not stderr, thus causing the JSON parsing to break):


try {
    data = JSON.parse(stdout);
} catch(err) {
    error(err);
}

Once we have our data, we call displayStatistics, which uses stdout to write a message to the user’s terminal.

Lastly, we call createManifestFile, which creates an appcache.manifest file in the user’s current directory:


fs.writeFile(process.cwd() + '/appcache.manifest', data.manifestContent, function(err) {
    if (err) throw err;

    log('\nManifest file created');
});

Now that we understand how the script works in its entirety, let’s look at how to allow others to download and install our work.

Packaging

For other users to be able to install our module, we’ll need to publish it to a public repository. The place to do this is the npm registry.

To publish to npm, you’ll need a package.json file.

The purpose of package.json is to specify the dependencies of the project you’re working on. In this instance, it specifies the dependencies required by Squirrel to do its job.

Below is Squirrel’s package.json file:


{
  "name": "squirrel-js",
  "version": "0.1.3",
  "description": "Node.js-based CLI tool, using PhantomJS to automatically generate an Application Cache manifest file for a specified URL",
  "main": "lib/squirrel",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "engines": {
    "node": ">=0.10"
  },
  "repository": {
    "type": "git",
    "url": "git://github.com/Integralist/Squirrel.git"
  },
  "preferGlobal": "true",
  "bin": {
    "squirrel": "lib/squirrel.js"
  },
  "dependencies": {
    "phantomjs": "~1.9.2-6",
    "lodash.uniq": "~2.4.1"
  },
  "keywords": [
    "appcache",
    "phantomjs",
    "cli"
  ],
  "author": "Mark McDonnell  (http://www.integralist.co.uk/)",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/Integralist/Squirrel/issues"
  },
  "homepage": "https://github.com/Integralist/Squirrel"
}

You can read up on all of the properties of package.json in the npm registry.

The properties to note are these:

  • "preferGlobal": "true"
  • "bin": { "squirrel": "lib/squirrel.js" }

The first property indicates when a user has installed a module that you would prefer to be installed globally. In this case, we want it to be installed globally because then the user will be able to run the command anywhere in their system.

The second property indicates where the command will find the code required to execute the command.

To test that your command works, you’ll need to run the npm link command, which in this case creates a symlink from the squirrel command to the squirrel.js file.

Publishing

To publish your code, first register for an npm account.

You’ll need to verify the account via the command line. To do this, run npm adduser, which will ask you to specify a user name and password.

Once you’ve verified the account, you can publish your module to the npm registry using npm publish.

It could take a few minutes for the module to become publicly accessible.

Be aware that if you update the code and try to run npm publish without updating the package.json file’s version property, then npm will return an error asking you to update the version number.

Conclusion

This is just one example of the sort of command line tools you can develop with Node.js’ many features.

The next time you find yourself performing a repetitive task, consider automating the process with a CLI tool.

(al)


© Mark McDonnell for Smashing Magazine, 2014.

Tags: Coding

February 05 2014

10:49

Introducing Live Extensions For Better-DOM: What They Are And How They Work


  

After recently writing an article on “Writing A Better JavaScript Library For The DOM”, I realized that the topic is indeed a very complex one and that it’s important to understand what exactly live extensions are and how they work. In today’s article, I will answer most questions that were asked regarding “live extensions” and help you get going with this new concept.

The Responsibilities Of Live Extensions

Event handling is one of the key principles of working with the DOM. Events are the primary means of receiving feedback from user interaction.

Simple Event Binding

In this first example, documentation and tutorials that cover DOM events is what I call “simple event binding”. You attach a listener for the desired event on the DOM element in which you expect it to happen on.


link.addEventListener("click", function(e) {
  // do something when the link is clicked
}, false);

The first argument indicates the type of an event, the second argument is a listener, and the third argument defines an event phase (so-called “bubbling” or “capturing”). The reason why the last argument exists is because most DOM events traverse the DOM tree from document node to target node (capture phase) and back to the document node (bubble phase). This process is called “event flow” and brings several powerful features.

Live and Delegated Events

Instead of attaching a handler for each element in a group, we can attach one listener onto an ancestor shared by all of the elements in that specific group. Then, we can determine where an event took place using the target property of the event object, passed into the listener. This is known as “event delegation”:


list.addEventListener("click", function(e) {
  if (e.target.tagName === "LI") {
    // do something when a child <li> element is clicked
  }
}, false);

By having all event handlers on a particular parent, we can update the innerHTML property of this element without losing the ability to listen to events for new elements. The feature was called “Live Events” in jQuery, and it quickly became popular because of its ability to filter events by a CSS selector. Later, delegated events replaced them due to their flexibility by allowing to bind a listener to any element within the document tree.

But even event delegation does not overcome the following problems:

  • When DOM mutation is required after a new element (that matches a specific selector) comes into the document tree,
  • When an element should be initialized on a excessive event such as scroll or mousemove,
  • Or on non-bubbling events, e.g. load, error, etc.

This is what live Extensions aim to solve.

Live Extensions Use Cases

Take a look at the following diagram that explains the responsibilities:

live-extension-respo

1. DOM Mutations For Existing And Future Elements

Imagine you want to develop a reusable datepicker widget. In HTML5, there is a standards-based <input type="date"> element that could be used to create a polyfill. But the problem is that this element looks and behaves very different from browser to browser:

dateinputs
Date input element in different browsers.

The only way to make the element behave consistently is to set the type attribute value to "text". This will cancel a legacy implementation and enable JavaScript to make your own. Try defining a live extension with the example below:


DOM.extend("input[type=date]", {
  constructor: function() {
    // cancel browser-specific implementation
    this.set("type", "text");
    // make your own styleable datepicker,
    // attach additional event handlers etc.
  }
});

2. Media Query Callbacks

I highly recommend reading Paul Hayes’ article on how to “Use CSS transitions to link Media Queries and JavaScript”.

“A common problem in responsive design is the linking of CSS3’s media queries and JavaScript. For instance on a larger screen we can restyle, but it might be useful to use JavaScript and pull in different content at the same time, e.g. higher quality images.”

Paul was probably the first who started to use “hidden force” of CSS3 animation events to solve mutation-related problems. Live extensions are powered by the same trick, therefore you can use them to make DOM modifications depending on the current viewport:


DOM.extend(".rwd-menu", {
  constructor: function() {
    var viewportWidth = DOM.find("html").get("clientWidth");

    if (viewportWidth < 768) {
      // hide <ul> and construct Emmet abbreviation for a
      // <select> element that should be used on small screens
      this.hide().after("select>" +
        this.children("li").reduce(function(memo, item) {
          var text = item.get("textContent"),
            href = item.find("a").get("href");

          memo.push("option[data-url=" + href + "]>live-extension-respo{" + text + "}");
          return memo;
        }, []).join("^"));

      // redirect to stored url when an <option> is selected
      this.next().on("change", this.onMenuChanged.bind(this));
    }
  },
  onMenuChanged: function(select) {
    var selectedIndex = select.get("selectedIndex");
    // redirect to the appropriate data-url attribute value
    location.href = target.child(selectedIndex).data("url");
  }
});

3. Element Media Queries

Back in 2011, Andy Hume implemented a script for applying styles depending on the dimensions of a particular element (not viewport, like for media queries). Later, this technique was named “element media queries”:

“Media queries work really well when you want to adjust the core layouts of the site, but they’re less suited to changing styles at a smaller more granular level.”

With the help of live extensions, it’s easy to implement element media queries support using the offset method:


DOM.extend(".signup-form", {
  constructor: function() {
    var currentWidth = this.offset().width;
    // add extra class depending on current width
    if (currentWidth < 150) {
      this.addClass("small-signup-form");
    } else if (currentWidth > 300) {
      this.addClass("wide-signup-form");
    }
  }
});

4. Efficiently Attach A Global Listener To Frequent Events


DOM.extend(".detectable", {
  constructor: function() {
    // mousemove bubbles but it’s usually a very bad
    // idea to listen to such event on a document level
    // but live extensions help to solve the issue
    this.on("mousemove", this.onMouseMove, ["pageX", "pageY"]);
  },
  onMouseMove: function(x, y) {
    // just output current coordinates into console
    console.log("mouse position: x=" + x + ", y=" + y);
  }
});

5. Listing Non-Bubbling Events On A Document Level


DOM.extend("img.safe-img", {
  constructor: function() {
    // error event doesn’t bubble so it’s not
    // possible to do the same using live events
    this.on("error", this.onError);
  },
  onError: function() {
    // show a predefined png if an image download fails
    this.src = "/img/download-failed.png"
  }
});

Brief Look Into History

The problems which live extensions aim to solve is not entirely new, of course. There are different approaches that address the above-mentioned issues. Let’s have a quick look at some of them.

HTML Components

Internet Explorer started supporting DHTML behaviors with IE 5.5:

“DHTML behaviors are components that encapsulate specific functionality or behavior on a page. When applied to a standard HTML element on a page, a behavior enhances that element’s default behavior.”

To attach behavior to future elements, Internet Explorer used an *.htc file with a special syntax. Here’s an example illustrating how we used to make :hover work on elements instead of <a>:


<PUBLIC:COMPONENT URN="urn:msdn-microsoft-com:workshop" >
  <PUBLIC:ATTACH EVENT="onmouseover" ONEVENT="Hilite()" />
  <PUBLIC:ATTACH EVENT="onmouseout"  ONEVENT="Restore()"  />
  <SCRIPT LANGUAGE="JScript">
  var normalColor, normalSpacing;
 
  function Hilite() {
    normalColor  = currentStyle.color;  
    normalSpacing= currentStyle.letterSpacing;
 
    runtimeStyle.color  = "red";
    runtimeStyle.letterSpacing = 2;
  }

  function Restore() {
    runtimeStyle.color  = normalColor;
    runtimeStyle.letterSpacing = normalSpacing;
  }
</SCRIPT>
</PUBLIC:COMPONENT>

If you provided the above-mentioned code into the hilite.htc file, you could access it within CSS through the behavior property:


li {
  behavior: url(hilite.htc);
}

I was really surprised to discover that HTML components supported creating custom tags (starting from version 5.5), have single domain limitations and tons of other stuff that you probably have never used before. Despite Microsoft submitting a proposal to W3C, other browser vendors decided not to support this feature. As a result, HTML components were removed from Internet Explorer 10.

Decorators

In my previous article, I mentioned the Decorators which are a part of Web components. Here’s how you can implement the open/closed state indicator of the <details> element using decorators:


<decorator id="details-closed">
  <script>
    function clicked(event) {
      event.target.setAttribute('open', 'open');
    }
    [{selector: '#summary', type: 'click', handler: clicked}];
  </script>
  <template>
    <a id="summary">
      &blacktriangleright; <content select="summary"></content>
    </a>
  </template>
</decorator>

<decorator id="details-open">
  <script>
  function clicked(event) {
    event.target.removeAttribute('open');
  }
  [{selector: '#summary', type: 'click', handler: clicked}];
  </script>
  <template>
    <a id="summary">
      &blacktriangledown; <content select="summary"></content>
    </a>
    <content></content>
  </template>
</decorator>

Decorators are also applied using the special decorator property in CSS:


details {
  decorator: url(#details-closed);
}

details[open] {
  decorator: url(#details-open);
}

You’ll quickly notice that this is very close to what Microsoft proposed in HTML Components. The difference is that instead of separate HTC files, decorators are HTML elements that can be defined within the same document. The example above is only provided to show that the Web platform is working on these topics, since decorators aren’t properly specified just yet.

Live Extensions API

While designing APIs for live extensions, I decided to follow the following rules:

  1. Live extensions should be declared in JavaScript.
    I strongly believe that everything that somehow changes the behavior of an element should be presented in a JavaScript file. (Note that better-dom inserts a new CSS rule behind the scenes, but this includes only implementation details).
  2. APIs should be simple to use.
    No tricky file formats or new HTML elements: only a small amount of knowledge related to the constructor and event handlers is required to start developing a live extension (thus, the barrier to entry should be low).

As a result, there are only two methods to deal with: DOM.extend and DOM.mock.

DOM.extend

DOM.extend declares a live extension. It accepts a CSS selector as the first argument which defines what elements you want to capture. General advice: try to make the selector simple.

Ideally, you should only use a tag name, class or attribute with or without a value or their combinations with each other. These selectors can be tested quicker without calling an expensive matchesSelector method.

The second argument is a live extension definition. All properties of the object will be mixed with an element wrapper interface except constructor and event handlers.

Let’s look at a simple example. Let’s assume we have such an element on a Web page:


<div class="signin-form modal-dlg">...</div>

The task is to show it as a modal dialog. This is how the live extension could look like:


DOM.extend(".modal-dlg", {
  constructor: function() {
    var backdrop = DOM.create("div.modal-dlg-backdrop");
    // using bind to store reference to backdrop internally
    this.showModal = this.showModal.bind(this, backdrop);
    // we will define event handlers later
  },
  showModal: function(backdrop) {
    this.show();
    backdrop.show();
  }
});

Now you can access the public method showModal in any (present or future) element that has the modal-dlg class (in our case this is the signin-form div):


var signinForm = DOM.find(".signin-form");

DOM.find(".signin-btn").on("click", function() {
  // the signin button doesn’t have the modal-dlg class
  // so it’s interface doesn’t contain the showModal method
  console.log(this.showModal); // => undefined
  signinForm.showModal(); // => shows the signin dialog
});

Note: The better-dom-legacy.js file which is included conditionally for Internet Explorer versions 8 and 9, contains the es5-shim library so you can safely use standards-based EcmaScript 5 functions (such as Function.prototype.bind) in your code. I’ve been using the bind method heavily in my code to build testable methods easily.

The Constructor Property

The constructor function is called when an element becomes visible. This is because of the animationstart event that is used to implement DOM.extend. Browsers are clever so they don’t fire animation events for hidden elements. This lazy initialization saves resources sometimes, but be careful with accessing initially hidden elements.

In older Internet Explorers versions such as 8 and 9, contentready event from better-dom-legacy.htc is used to implement live extensions. Therefore, the constructor function executes immediately in these browsers — even for hidden elements.

Note: Keep in mind not to rely on time whenever an extension has been initialized. The actual initialization of a live extension varies across browsers!

Constructor is usually the place where you attach event handlers and perform DOM mutations where necessary. Once the function has been completed, all methods that begin with “on” (in better-dom 1.7 also “do”) followed by an uppercase letter, event handlers will be removed from the element wrapper’s interface.

Let’s update our .signin-form live extension with the help of a close button and the ESC key:


DOM.extend(".modal-dlg", {
  constructor: function() {
    var backdrop = DOM.create("div.modal-dlg-backdrop"),
      closeBtn = this.find(".close-btn");

    this.showModal = this.showModal.bind(this, backdrop);
    // handle click on the close button and ESC key
    closeBtn.on("click", this.onClose.bind(this, backdrop));
    DOM.on("keydown", this.onKeyDown.bind(this, closeBtn), ["which"])
  },
  showModal: function(backdrop) {
    this.show();
    backdrop.show();
  },
  onClose: function(backdrop) {
    this.hide();
    frame.hide();
  },
  onKeyDown: function(closeBtn, which) {
    if (which === 27) {
      // close dialog by triggering click event 
      closeBtn.fire("click");
    }
  }
});

Despite the fact that the live extension contains both onClose and onKeyDown methods, they won’t be mixed into the element wrapper interface:


var signinForm = DOM.find(".signin-form");

console.log(signinForm.onClose); // => undefined
console.log(signinForm.onKeyDown); // => undefined

This kind of behavior exists simply because you can have multiple live extensions for a single element that may overload public methods of each other and produce unexpected results. For event handlers, this is not possible; they exist only inside of the constructor function.

Extending * Elements

Sometimes it is useful to extend all of the element wrappers with a particular method (or methods). But then again, you can also use the universal selector to solve the problem:


DOM.extend("*", {
  gesture: function(type, handler) {
    // implement gestures support
  }
});
…
DOM.find("body").gesture("swipe", function() {
  // handle a swipe gesture on body
});

The * selector has a special behavior: all extension declaration properties will be injected directly into the element wrapper prototype except for the constructor which is totally ignored. Therefore, there is no performance penalty that is usually associated with the universal selector.

Note: Never pass more specific selectors such as .some-class * into DOM.extend because they are slow and do not have the same behavior as mentioned above.

Multiple Live Extensions on the Same Element

More often that not, it makes sense to split a large live extension into several pieces to reduce complexity. For instance, you may have such an element on your page:


<div class="infinite-scroll chat"></div>

There are two different extensions attached to it. The .infinite-scroll extension implements a well-known infinite scroll pattern, e.g. it’s responsible for loading new content. At the same time, the .chat extension shows tooltips whenever a user hovers over a userpic, adds smileys into messages, and so on. However, be accurate with multiple extensions: even though all event handlers may have been removed from the interface, you still may have public methods that intersect with each other.

Inheritance

Live extensions respect declaration order; you can use this to your advantage and develop your own component hierarchy. Late binding helps to declare overridable event handlers and method overloading allows to redefine a method implementation in a child extension:


DOM.extend(".my-widget", {
  constructor: function() {
    this.on("click", "_handleClick");
  },
  showMessage: function() { }
});

DOM.extend(".my-button", {
  _handleClick: function() {
    console.log("I am a button!");
  },
  showMessage: function() {
    alert("I am a button message!");
  }
});

If you take a closer look at the code above, you’ll notice that the .my-button extension does not attach a click listener. The registration is done with the help of late binding instead of a simple event handler in .my-widget. Late binding is a perfect choice here: even if a child does not implement _handleClick there won’t be any errors since the handler will be silently ignored.

While spreading functionality across multiple modules is possible, this is not recommended in everyday use. Double check if you really need to go in this direction, because it’s the most complex one.

Writing Tests with DOM.mock

One requirement for a high-quality widget is test coverage. New elements are captured by a live extension asynchronously, so it’s not that easy to simply make them in memory. To solve this problem, better-dom has the DOM.mock function:


var myButton = DOM.mock("button.my-button");

DOM.mock creates elements, just like DOM.create. Additionally, it synchronously applies the registered live extensions to the newly created elements. For even more convenience, all wrapper objects created by DOM.mock preserve event handlers (e.g. onClick), so you can test them.

From time to time, you may need to create a “fake” instance of an element. Use DOM.mock without arguments to make such an object:


console.log(DOM.mock().length); // => 0

A test for the modal dialog live extension introduced earlier could look like this (I use Jasmine):


describe(".modal-dlg", function() {
  var dlg, backdrop;

  beforeEach(function() {
    dlg = DOM.mock("div.modal-dlg");
    backdrop = DOM.mock();
  });

  it("should hide itself and backdrop on close", function() {
    var dlgSpy = spyOn(dlg, "hide"),
      backdropSpy = spyOn(backdrop, "hide");

    dlg.onClose(backdrop);
    expect(dlgSpy).toHaveBeenCalled();
    expect(backdropSpy).toHaveBeenCalled();
  });

  it("should show itself and backdrop on show", function() {
    var dlgSpy = spyOn(dlg, "show"),
      backdropSpy = spyOn(backdrop, "show");

    dlg.showModal(backdrop);
    expect(dlgSpy).toHaveBeenCalled();
    expect(backdropSpy).toHaveBeenCalled();
  });
});

Feature Detection (in better-dom 1.7)

There are some cases when filtering with a CSS selector is not flexible enough. For instance, let’s say you want to declare a live extension but only for browsers that support (or do not support) a particular feature. You may need to run tests in a headless browser like PhantomJS that support the feature natively. Starting with better-dom 1.7, DOM.extend supports the optional argument condition.

Assume we need to create a polyfill for the placeholder attribute. It doesn’t make sense to implement it for browsers that have built-in support. Below is an example of how the feature detection could look like:


var supportsPlaceholder = typeof DOM.create("input")
      .get("placeholder") === "string";

By using just a simple “If” statement as shown in the example below, we won’t have an ability to test the widget because PhantomJS supports the placeholder attribute and the live extension will never be declared.


if (!supportsPlaceholder) {
  DOM.extend("[placeholder]", {
    // implement placeholder support
  };
}

In order to solve this problem, you can use an extra condition argument in DOM.extend that might be Boolean or a function:


DOM.extend("[placeholder]", !supportsPlaceholder, {
  constructor: function() { … },
  onFocus: function() { … },
  onBlur: function() { … }
});

DOM.mock ignores the condition argument, so you can access all methods of the [placeholder] extension even if current browser passes the check:


var input = DOM.mock("input[placeholder=test]");

typeof input.onFocus; // => "function"

Conclusion

Live extensions — and better-dom as an implementation of the concept — are a good base to build upon whenever your target is uncertain, e.g. when creating a polyfill that may or may not be used on a particular site. Or regular widgets that may or may not be needed, depending upon some AJAX call.

Live extensions aim to separate declaration and the use of widgets. They bring loose coupling (or decoupling, rather) of any DOM-based component, and allow your code to become smaller, cleaner and easier to maintain. You can even combine such independent pieces with any existing framework within the market (or with the vanilla DOM, of course).

You may now be thinking, “But wait, there are projects like Polymer or x-tags, right?” Well, live extensions cover a different area; they are not about custom tags but rather about extending existing ones instead. I prefer a standards-based way (if possible) of creating UI widgets, so making polyfills is my choice.

Better-dom also has another advantage: a carefully crafted live extension does not force you to rewrite a website’s markup using different tags. All you need is to simply include a script file on your page. Standards-based elements can potentially work without JavaScript, so they degrade well when it’s disabled. And the library’s browser support allows you to start using live extensions straight away.

Feel free to share your thoughts in the comments section below or on the better-dom project home page.

(il)


© Maksim Chemerisuk for Smashing Magazine, 2014.

Tags: Coding

January 31 2014

14:00

Learning SASS: A Beginner’s Guide to SASS

Writing a lot of CSS can be overwhelming; that is why learning SASS and LESS can make  any web developer and designer’s life much easier. For a beginner, you might find it fine but as time goes by and your CSS skills are improving. You begin to wonder if there is a way so that you don’t need to repeat a lot of CSS codes in your style sheet. The good news is, there is one! Thanks to the CSS pre – processor, it’s now possible to write concise CSS codes without repeating each of them again and again. It is even formatted nicely. You can perform computations and do dynamic styling using these pre-processing methods. There are two pre-processing methods that I will tackle: SASS and LESS. For this tutorial I will talk about SASS first and then on a separate tutorial, I’ll talk about LESS.

CSS Drawbacks

Using CSS only might work for you but when making big websites with multiple pages, there might be some features you wish CSS has. Take a look at the following disadvantages of using CSS alone.

  • No way to re-use common style rules.
  • No way to specify variables that can be defined and re-used all through the style sheet.
  • You can’t execute computations where you can add numerical values to elements.

Advantages of Using Pre-Processing Methods

While using CSS alone might give you nuisance, using pre-processing methods can save you a lot of time and effort. Check out the list of advantages of using pre-processing methods below.

  • Allows you to use variables that can be re-used all throughout the style sheet.
  • Higher level style syntax that provides advanced CSS features.
  • Compiled CSS files are uploaded to the production web server.

What Is SASS?

SASS stands for Syntactically Awesome Style Sheets and was  designed and created by Hampton Catlin. SASS manipulates CSS using variables, mixins, inheritance and nesting rules. Given the extensions .sass and .scss respectively, it’s translated to well-formatted CSS using a command line tool or web-framework plugin.

SASS makes it easier to write less CSS codes and manipulate them dynamically. It’s a great way to write more functional CSS codes and can speed up the workflow of every web developer and designer.

.sass VS. .scss Format

Before we begin on how to use SASS, let’s compare .sass and .scss extensions of SASS. First I will provide a simple CSS code and then I will show you how to simplify them on both extensions of SASS.

CSS Code

For our CSS, I used a header tag and put a zero value for margin and padding then white color for its text color.


header {
     margin: 0;
     padding: 0;
     color: #fff;
}

.scss Extension Format (New Way of Writing SASS)

To format this into .scss extension format, we will use a variable $color and give it a hexadecimal color value of #fff for white color. And then under the CSS style, instead of putting a hexadecimal color value of #fff, use the variable $color that was set in the beginning of the code.

$color:  #fff;
header {
    margin: 0;
    padding:0;
    color: $color;
}

.sass Extension Format (Old Way of Writing SASS)

For our .sass extension, we will have the same variable and value just like the .scss extension format, but, this time, without semi-colons and brackets. Notice that indentions are more reliant. This is the old format in writing SASS.

$color: #fff
header
   margin: 0
   padding: 0
   color: $color

Resources you need to complete this tutorial:

Installing Ruby

Before you can be able to test how SASS works, you need to download Ruby to your computer. Launch the Ruby installer and you will be prompted with the Setup page. Select your preferred language and click OK.

1

Then click on I accept the License radio button. 2

Next, install it to your preferred location and make sure that the radio button for Add Ruby executables to your PATH is checked. Click the Install button. This will install the software and when it’s done installing, just click the Finish button.

34

Checking if Ruby is Running Properly

Now that you have installed Ruby, let’s go ahead and check if this is working properly. Open Command Prompt and type the word ruby -v. And you can see, it would return the current version of the Ruby installed along with the date. If it’s returning an error, it could be that Ruby was not installed correctly or you did not put the Ruby executable into your path.

5

Installing SASS

To install SASS, open Command Prompt and type the word gem install sass and you can see that the Installation Prompt that it was successfully installed.

6

Preparing the Necessary Files

Before digging in with SASS, we need to prepare the necessary file you need before you code. Create a new folder to your preferred location (for this tutorial, I placed mine on my desktop) and name it SASS or any name you preferred. Inside the SASS folder, create an HTML file and name it index.html. For the HTML content put the following code.


Introduction to SASS
        	<link href="style.css" rel="stylesheet" type="text/css" /></pre>
<div id="container"><header>
<h1>Sass Sample Document</h1>
<h2>A 1stwebdesigner tutorial</h2>
</header>
<div>
Simple paragraph of text

Another paragraph of text</div>
<div>
<ul id="list1">
	<li>List Item 1</li>
	<li>List Item 2</li>
	<li>List Item 3</li>
</ul>
</div>
<footer>
<h3>This is a cool footer content</h3>
</footer></div>
<pre>

Now for our SASS file, create a new blank file in your text editor and name it style.scss. If you followed the steps, by this time you will have the following file structure.

7

Converting SASS code into CSS code

To convert the SASS code into CSS code, we’re going to use the -watch command in command prompt. This will compile the SASS codes to CSS. Additionally, this will also watch the directories for changes or updates. Let’s try to convert  the SASS file to CSS file. Before we start, we need to put a sample code on our style.scss to see if this working.  Copy and paste the following sample SASS code on the stye.scss file you created under SASS folder.


$myMargin: 0px auto;
$myColor: red;
$myWidth: 600px;

h1 {
	color: $myColor;
	$myMargin: $margin;
}

Next, open your command prompt and then go to the location where you put your files in. In my case, I put it in my desktop so I will type in cd ”Desktop” and it will locate the desktop directory.

8

Now that we are in the desktop file directory, type in the sass –watch Sass:Sass 9

Using –watch command, we will convert all of the .scss files on the folder SASS. It will also watch for the changes or updates on the file. Notice that there are two SASS, divided by a colon. The first one represents the current location of the .scss file while the second one represents the location of the output of the file. Make sure you link the converted CSS file to your HTML file to see this working.

 10

Using Variables

SASS variables are declared using the $ character and are defined like CSS values. Using SASS, you can declare variables for styles like font size, margin, padding and so on. Using variables and giving it a style value makes it easy to reuse a style repeatedly.

There are six different types of variables you can use with SASS.

  1. Strings (e.g. $myString: “your text here”;)
  2. Numbers (e.g. $myNum: 10px;)
  3. Colors (e.g. $myColor: white;)
  4. Booleans (e.g. $myBool: true;)
  5. Lists (e.g. $myItemList: 1px solid red;)
  6. Nulls (e.g. $myVar: null;)

Let’s put some of these types into practice. Open up your style.scss file and type the following code.


$myColor: #009a82;
$myString: " some text here ";
$myFontSize: 13px;
$myMargin: 0px auto;
$myWidth: 460px;

h1 {
	color: $myColor;
	margin: 0;
	padding: 0;
}

#container {
	width: $myWidth;
	margin: $myMargin;
}

Now when you run this code into your browser, you will have this output.

12

Nesting

SASS also allows you to define nested styles. This will let you write easy-to-read codes. For instance, you have the following CSS code.

#container p {
   font-family: Arial;
   font-size: 13px;
}

#container h1 {
   font-family: Tahoma;
   font-size: 15px;
}

#container h2 {
   font-family: Helvetica;
   font-size: 14px;
}

For the SASS version, you will have a format like this.

$myFontsize1: 13px;
$myFontsize2: 18px;
$myFontsize3: 25px;
$myWidth: 500px;
$myMargin: 0px auto;

#container {
    width: $myWidth;
	margin: $myMargin;

	p {
		font-family: Arial;
		font-size: $myFontsize1;
	}

    h1 {
		font-family: Tahoma;
		font-size: $myFontsize3;
	}

	h2 {

		font-family: Helvetica;
		font-size: $myFontsize2;
}
}

Notice that we put all of the elements styles under the id container, instead of naming them one by one under the same element. If you run this code into your browser, you will have something like this.

12

Mixins

Mixins let you define common properties once then re-use them over and over again. Mixins are defined using @mixin directive and contains a block of codes and then reuse them using @include directive. Let’s put this into practice. Copy the code below to your style.scss file.

@mixin border {
   border: 1px solid red;
}

#container {
   width: 960px;
   margin: 0 auto;
   @include border;
}

As you can see, we use the @mixins directive to create a style for border and then we include the style to id container using @include directive. If you run this in your browser, you will have the output below.

13

Operators

Performing mathematical operations is one of the best features of pre-processors like SASS, something which you can’t do with just a normal CSS code. Given this feature allows you do more complex and dynamic codes. Let’s take a look at this how it works. Go ahead and open your style.scss file and copy and paste the code below.


$myColor: #aa30ff;
$myPadding: 20px;
$thickness: 1px;
$border: solid red;

#samplepara{
	color: $myColor;
	padding: $myPadding+30;
}

#list1 {
	color: $myColor;
	border: $thickness+5 $border;
}

As you can see, we perform mathematical computations by adding 30px on the padding. We also added border thickness of 5px on the border. If you run this in your browser, you can see the output will look like this.

14

Functions

SASS offers a variety of functions. A good example of this is the color functions where you can manipulate color styles. Check out the series of color functions below.

  1. darken(color, amount)
  2. lighten(color, amount)
  3. saturate(color, amount)
  4. desaturate(color, amount)
  5. alpha(color)

These are just series of examples of functions. To learn more about functions check out the SASS documentation. For now let’s go ahead and try this example to see how SASS functions work. Open your style.scss folder and then copy and paste the code below.


$myColor: #202020;
$myBackground: #e6e6e6;

body {
     background: darken($myBackground, 20%);
}

h1, h2 {
     color: lighten($myColor, 40%);
}

Notice that the color darkens by 20% using the darken function while the H1 lightens, and also the H2 text by 40% using the lighten functions. So if run this in your browser you can see similar output below.

15

SASS Output Formatting

One of the best features of SASS is it offers several options to control how the .scss code is formatted when compiled into CSS. Using the -style option command, we can perform formatting to our compile CSS code. The following are the SASS formatting styles.

Nested Format

Nested style is the default format of SASS. This format gives indention to all of the styles in your compiled CSS file. To see this in action, copy and paste the code below into your style.scss and then open your Command Prompt (make sure you are inside the directory of SASS) and type in SASS –update style.scss . This command will update the formatting for the compiled CSS file using the update command.

16

 Now go ahead and open your compiled style.css file. As I’ve said above all of the styles will have proper indention.

  17

Expanded Format

This is the most user-friendly and readable format as the braces are properly expanded and each property will have its own line. Let’s see this in action. Using the same code above, open your command prompt and type sass –update style.scss –style expanded. Notice that we added –style command this is used to format compiled CSS file to a particular formatting.

18

So if you are going to look at the compiled CSS file on the SASS file, you can see the code formatting is similar to the image below. Notice that each property has its own line. The braces are also fully expanded.

19

Compact Format

This is the compact format output CSS code in a condensed but still readable format. It adds spaces between braces but all in one line. To see how this works using the same code above, open your command prompt and type sass –update style.scss –style compact .

20

If you are going to check the compiled CSS file, you can see something similar to the image below. It is condensed. Each property and style is in one line.

 21

Compressed Format

The compressed format has minimized output. This is suitable for the production environment. This format has more condensed formatting. Using the same code, open your command prompt and type in sass –update style.scss –style compressed .

22

So if you are going to look at the compiled CSS file on the SASS file, you can see code formatting similar to the image below. As you can see, all codes are in one line with no spaces at all. 23

Conclusion

SASS offers a variety of ways to simplify your CSS issues. It allows you to reduce repetition of codes by using variables. SASS is powerful. This can help to improve the workflow of both web designer and web developers. Learn SASS now and discover how it can it help you build professional websites in a fast-paced development. Hope you enjoyed this tutorial and see you again next time.

January 27 2014

14:00

Simple Yet Important Cross-Browser Styling Tips Everyone Should Know

Many have been searching for cross-browser styling tips that can help them solve their browser compatibility problems. After all, creating a website can be easy but developing one that looks the same on all browsers can be tough. For many years, browser compatibility is one of the hardest parts of web development.

As a web designer/developer, it’s always our goal to give our website visitors better experience on viewing our site. Having a consistent look on every browser leaves a visitor good impression of the site.

cross-browser-styling

If you’ve been developing websites for a long time you might agree with me that IE8 and the Jurassic lower versions of IE are the one of the web designer’s and developer’s nightmare. Given this fact that, there are still people using these versions of IE to view websites.

This is just one of the issues that we might encounter in developing a cross-browser website. The good thing is that we can redesign a site or initiate some good practices to make them compatible and look the same on every browser. In this article, I will show you some cross-browser styling tips and tricks to make your website development easier.

Use CSS Reset

Let’s face the fact that web browsers interpret different default styling for HTML elements. Some browsers have different interpretations towards values for styles like margin and padding.

To fix this issue, adding a CSS reset at the start of your style sheet is a good practice. This will reset all CSS elements. This would also mean that it will start from a zero base, thus, giving you complete control to your style sheet without worrying about the browser’s CSS issues such as margin, padding and alignment.

Eric Meyer has a good set of CSS rules to do this. You might also want to consider Nicolas Gallagher’s Normalize.css, a modern HTML5 ready alternative to CSS resets. After you’ve linked the CSS rules to your markup, you can now make sure that you have a starting zero base CSS on every browser.

Validate Your HTML and CSS

Before running or publishing your website, it’s a good practice if you validate both your HTML and CSS using validators as it will correct some minor errors that might give you some problems in the future.

You can use W3C HTML Validator and CSS Validator. These are trusted validators by W3C but feel free to use your own tools if you have another resource of HTML and CSS Validator that works for you.

IE Conditional Comments

The Jurassic versions of IE had been giving web designers and developers a headache when it comes to compatibility issues. As an answer to this issue, Microsoft developed conditional comments in their browser that allows linking a style sheet that will be interpreted by IE alone. Check out the code below.

The code below will target all versions of IE.

!
<--[if IE]>
  	<link href="ie.css" rel="stylesheet" type="text/css" /!>
<[endif]-->

The code below will target a specific version of IE.

!
<--[if IE6]>
  	<link href="ie.css" rel="stylesheet" type="text/css" /!>
<[endif]-->

Use Vendor Prefixes

W3C standards are continually evolving. It’s good practice that you know certain drawbacks if a browser supports a particular CSS property. Styles like rounded corners, drop shadows and gradients can now be implemented using CSS alone without the use of images.

Using vendor prefixes will save you a lot of time without worrying if the browser supports your CSS.

Check out the following list of vendor prefixes.

1. Safari and Chrome (-webkit-)
2. Firefox (-moz-)
3. Opera (-o-)
4. Internet Explorer (-ms-)

As an example, let us use the transition property along with vendor prefixes to target specific browsers.


-webkit-transition: all 4s ease;
-moz-transition: all 4s ease;
-ms-transition: all 4s ease;
-o-transition: all 4s ease;
transition: all 4s ease;

Clear Your Floats

We all know how to float elements either to the left or right by using the style property float. By default, if you only just use onefloat along with a non-floated element, it will just flow along the side of the element you floated.

But that is not the issue here; most often we float divs to the left and right and wraps them with a container or wrapper. Consider the images below.

This image shows what we are trying to achieve.

This image shows what we are trying to achieve.

But instead our divs end up like this image.

But instead our divs end up like this image.

This issue can be solved by simply clearing the float using the CSS property clear and adding a value of both. That means both floats will be clear.

Font Size Measurement

Although there are no fixed thumb rules what font size measurement to use, I would recommend using ems (em) and percentages (%) since ems and percentages are based on the default preference set in the user’s browser.

Most people use pixels or points, which are measurements based on the resolution of the screen. This is always fixed.

Regardless your preference, this How to Size Text in CSS article would help you learn more about text in CSS.

Always Test Your Website Using Different Browsers

You might feel lazy testing your website in different browsers, thinking that it looks good in the modern browsers, believe me, I’ve been there but not doing it might give your website visitors a bad experience.

Using an emulator like spoon plugin might save you some time but it is highly recommended if you test your creation directly on the actual browser itself.

Code Simply

Coding simply makes the whole workflow easier and faster. What do I mean? Just by using simple elements in building up your website makes the browser’s task a lot easier to read your site.

As an example, instead of using tables or paragraphs in creating navigation you might just use an unordered list to form the navigation. In creating a responsive site, instead of using tables, divs works perfectly. Keep it simple, organize your codes and name them properly. Believe me, this will save you a lot of time and headache when you run into some problems.

Polishing Off

There are more good tips and tricks out there about cross browser compatibility but this should guide you on the basic foundation in creating a firm cross-browser experience.

It’s a good idea taking some time to read your codes and check them across different browsers. If you know some more tricks and tips that we’ve not listed here or you have some comments or suggestions, I would love to hear from you. Please free to drop them in the comment section below.

January 23 2014

10:07

Understanding JavaScript’s Function.prototype.bind


  

Function binding is most probably your least concern when beginning with JavaScript, but when you realize that you need a solution to the problem of how to keep the context of this within another function, then you might not realize that what you actually need is Function.prototype.bind().

The first time you hit upon the problem, you might be inclined to set this to a variable that you can reference when you change context. Many people opt for self, _this or sometimes context as a variable name. They’re all usable and nothing is wrong with doing that, but there is a better, dedicated way.

Jack Archibald tweets about caching this:

It should have been more apparent to me when Sindre Sorhus spelled it out:

I ignored this wise advice for many months.

What Problem Are We Actually Looking To Solve?

Here is sample code in which one could be forgiven for caching the context to a variable:


var myObj = {

    specialFunction: function () {

    },

    anotherSpecialFunction: function () {

    },

    getAsyncData: function (cb) {
        cb();
    },

    render: function () {
        var that = this;
        this.getAsyncData(function () {
            that.specialFunction();
            that.anotherSpecialFunction();
        });
    }
};

myObj.render();

If we had left our function calls as this.specialFunction(), then we would have received the following error:


Uncaught TypeError: Object [object global] has no method 'specialFunction'

We need to keep the context of the myObj object referenced for when the callback function is called. Calling that.specialFunction() enables us to maintain that context and correctly execute our function. However, this could be neatened somewhat by using Function.prototype.bind().

Let’s rewrite our example:


render: function () {

    this.getAsyncData(function () {

        this.specialFunction();

        this.anotherSpecialFunction();

    }.bind(this));

}

What Did We Just Do?

Well, .bind() simply creates a new function that, when called, has its this keyword set to the provided value. So, we pass our desired context, this (which is myObj), into the .bind() function. Then, when the callback function is executed, this references myObj.

If you’re interested to see what Function.prototype.bind() might look like and what its doing internally, here is a very simple example:


Function.prototype.bind = function (scope) {
    var fn = this;
    return function () {
        return fn.apply(scope);
    };
}

And here is a very simple use case:


var foo = {
    x: 3
}

var bar = function(){
    console.log(this.x);
}

bar(); // undefined

var boundFunc = bar.bind(foo);

boundFunc(); // 3

We’ve created a new function that, when executed, has its this set to foo — not the global scope, as in the example where we called bar();.

Browser Support

Browser Version support Chrome 7 Firefox (Gecko) 4.0 (2) Internet Explorer 9 Opera 11.60 Safari 5.1.4

As you can see, unfortunately, Function.prototype.bind isn’t supported in Internet Explorer 8 and below, so you’ll run into problems if you try to use it without a fallback.

Luckily, Mozilla Developer Network, being the wonderful resource it is, provides a rock-solid alternative if the browser hasn’t implemented the native .bind() method:


if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5 internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instance of fNOP && oThis
                                 ? this
                                 : oThis,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}

Patterns For Usage

When learning something, I find it useful not only to thoroughly learn the concept, but to see it applied to what I’m currently working on (or something close to it). Hopefully, some of the examples below can be applied to your code or to problems you’re facing.

Click Handlers

One use is to track clicks (or to perform an action after a click) that might require us to store information in an object, like so:


var logger = {
    x: 0,       
    updateCount: function(){
        this.x++;
        console.log(this.x);
    }
}

We might assign click handlers like this and subsequently call the updateCount() in our logger object:


document.querySelector('button').addEventListener('click', function(){
    logger.updateCount();
});

But we’ve had to create an unnecessary anonymous function to allow the this keyword to stand correct in the updateCount() function.

This could be neatened up, like so:


document.querySelector('button').addEventListener('click', logger.updateCount.bind(logger));

We’ve used the subtly handy .bind() function to create a new function and then set the scope to be bound to the logger object.

setTimeout

If you’ve ever worked with templating engines (such as Handlebars) or especially with certain MV* frameworks (I can only speak of Backbone.js from experience), then you might be aware of the problem that occurs when you render the template but want to access the new DOM nodes immediately after your render call.

Suppose we try to instantiate a jQuery plugin:


var myView = {

    template: '/* a template string containing our <select /> */',

    $el: $('#content'),

    afterRender: function () {
        this.$el.find('select').myPlugin();
    },

    render: function () {
        this.$el.html(this.template());
        this.afterRender();
    }
}

myView.render();

You might find that it works — but not all the time. Therein lies the problem. It’s a rat race: Whatever happens to get there first wins. Sometimes it’s the render, sometimes it’s the plugin’s instantiation.

Now, unbeknownst to some, we can use a slight hack with setTimeout().

With a slight rewrite, we can safely instantiate our jQuery plugin once the DOM nodes are present:


//

    afterRender: function () {
        this.$el.find('select').myPlugin();
    },

    render: function () {
        this.$el.html(this.template());
        setTimeout(this.afterRender, 0);        
    }

// 

However, we will receive the trusty message that the function .afterRender() cannot be found.

What we do, then, is throw our .bind() into the mix:


//

    afterRender: function () {
        this.$el.find('select').myPlugin();
    },

    render: function () {
        this.$el.html(this.template());
        setTimeout(this.afterRender.bind(this), 0);        
    }

// 

Now, our afterRender() function will execute in the correct context.

Tidier Event Binding With querySelectorAll

The DOM API improved significantly once it included such useful methods as querySelector, querySelectorAll and the classList API, to name a few of the many.

However, there’s not really a way to natively add events to a NodeList as of yet. So, we end up stealing the forEach function from the Array.prototype to loop, like so:


Array.prototype.forEach.call(document.querySelectorAll('.klasses'), function(el){
    el.addEventListener('click', someFunction);
});

We can do better than that, though, with our friend .bind():


var unboundForEach = Array.prototype.forEach,
    forEach = Function.prototype.call.bind(unboundForEach);

forEach(document.querySelectorAll('.klasses'), function (el) {
    el.addEventListener('click', someFunction);
});

We now have a tidy method to loop our DOM nodes.

Conclusion

As you can see, the .bind() function can be subtly included for many different purposes, as well as to neaten existing code. Hopefully, this overview has given you what you need to add .bind() to your own code (if necessary!) and to harness the power of transforming the value of this.

(al, il)


© Ben Howdle for Smashing Magazine, 2014.

Tags: Coding

January 15 2014

12:03

Creating Custom Shipping Methods In Magento


  

In this tutorial, we will use Magento’s powerful shipping-method code abstraction to create a shipping carrier. We will create two shipping methods that provide a fixed shipping price, allow for free shipping promotions, define logic based on an item’s weight and, finally, make it all configurable in the admin panel.

We will cover the following:

  • Extend the abstract shipping class and implement the required methods.
  • Make the shipping method configurable in Magento’s admin panel.
  • Work with promotions to allow for free shipping.
  • Allow tracking codes to be set against an order.

Before We Start

This tutorial assumes that you are familiar with how to create a Magento module. If you are not, please first refer to an earlier tutorial in this series, “The Basics of Creating a Magento Module.” To begin, you will need a Community or Enterprise installation of Magento, either locally or on a server that you are able to access.

The logic we will implement in this tutorial could be client-specific, so we will implement our module as a “local module” and, therefore, create it in app/code/local. Let’s start by creating the following file structure:


app
  - code
    - local
      - SmashingMagazine
        - MyCarrier
          - Model
            - Carrier.php
          - etc
            - config.xml
            - system.xml
  - etc
    - modules
      - SmashingMagazine_MyCarrier.xml

Now we can create SmashingMagazine_MyCarrier.xml:


<?xml version="1.0"?>
<config>
    <modules>
        <SmashingMagazine_MyCarrier>
            <active>true</active>
            <codePool>local</codePool>
            <depends>
                <Mage_Shipping />
            </depends>
        </SmashingMagazine_MyCarrier>
    </modules>
</config>

Notice the dependency on the shipping module. This ensures that our SmashingMagazine MyCarrier module will load after the Mage Shipping module, and it will throw an error if the Mage Shipping module has been disabled.

Carriers, Methods, Requests And Results

Before continuing, we should understand the terminology that Magento uses throughout its shipping abstraction. A “carrier” represents a shipping carrier in the sense you would expect (DPD, FedEx, etc.). Each carrier has one or many shipping methods, which contain the carrier code, the carrier title, the method code, the method title, a price to be paid by the customer and a cost of shipping to the retailer (optional).

During the checkout process, Magento creates a shipping-rate “request” object that contains all of the shipping information. The request can be used to determine which rates apply. For example, an “express” shipping method might not apply to orders under $10. All applicable rates are then “appended” to a shipping-rate “result” object, which generates a list of methods for the customer to choose from.

The following list names these concepts defined above, along with their representation as Magento classes:

  • Request
    Mage_Shipping_Model_Rate_Request
  • Result
    Mage_Shipping_Model_Rate_Result
  • Method
    Mage_Shipping_Model_Rate_Result_Method
  • Carrier
    Any class that extends the abstract class Mage_Shipping_Model_Carrier_Abstract and implements the interface Mage_Shipping_Model_Carrier_Interface

Extending The Shipping Abstract

To create our shipping carrier, we need to extend Mage_Shipping_Model_Carrier_Abstract, implement Mage_Shipping_Model_Carrier_Interface and add the required abstract methods.

The most important method is collectRates. This is the method that receives a shipping request, appends applicable shipping methods and returns a shipping result.

Copy the following code into app/code/local/SmashingMagazine/MyCarrier/Model/Carrier.php:


<?php
class SmashingMagazine_MyCarrier_Model_Carrier
    extends Mage_Shipping_Model_Carrier_Abstract
    implements Mage_Shipping_Model_Carrier_Interface
{
    protected $_code = 'smashingmagazine_mycarrier';

    public function collectRates(
        Mage_Shipping_Model_Rate_Request $request
    )
    {
        return Mage::getModel('shipping/rate_result');
    }

    public function getAllowedMethods()
    {
        return array();
    }
}

This is the skeleton for a shipping method class, but it is pretty useless because we have no shipping methods.

Let’s start by hardcoding a method. This method will be called “standard” and have a price of $9.99. For now, we will assume there is no cost to the retailer.


<?php
class SmashingMagazine_MyCarrier_Model_Carrier
    extends Mage_Shipping_Model_Carrier_Abstract
    implements Mage_Shipping_Model_Carrier_Interface
{
    protected $_code = 'smashingmagazine_mycarrier';
   
    public function collectRates(
        Mage_Shipping_Model_Rate_Request $request
    )
    {
        $result = Mage::getModel('shipping/rate_result');
        /* @var $result Mage_Shipping_Model_Rate_Result */
       
        $result->append($this->_getStandardShippingRate());
       
        return $result;
    }

    protected function _getStandardShippingRate()
    {
        $rate = Mage::getModel('shipping/rate_result_method');
        /* @var $rate Mage_Shipping_Model_Rate_Result_Method */
       
        $rate->setCarrier($this->_code);
        /**
         * getConfigData(config_key) returns the configuration value for the
         * carriers/[carrier_code]/[config_key]
         */
        $rate->setCarrierTitle($this->getConfigData('title'));
       
        $rate->setMethod('standand');
        $rate->setMethodTitle('Standard');
       
        $rate->setPrice(4.99);
        $rate->setCost(0);
       
        return $rate;
    }
   
    public function getAllowedMethods()
    {
        return array(
            'standard' => 'Standard',
        );
    }
}

Now we are just one step away from a working shipping method — the module configuration file.

Module Configuration

The module configuration has the standard structure (as detailed in “The Basics of Creating a Magento Module”). Copy the following into app/code/local/SmashingMagazine/MyCarrier/etc/config.xml:


<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <SmashingMagazine_MyCarrier>
            <module>0.0.1</module>
        </SmashingMagazine_MyCarrier>
    </modules>
    <global>
        <models>
            <smashingmagazine_mycarrier>
                <class>SmashingMagazine_MyCarrier_Model</class>
            </smashingmagazine_mycarrier>
        </models>
    </global>
    <!-- Default configuration -->
    <default>
        <carriers>
            <smashingmagazine_mycarrier>
                <active>1</active>
                <!--
                     This configuration should not be made visible
                     to the administrator, because it specifies
                     the model to be used for this carrier.
                -->
                <model>smashingmagazine_mycarrier/carrier</model>
                <!--
                    The title as referenced in the carrier class
                -->
                <title>Smashing Magazine Carrier</title>
                <!--
                    The sort order specifies the position that
                    this carrier appears relative to the other
                    carriers available in checkout.
                -->
                <sort_order>10</sort_order>
                <!--
                    Out of the box, Magento offers shipping
                    carriers the ability to restrict themselves
                    to specific countries. For this configuration
                    option, 0 means allow all countries available,
                    and 1 means allow all countries specified
                    in the country list that we will add later
                    in system.xml
                -->
                <sallowspecific>0</sallowspecific>
            </smashingmagazine_mycarrier>
        </carriers>
    </default>
</config>

This default configuration “registers” the model we have just created as a shipping carrier. As you may know, Magento merges all of its configuration XML together and caches the result (if the cache is enabled). When a customer loads the shipping-method list, Magento loops through all of the carriers in the carriers node of the configuration and loads the shipping methods from the models determined by the “active” carriers.

We should now be able to see our shipping method in the checkout.

Making It Configurable

We have already specified the default configuration for this module. So, let’s make our module configurable in the admin panel by copying the following into app/code/local/SmashingMagazine/etc/system.xml:


<?xml version="1.0" encoding="UTF-8"?>
<config>
    <sections>
        <carriers translate="label" module="shipping">
            <groups>
                <smashingmagazine_mycarrier translate="label">
                    <label>Smashing Magazine Carrier</label>
                    <frontend_type>text</frontend_type>
                    <sort_order>2</sort_order>
                    <show_in_default>1</show_in_default>
                    <show_in_website>1</show_in_website>
                    <show_in_store>1</show_in_store>
                    <fields>
                        <!--
                            The following fields are available
                            to modify in the admin panel.
                            The values are saved in the
                            database.

                            This shipping carrier abstract checks
                            this value to determine whether
                            the carrier should be shown.
                        -->
                        <active translate="label">
                            <label>Enabled</label>
                            <frontend_type>select</frontend_type>
                            <source_model>adminhtml/system_config_source_yesno</source_model>
                            <sort_order>1</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>0</show_in_store>
                        </active>
                        <!--
                            This value can be used to specify a
                            custom title for our method.
                        -->
                        <title translate="label">
                            <label>Title</label>
                            <frontend_type>text</frontend_type>
                            <sort_order>2</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </title>
                        <!--
                            The sort order is used in Magento
                            to determine what order the carrier
                            will appear in relative to the
                            other carriers available.
                        -->
                        <sort_order translate="label">
                            <label>Sort Order</label>
                            <frontend_type>text</frontend_type>
                            <sort_order>100</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>0</show_in_store>
                        </sort_order>
                        <!--
                            This value is used to specify whether
                            the carrier is available only for
                            specific countries or all countries
                            available in the current Magento
                            installation.
                        -->
                        <sallowspecific translate="label">
                            <label>Ship to Applicable Countries</label>
                            <frontend_type>select</frontend_type>
                            <sort_order>90</sort_order>
                            <frontend_class>shipping-applicable-country</frontend_class>
                            <source_model>adminhtml/system_config_source_shipping_allspecificcountries</source_model>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>0</show_in_store>
                        </sallowspecific>
                        <!--
                            If 'specific countries' is chosen
                            in the previous option, then this field
                            allows the administrator to specify
                            which specific countries this carrier
                            should be available for.
                        -->                   
                        <specificcountry translate="label">
                            <label>Ship to Specific Countries</label>
                            <frontend_type>multiselect</frontend_type>
                            <sort_order>91</sort_order>
                            <source_model>adminhtml/system_config_source_country</source_model>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>0</show_in_store>
                            <can_be_empty>1</can_be_empty>
                        </specificcountry>
                    </fields>
                </smashingmagazine_mycarrier>
            </groups>
        </carriers>
    </sections>
</config>

These fields are visible in the admin panel by navigating to System → Configuration → Shipping Method → Smashing Magazine Carrier.

Using Multiple Shipping Methods

Express Shipping

So far, we have added a standard shipping method for the price of $9.99. However, the customer may wish to pay more to receive their order faster. The following code creates a shipping rate with a higher price and different shipping code:


protected function _getExpressShippingRate()
{
    $rate = Mage::getModel('shipping/rate_result_method');
    /* @var $rate Mage_Shipping_Model_Rate_Result_Method */
    $rate->setCarrier($this->_code);
    $rate->setCarrierTitle($this->getConfigData('title'));
    $rate->setMethod('express');
    $rate->setMethodTitle('Express (Next day)');
    $rate->setPrice(12.99);
    $rate->setCost(0);
    return $rate;
}

To make this shipping rate appear next to the standard rate that we created earlier, we will need to modify the code in the collectRates method to append the new rate. Add the following before the return statement:


$result->append($this->_getExpressShippingRate());

Finally, add the shipping method to the allowed methods array in getAllowedMethods:


public function getAllowedMethods()
{
    return array(
        'standard' => 'Standard',
        'express' => 'Express',
    );
}

Free Shipping

Many websites offer free shipping when a customer spends over a certain amount or satisfies certain conditions. We need to be able to do the same here. In Magento, you can set up a “Shopping Cart Rule.” With it, you can specify a set of conditions and define actions if those conditions are met; one of those actions is free shipping.

If free shipping is available for a customer, then the request object will populated with is_free_shipping set to 1. We need to check for and handle this possibility in our shipping method. Add the following before the return statement in the collectRates method:


if ($request->getFreeShipping()) {
    /**
     *  If the request has the free shipping flag,
     *  append a free shipping rate to the result.
     */
    $freeShippingRate = $this->_getFreeShippingRate();
    $result->append($freeShippingRate);
}

Add the following code to app/code/local/SmashingMagazine/MyCarrier/Model/Carrier.php:


protected function _getFreeShippingRate()
{
    $rate = Mage::getModel('shipping/rate_result_method');
    /* @var $rate Mage_Shipping_Model_Rate_Result_Method */
    $rate->setCarrier($this->_code);
    $rate->setCarrierTitle($this->getConfigData('title'));
    $rate->setMethod('free_shipping');
    $rate->setMethodTitle('Free Shipping (3 - 5 days)');      
    $rate->setPrice(0);
    $rate->setCost(0);
    return $rate;       
}

Remember to add the method to the allowed methods array:


public function getAllowedMethods()
{
    return array(
        'standard' => 'Standard',
        'express' => 'Express',
        'free_shipping' => 'Free Shipping',
    );
}

Taking It A Bit Further

Tracking Deliveries

Tracking numbers may be added to shipments through either the admin panel or an API. But to make our shipping methods visible in the admin panel, we will have to overwrite the isTrackingAvailable method in the abstract to return true.

Add the following method to the end of SmashingMagazine_MyCarrier_Model_Carrier.


public function isTrackingAvailable()
{
    return true;
}

You should now see the shipping carriers and methods available in the delivery courier drop-down menu when you try to place a shipment in the admin panel.

Using the Weight

Earlier, we added a more expensive express shipping method. But heavier items that require complex shipping arrangements might not be available for next-day delivery. We can check for this using the weight attribute of the request object by wrapping the code that appends the shipping method to the shipping result:


// ...
$expressWeightThreshold =
    $this->_getConfigData('express_weight_threshold');

$eligibleForExpressDelivery = true;
foreach ($request->getAllItems() as $_item) {
    if ($_item->getWeight() > $expressWeightThreshold) {
        $eligibleForExpressDelivery = false;
    }
}         

if ($eligibleForExpressDelivery) {
    $result->append($this->_getExpressShippingRate());
}
// ...

Notice that we have added a reference to the configuration. To make this appear in the admin panel, we need to add the following XML to the app/code/local/SmashingMagazine/MyCarrier/etc/system.xml file in the fields node:


<express_weight_threshold translate="label">
    <label>Express Weight Threshold</label>
    <frontend_type>text</frontend_type>
    <sort_order>100</sort_order>
    <show_in_default>1</show_in_default>
    <show_in_website>1</show_in_website>
    <show_in_store>0</show_in_store>
</express_weight_threshold>

Summary

With a relatively small amount of code, we have been able to define our own shipping logic that integrates with checkout, the admin panel and even the shopping-cart promotions. You can learn much more about creating shipping modules in Magento by looking at the examples in the core files — namely, Mage_Usa and Mage_Shipping.

I welcome any questions and would love to hear your feedback in the comments area below.

(al, ea)


© Matthew Haworth for Smashing Magazine, 2014.

Tags: Coding

January 13 2014

10:38

Writing A Better JavaScript Library For The DOM


  

At present, jQuery is the de facto library for working with the document object model (DOM). It can be used with popular client-side MV* frameworks (such as Backbone), and it has a ton of plugins and a very large community. As developers’ interest in JavaScript increases by the minute, a lot of people are becoming curious about how native APIs really work and about when we can just use them instead of including an extra library.

Lately, I have started to see more and more problems with jQuery, at least my use of it. Most of the problems are with jQuery’s core and can’t be fixed without breaking backwards compatibility — which is very important. I, like many others, continued using the library for a while, navigating all of the pesky quirks every day.

Then, Daniel Buchner created SelectorListener, and the idea of “live extensions” manifested. I started to think about creating a set of functions that would enable us to build unobtrusive DOM components using a better approach than what we have used so far. The objective was to review existing APIs and solutions and to build a clearer, testable and lightweight library.

Adding Useful Features To The Library

The idea of live extensions encouraged me to develop the better-dom project, although other interesting features make the library unique. Let’s review them quickly:

  • live extensions
  • native animations
  • embedded microtemplating
  • internationalization support

Live Extensions

jQuery has a concept called “live events.” Drawing on the idea of event delegation, it enables developers to handle existing and future elements. But more flexibility is required in a lot of cases. For example, delegated events fall short when the DOM needs to be mutated in order to initialize a widget. Hence, live extensions.

The goal is to define an extension once and have any future elements run through the initialization function, regardless of the widget’s complexity. This is important because it enables us to write Web pages declaratively; so, it works great with AJAX applications.

Live extensions make is easier to handle any future elements.
Live extensions enable you to handle any future elements without the need to invoke the initialization function. (Image credits)

Let’s look at a simple example. Let’s say our task is to implement a fully customizable tooltip. The :hover pseudo-selector won’t help us here because the position of the tooltip changes with the mouse cursor. Event delegation doesn’t fit well either; listening to mouseover and mouseleave for all elements in the document tree is very expensive. Live extensions to the rescue!


DOM.extend("[title]", {
  constructor: function() {
    var tooltip = DOM.create("span.custom-title");

    // set the title's textContent and hide it initially
    tooltip.set("textContent", this.get("title")).hide();

    this
      // remove legacy title
      .set("title", null)
      // store reference for quicker access
      .data("tooltip", tooltip)
      // register event handlers
      .on("mouseenter", this.onMouseEnter, ["clientX", "clientY"])
      .on("mouseleave", this.onMouseLeave)
      // insert the title element into DOM
      .append(tooltip);
  },
  onMouseEnter: function(x, y) {
    this.data("tooltip").style({left: x, top: y}).show();
  },
  onMouseLeave: function() {
    this.data("tooltip").hide();
  }
});

We can style the .custom-title element in CSS:


.custom-title {
  position: fixed; /* required */
  border: 1px solid #faebcc;
  background: #faf8f0;
}

The most interesting part happens when you insert a new element with a title attribute in the page. The custom tooltip will work without any initialization call.

Live extensions are self-contained; thus, they don’t require you to invoke an initialization function in order to work with future content. So, they can be combined with any DOM library and will simplify your application logic by separating the UI code into many small independent pieces.

Last but not least, a few words on Web components. A section of the specification, “Decorators,” aims to solve a similar problem. Currently, it uses a markup-based implementation with a special syntax for attaching event listeners to child elements. But it’s still an early draft:

“Decorators, unlike other parts of Web Components, do not have a specification yet.”

Native Animations

Thanks to Apple, CSS has good animation support now. In the past, animations were usually implemented in JavaScript via setInterval and setTimeout. It was a cool feature — but now it’s more like a bad practice. Native animations will always be smoother: They are usually faster, take less energy and degrade well if not supported by the browser.

In better-dom, there is no animate method: just show, hide and toggle. To capture a hidden element state in CSS, the library uses the standards-based aria-hidden attribute.

To illustrate how it works, let’s add a simple animation effect to the custom tooltip that we introduced earlier:


.custom-title {
  position: fixed; /* required */
  border: 1px solid #faebcc;
  background: #faf8f0;
  /* animation code */
  opacity: 1;
  -webkit-transition: opacity 0.5s;
  transition: opacity 0.5s;
}

.custom-title[aria-hidden=true] {
  display: block; /* have to override default "none" */
  opacity: 0;
}

Internally, show() and hide() just set the aria-hidden attribute value to be false and true. This is enough to enable the CSS to handle the animations and transitions.

You can see a demo with more animation examples that use better-dom.

Embedded Microtemplating

HTML strings are annoyingly verbose. Looking for a replacement, I found the excellent Emmet. Today, Emmet is quite a popular plugin for text editors, and it has a nice and compact syntax. Take this HTML:


body.append("<ul><li class='list-item'></li><li class='list-item'></li><li class='list-item'></li></ul>");

And compare it to the equivalent microtemplate:


body.append("ul>li.list-item*3");

In better-dom, any method that accepts HTML may use Emmet expressions as well. The abbreviation parser is fast, so no need to worry about a performance penalty. A template precompilation function also exists to be used on demand.

Internationalization Support

Developing a UI widget often requires localization — not an easy task. Over the years, many have tackled this in different ways. With better-dom, I believe that changing the state of a CSS selector is like switching languages.

Conceptually speaking, switching a language is like changing the “representation” of content. In CSS2, several pseudo-selectors help to describe such a model: :lang and :before. Take the code below:


[data-i18n="hello"]:before {
  content: "Hello Maksim!";
}

[data-i18n="hello"]:lang(ru):before {
  content: "Привет Максим!";
}

The trick is simple: The value of the content property changes according to the current language, which is determined by the lang attribute of the html element. By using data attributes such as data-i18n, we can maintain the textual content in HTML:


[data-i18n]:before {
  content: attr(data-i18n);
}

[data-i18n="Hello Maksim!"]:lang(ru):before {
  content: "Привет Максим!";
}

Of course, such CSS isn’t exactly attractive, so better-dom has two helpers: i18n and DOM.importStrings. The first is used to update the data-i18n attribute with the appropriate value, and the second localizes strings for a particular language.


label.i18n("Hello Maksim!");
// the label displays "Hello Maksim!"
DOM.importStrings("ru",  "Hello Maksim!", "Привет Максим!");
// now if the page is set to ru language,
// the label will display "Привет Максим!"
label.set("lang", "ru");
// now the label will display "Привет Максим!"
// despite the web page's language

Parameterized strings can be used as well. Just add ${param} variables to a key string:


label.i18n("Hello ${user}!", {user: "Maksim"});
// the label will display "Hello Maksim!"

Making Native APIs More Elegant

Generally, we want to stick to standards. But sometimes the standards aren’t exactly user-friendly. The DOM is a total mess, and to make it bearable, we have to wrap it in a convenient API. Despite all of the improvements made by open-source libraries, some parts could still be done better:

  • getter and setter,
  • event handling,
  • functional methods support.

Getter and Setter

The native DOM has the concept of attributes and properties of elements that could behave differently. Assume we have the markup below on a Web page:


<a href="/chemerisuk/better-dom" id="foo" data-test="test">better-dom</a>

To explain why “the DOM is a total mess,” let’s look at this:


var link = document.getElementById("foo");

link.href; // => "https://github.com/chemerisuk/better-dom"
link.getAttribute("href"); // => "/chemerisuk/better-dom"
link["data-test"]; // => undefined
link.getAttribute("data-test"); // => "test"

link.href = "abc";
link.href; // => "https://github.com/abc"
link.getAttribute("href"); // => "abc"

An attribute value is equal to the appropriate string in HTML, while the element property with the same name could have some special behavior, such as generating the fully qualified URL in the listing above. These differences can be confusing.

In practice, it’s hard to imagine a practical situation in which such a distinction would be useful. Moreover, the developer should always keep in mind which value (attribute or property) is being used that introduces unnecessary complexity.

In better-dom, things are clearer. Every element has only smart getters and setters.


var link = DOM.find("#foo");

link.get("href"); // => "https://github.com/chemerisuk/better-dom"
link.set("href", "abc");
link.get("href"); // => "https://github.com/abc"
link.get("data-attr"); // => "test"

In the first step, it does a property lookup, and if it’s defined, then it’s used for manipulation. Otherwise, getter and setter work with the appropriate attribute of the element. For booleans (checked, selected, etc.), you could just use true or false to update the value: Changing such a property on an element would trigger the appropriate attribute (native behavior) to be updated.

Improved Event Handling

Event handling is a big part of the DOM, however, I’ve discovered one fundamental problem: Having an event object in element listeners forces a developer who cares about testability to mock the first argument, or to create an extra function that passes only event properties used in the handler.


var button = document.getElementById("foo");

button.addEventListener("click", function(e) {
  handleButtonClick(e.button);
}, false);

This is really annoying. What if we extracted the changing part as an argument? This would allow us to get rid of the extra function:


var button = DOM.find("#foo");

button.on("click", handleButtonClick, ["button"]);

By default, the event handler passes the ["target", "defaultPrevented"] array, so no need to add the last argument to get access to these properties:


button.on("click", function(target, canceled) {
  // handle button click here
});

Late binding is supported as well (I’d recommend reading Peter Michaux’s review of the topic). It’s a more flexible alternative to the regular event handlers that exist in the W3C’s standard. It could be useful when you need frequent on and off method calls.


button._handleButtonClick = function() { alert("click!"); };

button.on("click", "_handleButtonClick");
button.fire("click"); // shows "clicked" message
button._handleButtonClick = null;
button.fire("click"); // shows nothing

Last but not least, better-dom has none of the shortcuts that exist in legacy APIs and that behave inconsistently across browsers, like click(), focus() and submit(). The only way to call them is to use the fire method, which executes the default action when no listener has returned false:


link.fire("click"); // clicks on the link
link.on("click", function() { return false; });
link.fire("click"); // triggers the handler above but doesn't do a click

Functional Methods Support

ES5 standardized a couple of useful methods for arrays, including map, filter and some. They allow us to use common collection operations in a standards-based way. As a result, today we have projects like Underscore and Lo-Dash, which polyfill these methods for old browsers.

Each element (or collection) in better-dom has the methods below built in:

  • each (which differs from forEach by returning this instead of undefined)
  • some
  • every
  • map
  • filter
  • reduce[Right]

var urls, activeLi, linkText; 

urls = menu.findAll("a").map(function(el) {
  return el.get("href");
});
activeLi = menu.children().filter(function(el) {
  return el.hasClass("active");
});
linkText = menu.children().reduce(function(memo, el) {
  return memo || el.hasClass("active") && el.find("a").get()
}, false);

Avoiding jQuery Problems

Most of the following issues can’t be fixed in jQuery without breaking backwards compatibility. That’s why creating a new library seemed like the logical way out.

  • the “magical” $ function
  • the value of the [] operator
  • issues with return false
  • find and findAll

The “Magical” $ Function

Everyone has heard at some point that the $ (dollar) function is kind of like magic. A single-character name is not very descriptive, so it looks like a built-in language operator. That’s why inexperienced developers call it inline everywhere.

Behind the scenes, the dollar is quite a complex function. Executing it too often, especially in frequent events such as mousemove and scroll, could cause poor UI performance.

Despite so many articles recommending jQuery objects to be cached, developers continue to insert the dollar function inline, because the library’s syntax encourages them to use this coding style.

Another issue with the dollar function is that it allows us to do two completely different things. People have gotten used to such a syntax, but it’s a bad practice of a function design in general:


$("a"); // => searches all elements that match “a” selector
$("<a>"); // => creates a <a> element with jQuery wrapper

In better-dom, several methods cover the responsibilities of the dollar function in jQuery: find[All] and DOM.create. find[All] is used to search element(s) according to the CSS selector. DOM.create makes a new elements tree in memory. Their names make it very clear what they are responsible for.

Value of the [] Operator

Another reason for the problem of frequent dollar function calls is the brackets operator. When a new jQuery object is created, all associated nodes are stored in numeric properties. But note that the value of such a property contains a native element instance (not a jQuery wrapper):


var links = $("a");

links[0].on("click", function() { ... }); // throws an error
$(links[0]).on("click", function() { ... }); // works fine

Because of such a feature, every functional method in jQuery or another library (like Underscore) requires the current element to be wrapped with $() inside of a callback function. Therefore, developers must always keep in mind the type of object they are working with — a native element or a wrapper — despite the fact that they are using a library to work with the DOM.

In better-dom, the brackets operator returns a library’s object, so developers can forget about native elements. There is only one acceptable way to access them: by using a special legacy method.


var foo = DOM.find("#foo");

foo.legacy(function(node) {
  // use Hammer library to bind a swipe listener
  Hammer(node).on("swipe", function(e) {
    // handle swipe gesture here
  }); 
});

In reality, this method is required in very rare cases, such as to be compatible with a native function or with another DOM library (like Hammer in the example above).

Issues With return false

One thing that really blows my mind is the strange return false interception in jQuery’s event handlers. According to the W3C’s standards, it should in most cases cancel the default behavior. In jQuery, return false also stops event delegation.

Such interception creates problems:

  1. Invoking stopPropagation() by itself could lead to compatibility problems, because it prevents listeners that are related to some other task from doing their work.
  2. Most developers (even experienced ones) are not aware of such behavior.

It’s unclear why the jQuery community decided to go cross-standards. But better-dom is not going to repeat the same mistake. Thus, return false in an event handler only prevents the browser’s default action, without messing with event propagation, as everyone would expect.

find and findAll

Element search is one of the most expensive operations in the browser. Two native methods could be used to implement it: querySelector and querySelectorAll. The difference is that the first one stops searching on the first match.

This feature enables us to decrease the iterations count dramatically in certain cases. In my tests, the speed was up to 20 times faster! Also, you can expect that the improvement will grow according to the size of the document tree.

jQuery has a find method that uses querySelectorAll for general cases. Currently, no function uses querySelector to fetch only the first matched element.

The better-dom library has two separate methods: find and findAll. They allow us to use querySelector optimization. To estimate the potential improvement in performance, I searched for the usage of these methods in all of the source code of my last commercial project:

  • find
    103 matches across 11 files
  • findAll
    14 matches across 4 files

The find method is definitely much more popular. It means that querySelector optimization makes sense in most use cases and could give a major performance boost.

Conclusion

Live extensions really make solving front-end problems much easier. Splitting the UI in many small pieces leads to more independent and maintainable solutions. But as we’ve shown, a framework is not only about them (although it is the main goal).

One thing I’ve learned in the development process is that if you don’t like a standard or you have a different opinion of how things should work, then just implement it and prove that your approach works. It’s really fun, too!

More information about the better-dom project can be found on GitHub.

(al, il, ea)


© Maksim Chemerisuk for Smashing Magazine, 2014.

Tags: Coding

January 06 2014

14:00

70 Best Web Design Tutorials of 2013

2013 has ended with a great blast and I’m pretty sure that everyone has done their countdowns and throwback blog posts, tweets and even status updates. It’s a pretty cool thing to do because you get to remember all the awesome things 2013 has given you. You look back at how bad you were design-wise last year and how well you’ve improved. With this, you get to assess yourself how much more you could exert and, in turn, how much more money you could earn. Retrospection also tells you what more could you learn. Seeing what you’re weak at will also allow you to see where you could trained. That is why self-correction and openness to new things is a great habit for a designer.

Speaking of learning, were you able to catch some of the best web design tutorials of 2013? has given a lot of trends and knowledge that made tutorials prevalent as stars in the night sky. Well, for one thing, these tutorials will teach give you new knowledge and improve on what you already know. You’ll never know; maybe one day, the web design noob becomes the ninja.

Now, let’s look back at the top 70 tutorials that rocked the web design world in 2013. Some of these, you might have seen before. Others, you might have missed. But one thing is for sure, these tutorials don’t kid when they say they will teach you new cool things. So, for those who lived under the rock last year, let’s roll!

Graphic Design

The following tutorials highlights graphic designs. It includes typography and basic web mock up designs. These tutorials are aimed at making your website look better using graphic design elements such as fonts, shapes and a lot more!

Working with Types: Typography Design Tutorial for Beginners
This one discusses the basics of typography and how to create your very first design! For beginners, this one’s recommended.

70-tutorials-2013-typography

Your Logo as a Web Font Ligature
Create beautiful logos using types and not worry about CSS3 cross browser considerations (which is very, very tricky, messy and takes a lot of effort). This one shall render with better quality with the smallest effort and time.

70-tutorials-2013-ligature

Flat Web Design Tutorial – Portfolio Landing Page [FREE Download]

This tutorial will teach you to create a very beautiful flat design portfolio landing page. Very easy to follow and tinker with!

70-tutorials-2013-portfolio-landing-page

PSD to HTML/CSS, SASS, CSS3, HTML5

Three of the best used platforms in web design are used in this section. This is a list of PSD to HTML/CSS tutorials and HTML and CSS tricks. Make your site look better using these easy-to-follow tutorials.

Basic Web Design Video Course – Wireframing, Photoshop Tools & Panels, and Designing [Part 1]

Talking about details, this one goes off the charts. Broken into parts, this comprehensive tutorial discusses the major facets of web design that you should know. Great for both beginners and experts.

70-tutorials-2013-wireframing

How to Create Your Own Custom Grid System in Photoshop

Grid systems are of great help in putting different elements in a page without becoming lost in pixels. This tutorial teaches you how to use a very simple tool (a Photoshop plugin called Guide Guide) to design using grids.

70-tutorials-2013-grid-using-photoshop

 Hide / Show Notification Bar With CSS3

Do you want to create a sliding notification bar? This tutorial is for you. Written in the simplest and most understandable way possible, you will be amazed at how easy it is to add a notification bar in your website design.

70-tutorials-2013-hide-or-show-notifications

Web Design: How to Convert CSS to SASS & SCSS

SASS and SCSS are pretty new to some designers. With CSS still known as a very plausible and useful language, learning how to convert CSS to SASS and SCCS is not bad for some knowledge that you might use in the future. When you read this tutorial, you will see how these languages are both alike and different in many ways.

70-tutorials-2013-css-to-sass

How To Style Google Maps

Putting maps on your website is great, especially for corporate clients. But styling them? That’s better. Now learn how to do this through this tutorial, and you’ll be able to look at maps in a whole new way.

710tutorials-2013-Style-google-maps

Getting Creative with the Google Maps API 

We’ve seen earlier how could we style maps. This one is another tutorial about this. The only difference is that it pushed further as it incorporated a lot of new elements that we thought we could never place in maps before. Amaze your clients at how you could design a map just for them.

70-tutorials-2013-googlemaps-creative

Web Design: Equal Column Height With CSS
What bugs most designers is unequal column heights. An OC designer will have a hard time tinkering with pixels and doing trial and error computations. This becomes a major headache for beginners as they still don’t know the tricks of the trade yet. So, this tutorial becomes a must if they need to add more to their skills. Look at this tutorial to see which of your methods is easier  to execute and which produces more accurate results!

70-tutorials-2013-unequal-height

How To Create a Web-Based Video Player

It’s always fun to customize all the elements you post in your website. It gives a personal touch to it. More so, you achieve uniformity in design. This tutorial is a proof of this perk. Learn how to create a web-based video player that you can really call ‘yours’.

70-tutorials-2013-web-based-video-payer

How To Use CSS3 Multiple Backgrounds
When I first read this tutorial, I never knew that having multiple backgrounds in one div or class is possible. Well, we all have our moments of learning, don’t we? This one is a great yet simple example of this. 

70-tutorials-2013-multiple-backgrounds

Creating Content Tabs with Pure CSS

In the early popularity of CSS3,  this tutorial has done wonders for me. Being able to create tabbed contents easily with CSS? It’s a pretty amazing thing to know.

70-tutorials-2013-content-tabs

Fun CSS Loading Animations

I’ve talked about loading animations before and how cool they are. They make your website more fun and makes your audience do (or see) something while they’re waiting. Interested? Learn from this tutorial.

70-tutorials-2013-loading-animation
Flat UI Pro Tutorial: How To Use LESS to Create a Sign-In Form

David East gives a very simple and detailed explanation on how he created a custom sign-up form using LESS and HTML. It’s very simple and easy to follow. You just need basic coding knowledge. That’s all.

70-tutorials-2013-custom-sign-up-using-css-and-less

Create an e-Commerce Web Element with CSS3
Have you ever had any e-commerce customers? Or you wanted to put up one? Well, this tutorial is a good kick-start. Learn how to put e-commerce elements in any site using only CSS3!

70-tutorials-2013-e-commerce

Create a Drop Down Menu with Search Box in CSS3 and HTML
Do you want to create a very simple and elegant drop-down menu and search box? This one’s for you. With its very trendy design and easy to follow steps, a drop down menu could never be created this easier.

70-tutorials-2013-drop-down-menu

Learn SASS on Treehouse
“SASS is the most mature, stable, and powerful professional grade CSS extension language in the world.” So does the SASS website says. So where to learn this Syntactically Awesome Style Sheets? Nowhere else but on this tutorial!

70-tutorials-2013-sass

Creating Non-Rectangular Layouts with CSS Shapes

Sometimes it’s good to deviate from designing norms. A good example of this is non-rectangular layouts. It gives a refreshing look to the over-all design of the page. Give your clients and yourself a new view by trying this tutorial out.

70-tutorials-2013-non-rectangular

How To Create Flat Style Breadcrumb Links with CSS

Breadcrumbs are great tools for navigation. It lets you know where are you exactly on the website and prevents you from getting lost in the deep, dark woods. Learn how to save your viewers with breadcrumbs and learn how to do it creatively using CSS!

710tutorials-2013-bread-crumbs

Bring Your Forms U- to-Date With CSS3 and HTML5 Validation

Great for contact forms, this tutorial will help you create a simple but very useful contact form. I was amazed at how it matched with any design because of its awesome simplicity.

70-tutorials-2013-contact-form

Quick Tip: Don’t Forget the Viewport Meta Tag

Is responsive designing complicated? Or are you just complicating things out? It turns out that, most of the time, you’re the one complicating things out. Good thing you have this tutorial to clarify.

70-tutorials-2013-viewport

How to Customize the Foundation 4 Top Bar

Another navigation menu tutorial proves to be brilliant as it crosses over browsers and devices with simplicity, style and sense. Great tutorial.

70-tutorials-2013-ctopbar

Build a Freshly Squeezed Responsive Grid System

Using one of the many grid systems, this tutorial is aimed towards teaching designers to use grids for easy styling. This method is a very great tool, especially when doing responsive designs.

70-tutorials-2013-lemonade

Build an HTML Email Template From Scratch

Email templates could attract more traffic to your website. Once you send your subscriber cool emails, they will be awed at how good you are and might even hire you because of this. So learn how.

70-tutorials-2013-email-template

Quick Tip: Implement a Sticky “Back to Top” Button

One of the greatest advancements in web design is that “back-to-the-top” button which allows you to scroll into the top of the page in a single click. This tutorial lets you create that. Put it on your website now!

710tutorials-2013-back-to the-top

Quick Tip: Using Images as Fullscreen Faux-Gradient Backgrounds

I personally love full screen backgrounds. They give professional and neat-looking feel to your website. Try this on your designs and your client will surely love them!

70-tutorials-2013-faux-background

Circle Navigation Effect with Css3

This easy-to-navigate and use navigation effect gives a very clever method of presenting data in your website. Another quality tutorial for your convenience.

710tutorials-2013-circle-nav

Blur Menu with CSS3 Transitions

Another amazing navigation effects, this tutorial lets you create a very cool navigation that blurs unselected links. I recommend this for designers who want to design portfolios.

70-tutorials-2013-blur-menu

Animated Border Menus

Who would have thought that you could do something as clever as this? I love this tutorial because it lets me understand the greatness and coolness of using borders with animations.

70-tutorials-2013-animated-border=menus

Building a Circular Navigation with CSS Transforms

The world surely is both flat and round as this tutorial proves that something amazing can be achieved by fusing two opposing ideas in the literal aspect. With different effects to tinker on and with this conveniently written article, you’ll soon adapt this in your creative outputs.

70-tutorials-2013-circular-navigation

jQuery

Here’s for you guys who like to have a little bit of a challenge. Some advanced stuff! jQuery effects that will surely make your clients’ eyes pop! The list is easy to follow and comes with demos that you could download and tinker!

Beginner’s Guide to Working with jQuery Animation

This tutorial delves into jQuery animation basics and lets you understand these fundamentals while preparing you for more advanced stuff. For beginners, this is a yes-yes tutorial.

70-tutorials-2013-jquery

jQuery How-to: Creating and Inserting New Elements

Learn the basics of jQuery from this very comprehensive set of tutorials!

70-tutorials-2013-tutorial-part-1

Creating Touch-Enabled Slider With SwipeJS And jQuery

With the advent of mobile touch devices, doing jQuery sliders became a little bit more complicated. As we all know, it is very easy for web designers to use jQuery slider navigations. But it becomes difficult when transforming these into mobile as the navigation icons become cluttered in the tight screen resolution. The answer to this? Make the slider touch-enabled.

70-tutorials-2013-touch-enabled-slider

Customizing Browser Scroll Bar With CSS / JQuery

Does the default scroll bar bore you? Well, good news is, you could re-design them. Learn how from this tutorial.

70-tutorials-2013-scroll-bar

Building a Mobile Panel With jQuery Mobile

I first saw this mobile panel on the mobile version of Facebook. Well, for one thing, I knew that someday someone will do the re-creation of this panel. So, here it is!

70-tutorials-2013-mobile-panel

How to Implement Infinite Page Scroll Effect in Static Webpages

Are you into infinite scrolling? Or do you want your design to have one? You came to the right place because I will tell you where it is. It’s right in this particular tutorial. Learn how to seamlessly feed your future viewers with content.

710tutorials-2013-infinite-scroll

Smart (Auto-Hiding) Website Header & Footer Using jQuery

This tutorial discusses and demonstrates how you can build a sticky header bar that will auto-hide after a few scrolls. This is very advantageous because it allows you to achieve the ease of access capability of a sticky nav bar, and the convenience as well as lack of distraction in a regular nav bar.

710tutorials-2013-sticky-nav-bar

Sticky Position (Bar) with CSS or jQuery

Make your sidebar and other elements on your website always visible. Avoid scrolling them together with the content through this tutorial.

710tutorials-2013-sticky-nav-bar=02

Create an Awesome 3D Interactive Object Using Only Images and jQuery

Do you have a client who wants to put up an online shopping website? Do you need a 3d presentation of a product you want to place in your website? This one is perfect for you.

710tutorials-2013-3d-elements

Creating an Immersive Slider Experience with jQuery Immersive Slider

Sliders are still a thing in the world of web design. Be creative and try this tutorial out. Your eyes will pop at the fusion of simplicity and beauty at its finest.

710tutorials-2013-slider-background-blur

Creating a Parallax Scrolling Webpage Using Jarallax.js

Doing a videogame website design? Or a promotional website for a movie? Try this tutorial and let it help you using Parallax scrolling.

 70-tutorials-2013-parallax

Create a Smooth Jump To Sub-Navigation Menu in One JS Call

Navigation is a key element to any website. If you have the easiest to navigate design, more likely, you’ll get the money. Allow your users to still navigate even within posts using this tutorial.

70-tutorials-2013-jump-to

FancyScroll.js: Add an iOS/Android Overflow Scroll Effect

Are you a fan of Android or iOS scroll effects? Adapt them to your website and give it a feel of simplicity and professionalism.

70-tutorials-2013-overfow-scorll-effect

OnePageScroll.js: Creating an Apple’s iPhone 5S Website

One page scrolling has been a think this year. With the major companies like Apple following the trend, it becomes a bandwagon design as it epitomizes the functionality of all content within one page.

70-tutorials-2013-one-page-scroll

Notify Better.js: Creating a Dynamic Favicon and Title

Dynamic favicons are cool. Period. It gives a flare on your designs and lets you own every bit of it. Create dynamic favicons by following this tutorial and I bet you’ll have a very good time tinkering with this.

70-tutorials-2013-dynamic-notificaion

LoadingBar.js: Adding a YouTube-like Loading Bar to Your Website

When YouTube first adapted that loading bar, everybody’s eyebrows went high and wondered if they could re-do it and adapt the design into their website. Well, we can. Here’s the tutorial.

70-tutorials-2013-youtube-loading

Super Simple Text Rotator with Almost No Effort Required

Great for advertisements and short descriptions, this text-rotator will make your website more creative and adds flavor of movement and dynamism into it.

70-tutorials-2013-text-rotator

Create a Path-like Menu Button with WheelMenu.js

With the revamp of Path a few years back came a button like no other. It is very convenient and eye-pleasing. Learn how to create it through this tutorial.

70-tutorials-2013-path-like-menu

Creating the New Google Play’s Multi-Level Navigation from Scratch

Reeking with googliness is the next tutorial that borrowed the design of Google play. Learn how to create this eye-candy tutorial now.

70-tutorials-2013-multi-level-google-play-nav

Add Depth to Flat UI with Flat Shadow.js

Use long shadows using code? Yes, you can. I didn’t believe that too. Well, not before I have finished reading this tutorial. Read and see for yourself.

70-tutorials-2013-long-shadow

Creating a Swipeable Side Menu for the Web

Learn to recreate the behavior of side menus in smartphone applications in your website design. Both designs are suited for mobile and desktop versions. Learn how this tutorial works and become fun for both mobile and desktop users!

70-tutorials-2013-swipable-menu

Creating a 3D Interactive Gallery with CSS and jQuery

Imaginations are pushed further as this gallery is put into the limelight. Be amazed at how simple and yet awesome the output is. This is great for portfolio designs!

70-tutorials-2013-gallerry-using-jQuery

Making Draggable Webpage Elements Using jQuery

Draggable elements are very interactive. It lets your viewer see how your mind works by stretching your users’ imagination. Using this tutorial, you will learn how to put a little twist in your regular design.

70-tutorials-2013-draggable-elements

Make Pretty Charts For Your App with jQuery and xChart

Want to put something analytic in your website? How about trying charts using jQuery and xCharts? Be prepared for eyegasms as you will be pampered with good-looking data.

70-tutorials-2013-pretty-charts

Create a Beautiful Password Strength Meter

If you are planning to put sign-up forms, better spice it up using this tutorial. Add a beautiful password strength meter to ensure the safety of your users while adding a different look in your forms.

70-tutorials-2013-password-strenght-indicator

How to Build an iOS-Style Content Slider using jQuery

Create a cool slideshow using jQuery inspired by iOS. The good thing about this tutorial is that it allows you to drag the images to navigate them.

70-tutorials-2013-ios-style-slider

How to Create a CSS3 Login Form

Refurbish your log-in page through this very simple tutorial. Give your users a new look and make your website better.

70-tutorials-2013-css3-login-form

Create a Sticky Navigation Header Using jQuery Waypoints

This is another cool tutorial that involves the navbar. This time, it moves a few pixels down and becomes less opaque.

70-tutorials-2013-sticky-sidebar3

Quick Tip: Embedding New Google Maps

Maps are back on track as another tutorial is included in this list. Re-tweak your maps using this tutorial and revolutionize how people navigate in real life.

70-tutorials-2013-google-maps

Build a Dynamic Dashboard With ChartJS

This chart tutorial adds effect to the previous one as it provides with more samples and templates to choose from. Present your data using pie, line and hexagonal charts.

70-tutorials-2013-charts2

Draggable Image Boxes Grid

Do you have a client who wants a gallery type design? Well, this one might help you. Using grids and tiles as navigation tools, your client will surely love your work.

70-tutorials-2013-draggble-image-boxe

Medium-Style Page Transition

Showcase your posts in a totally new way using these transitions. With both simplicity and elegance, this tutorial will surely help you impress your clients, now and in the future.

70-tutorials-2013-medium-transitions

3D Shading with Box-Shadows

3D-looking boxes are not an abomination in the flat design trend. In fact, adding a 3D effect in your designs will accentuate the message you are trying to convey. Learn how it works on this tutorial.

70-tutorials-2013-box-shadows

Google Nexus Website Menu

Want to tinker more on menus? This Nexus-looking menu is great for a start. It is totally refreshing to see and amazingly easy to navigate. Try it.

70-tutorials-2013-google-nexus

Caption Hover Effects

Hovers have never been this awesome. I admit that I was pretty impressed with this tutorial. Clever as it may seem to be, reading this will make you realize that it’s not that difficult to do after all.

70-tutorials-2013-hover-effects

Responsive Retina-Ready Menu

Inspired from the colors of the Maliwan Borderlands Game, this menu is both retina-ready and colorful. Try to resize the page and see if the images become pixelated.

70-tutorials-2013-retina-ready

Thumbnail Grid With Expanding Preview

Another gallery tutorial that lets you enlarge the image for a better preview by clicking on the thumbnail.

70-tutorials-2013-Thumbnail-grid

How WordPress Plugins Work

Perhaps one of the best WordPress plugin tutorials there is. This one introduces you to the magical world of plugins and teaches you step-by-step to become a plugin developer.

70-tutorials-2013-wordpress-plugin

Killer Responsive Layouts With CSS Regions

Still in responsive layouts? Smashing Magazine gives an option using CSS Regions. Read this tutorial and become free from the limits posed by CSS elements.

Conclusion

And there you have it, guys. Seventy tutorials that highlights 2013. As 2014 comes right into our doorsteps, read these tutorials to refresh and to learn new things for the next year will surely be a bang! More tutorials to come, more design trends to be inspired from, more clients to ask for your help and hopefully, more money to fall right into your pockets.

December 17 2013

14:00

How to Create a Scrolling Parallax Website

Parallax websites are one of the latest and biggest trends on the web for some time now. If you want to impress your clients or implement immediate impact on your site, this is a good choice for you. In this tutorial, I’ll teach you how to create a simple but beautiful parallax website that will create an illusion of 3D depth to your site. It will surely catch your viewers’ attention. So let’s dig in.

Introduction to Parallax

According to Wikipedia, Parallax came from the Greek word παράλλαξις (parallaxis) meaning, “alteration”. It is the displacement or the difference in the apparent position of an object viewed along two different lines of sight. This Parallax scrolling effect became popular back in the days when Nike released its stunning and brilliant design website named “Nike Better World” as support to the athletes around the world. The website had achieved a lot of good reviews from different sites for its amazing Parallax scrolling effects and web design. Parallax has existed since 1980′s and was used in video game titles and later was used on websites. Today, it is one of the most widely-used designs on the modern Web.

Parallax Website Examples

To show you how this Parallax scrolling effect looks like I’ve collected sites that utilize the Parallax scrolling effect. Check out the following examples below.

brokentwill

bettybetty

express

unfold

The Design Concept

Since Christmas Season is just around the corner, we created a Christmas type mock-up design in Photoshop.

parallax

Resources You Will Need to Complete This Tutorial:

File Structure

For our file structure, we will create three folders and one HTML file:

  • index.html – this will serve as our main file. All of our designs will be utilized on this file.
  • js folder – for our JavaScript/jQuery
  • img folder – for our images
  • css folder – for our styling (CSS)

file-folder

The HTML Markup

On our HTML file, we will add first the HTML5 doctype and series of links on our head section. This will include our link to the CSS file and Google fonts links followed by our jQuery library file.

<!DOCTYPE html>
<html lang="en">
  <head>
	   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
       <title>Creating a Simple Parallax Scrolling Website</title>
       <link rel="stylesheet" href="css/style.css"/>
	   <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
       <link href='http://fonts.googleapis.com/css?family=Wellfleet' rel='stylesheet' type='text/css'>
	   <link href='http://fonts.googleapis.com/css?family=Wellfleet' rel='stylesheet' type='text/css'>
	   <link href='http://fonts.googleapis.com/css?family=Arvo:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
	   <link href='http://fonts.googleapis.com/css?family=Oswald' rel='stylesheet' type='text/css'>
	   <link href='http://fonts.googleapis.com/css?family=Goudy+Bookletter+1911' rel='stylesheet' type='text/css'>
	<script>

</head>

Let’s skip the jQuery code and discuss it later. For now, let’s continue with the HTML elements. We will add a header tag with the logo and navigation inside. The navigation will link to the specific slides. Later, we will add a smooth scrolling effect to make it look good.

<body>

<header id="header">
<div>
<div id="logo"><a href=""> PARALLAX </a></div>

<nav id="nav">
	<ul>
	<li><a href="#slide1" title="Next Section" >Slide 1</a></li>
	<li><a href="#slide2" title="Next Section">Slide 2</a></li>
	<li><a href="#slide3" title="Next Section">Slide 3</a></li>
	<li><a  href="#slide4" title="Next Section">Slide 4</a></li>
	<li><a href="#slide5" title="Next Section">Slide 5</a></li>
	</ul>
</nav>
</div>
</header>

Next let’s add the slides. We will put an ID to each slide which corresponds to its sequence. Inside of each slide, we will put a class of content to center the elements.

	<div id="slide1">
		<div>
			<div id="christmas_tree"> </div>
			<div id="snowflakes1"></div>
			<div id="snowflakes2"></div>
            <h2>MERRY</h2>
		    <h1>CHRISTMAS</h1>
		    <div id="divider"> </div>
		     <h3>HAPPY NEW YEAR </h3>
           <div id="ribbon"></div>
	       <div id="new_year"> </div>
		</div>
    </div>

	<div id="slide2">
		<div >
            <div>
		    <h3>“Then the Grinch thought of something he hadn't before! What if Christmas, he thought, doesn't come from a store. What if Christmas...perhaps...means a little bit more!”</h3>
		  <img src="img/dr-seuss.png" align="left"/> <h4>Dr. Seuss </h4>
		</div>
		</div>
    </div>

   <div id="slide3">
		<div>
			   <div>
		       <h3>“I truly believe that if we keep telling the Christmas story, singing the Christmas songs, and living the Christmas spirit, we can bring joy and happiness and peace to this world.”  </h3>
		      <img src="img/norman.png" align="left"/> <h4>Norman Vincent Peale </h4>
		    </div>
		</div>
    </div>

	<div id="slide4">
		<div>
              <div>
		    <h3>“Christmas doesn't come from a store, maybe Christmas perhaps means a little bit more....” </h3>
		  <img src="img/dr-seuss.png" align="left"/> <h4>Dr. Seuss </h4>
		</div>
		</div>
    </div>

	<div id="slide5">
		<div>
           <div>
		    <h3>“My idea of Christmas, whether old-fashioned or modern, is very simple: loving others. Come to think of it, why do we have to wait for Christmas to do that?” </h3>
		    <img src="img/bob.png" align="left"/> <h4>Bob Hope </h4>
		  </div>
		<div id="copyright"><a href="http://www.1stwebdesigner.com/">Copyright 1stwebdesigner.com </a></div>
		</div>

    </div>
</body>
</html>

Now that we already put all of the HTML elements, our site will look like this.

html

The CSS

We need to do our CSS general styling first. Let’s start by adding styles to our body tag as well as H1, H2, and H3 tags. We will set the font “Museo 700” as our fonts for our H1-H3 tags. We will put shadows on each of them.


body{
	margin: 0;
	padding: 0;
	width: 100%;
}

h1 {
    font-family:"Arvo";
	font-weight:normal;
	font-size: 55px;
	text-align: center;
	color: #fff;
	margin: 0;
	padding: 0;
}

h2 {
   font-family:"Arvo";
   font-weight: normal;
   font-size: 40px;
   text-align: center;
   color: #fff;
   margin: 0;
	padding: 0;
}

h3 {
   font-family: Oswald;
   font-weight: normal;
   font-size: 16px;
   text-align: center;
   margin: 5px 0;
	padding: 0;
	z-index: 1;
	position: relative;
}

Next let’s add the other general styling. This will include the positioning of the elements on screens as well as the container of each slides.


.center { margin: 0 auto; }
.content{ margin: 0 auto; width: 960px; }
.clear { clear: both; }

Now let’s go style the header section. We will add an image with a black background and add 60% opacity to the header, which will hold the logo and the navigation. For the logo, we will float it to the left and for the navigation we will float it to the right. 

#header {
	width: 100%;
	background: url('../img/header-bg.png');
	height: 80px;
	position: fixed;
	margin-top: 30px;
}

#nav { width: 410px; float: right; margin-top: 20px; }
#logo a { color: #fff; text-decoration: none; float: left; font-size: 30px; margin-top: 20px; color: #fff; font-family:"Wellfleet"; font-weight: bold; }
#nav ul{
	list-style: none;
	display: block;
	margin: 0 auto;
	list-style: none;
}

#nav li{
	margin-top: 9px;
	float: left;
	padding-left: 21px;
}

#nav li a { color: #fff; opacity:0.6; font-size: 16px; text-decoration: none; font-family: 'Wellfleet'; }
#nav li a.active { color: #fff; opacity:1; border-bottom: 2px solid #fff; }
#nav li a:hover { color: #fff; opacity:1;  }

Then, let’s add styles to our quotes, author and images as well as to the quotes container.

.quotes {
 font-family: 'Goudy Bookletter 1911', serif;
   font-weight: normal;
   font-size: 30px;
  text-align: left;
  margin: 50px auto;
}

 .author_name_white { font-family:"Wellfleet";  margin: 70px 0 0 75px; color: #fff; font-size: 20px; }
 .author_name_gray { font-family:"Wellfleet";  margin: 70px 0 0 75px; color: #94a4a4; font-size: 20px; }
 .quotes_container { width: 800px; margin: 0 auto; }
#christmas_tree { background: url('../img/christmas-tree.png')no-repeat; width: 48px; height: 77px; margin: 0 auto; position: relative; bottom: -35px;}
#divider { background: url('../img/divider.png')no-repeat; width: 300px; height: 35px; margin: 0px auto 27px auto; }
#ribbon { background: url('../img/ribbon.png')no-repeat; width: 251px; height: 48px; margin: 0 auto;  display: block; position: relative; top: -38px; }
#snowflakes1 { background: url('../img/snowflakes.png')no-repeat; width: 24px; height: 21px; margin: 0 auto;  display: block; position: relative; bottom: -54px; left: -102px; }
#snowflakes2 { background: url('../img/snowflakes.png')no-repeat; width: 24px; height: 21px; margin: 0 auto;  display: block; position: relative; bottom: -33px;  right: -100px; }

Each slide will have a background image or white background color with fixed position. We also need to add some padding to each slide to center the elements inside it.


#slide1, #slide2{ width: 100%; }
#slide1{
	background:url('../img/slide1.jpg') 50% 0 no-repeat fixed;
	color: #fff;
	height: 600px;
	margin: 0;
	padding: 200px 0 260px 0;
	background-size: cover;
}

#slide2{
	background-color: #fff;
	color: #333333;
	height: 300px;
	margin: 0 auto;
	overflow: hidden;
	padding: 200px 0;
}

#slide3{
	background: url(../img/slide3.jpg) 50% 0 no-repeat fixed;
	color: #fff;
	height: 600px;
	padding: 170px 0 0 0;
	background-size: cover;
}

#slide4{
	background-color: #fff;
	color: #333333;
	height: 300px;
	padding: 200px 0;
}

#slide5{
	background: url(../img/slide5.jpg) 50% 0 no-repeat fixed;
	height: 200px;
	margin: 0 auto;
	padding: 250px 0;
	color: #fff;
	background-size: cover;
}

Lastly, we will style our copyright text and center it to the screen.

#copyright  { color: #fff; font-family:"Wellfleet";  font-size: 14px; margin-top: 100px; text-align: center;  }
#copyright  a { text-decoration: none; color: #fff;  }

Now that we put all of our CSS styles, the look of our site will look like exactly the same with our PSD design.

The jQuery

Let’s put this Parallax site into life. On the head section, we are going to add the following jQuery codes. Check the Reference URL.

$(document).ready(function() {
$('a[href*=#]').each(function() {
if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'')
&& location.hostname == this.hostname
&& this.hash.replace(/#/,'') ) {
var $targetId = $(this.hash), $targetAnchor = $('[name=' + this.hash.slice(1) +']');
var $target = $targetId.length ? $targetId : $targetAnchor.length ? $targetAnchor : false;
if ($target) {
var targetOffset = $target.offset().top;
$(this).click(function() {
            $("#nav li a").removeClass("active");
            $(this).addClass('active');
$('html, body').animate({scrollTop: targetOffset}, 1000);
return false;
});
}
}
});

});

On this part of the jQuery code, we’re creating a click event handler to all the links that have “#” symbol anywhere in the href. The very next line, we will check if the link is pointing to the same page by checking for a match between location.pathname and this.pathname. We can then make sure that the link includes a qualified URL or just an identifier.

$(document).ready(function() {
  $('a[href*=#]').each(function() {
    if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'')
    && location.hostname == this.hostname
    && this.hash.replace(/#/,'') ) {
 var $targetId = $(this.hash), $targetAnchor = $('[name=' + this.hash.slice(1) +']');
 var $target = $targetId.length ? $targetId : $targetAnchor.length ? $targetAnchor : false;
       if ($target) {
 var targetOffset = $target.offset().top;

For this part, when the user click the menu link, it will remove the class active on that current active menu link and add it to that menu link that the user has clicked. This will also scroll to the target div id section.

$(this).click(function() {
            $("#nav li a").removeClass("active");
            $(this).addClass('active');
           $('html, body').animate({scrollTop: targetOffset}, 1000);
           return false;
         });
      }
    }
  });
});

Tips: You can also make the speed of the scrolling effect faster or slower by simply by changing the value of 1000. (1000 means 1000 milliseconds). You might also want to use the value “fast” (200 milliseconds) or “slow” (600 milliseconds). Take note that the default is 400 milliseconds. It depends on what you need or want.

$('html, body').animate({scrollTop: targetOffset}, 1000);

Final Words

Parallax scrolling effects are slowly being implemented on a lot of  sites across the Web. If you take the time to look for more inspiration around the Web, you will see a lot of advance Parallax sites that will blow your mind.

Today I’ve shown you how to do a relatively simple parallax scrolling website; you can play around with it and improve it. Hope you enjoyed this tutorial. Please feel free to leave links, comment or suggest if you learned in this tutorial. Until next time!

December 02 2013

15:00

10 Creative Loading Effects for Your Website

People hate waiting. It is a fact that web designers and developers should accept. It has been quite a maxim that as designers, we shouldn’t make our viewers wait. This is because many people tend to get bored and choose a website that loads faster. And that, my friend, is a big no-no.

However, waiting is seemingly inevitable. Despite modern internet connection speeds, some browsers still take time to load web elements. So it becomes a need to make people still want to visit your website even if it takes a little bit of time when they load it. There are a lot of plausible solutions for this, and I have to say, that our topic for this article is one of these.

Loading effects are very good tools to keep your viewer in viewing your website. It is not commonly used and that could be an edge. If you don’t want your viewers to wait, then give them something to look at (or do) while they’re waiting.

I know what you’re thinking, where the heck will I find these loading effects? And how can I include them in my website?

Well, that’s why we’re here. We collected 10 of the best website loading effects! Here you go, try these:

1. Creative Loading Effects

loading-effect-tutorials

This tutorial is a thing of beauty. You’ll love this one because it does not showcase a single effect; you can choose from a wide variety of effects. Try and click the demo and play with it.

2. Spinning Loading

loading-effect-tutorials-01

With bold red words written ‘Loading’ in it, this snippet is also a creative way of letting your viewers wait. It is clean-looking and relatively easy to use.

3. Planets

loading-effect-tutorials-02

It’s both peculiar and cool to see these planets dancing. This effect could be very good for websites which adapts a cutey-patootsy layout.

4. Sinister Loadscreen

loading-effect-tutorials-09

Yes, this loading effect seems sinister and dark. But it’s a good effect to intrigue your readers by shrouding your website with some sort of cryptic and evil.

5. Yet Another Loading Animation

loading-effect-tutorials-06

This loading animation is simple and yet full of movement. You’ll love the rhythm of its motion. Perfect for music-oriented websites!

6. CProgress

loading-effect-tutorials-03

Cprogress uses JQuery to magnificently show a very cool and easy to use loadscreen. Be awed as you tinker into these lines of code because they are fully customizable!

7. Spin.js

loading-effect-tutorials-04

Spin.js will make your world and imagination spin! Using sliders, you can easily customize how you want your loadscreen to be. Perfect for Mac and Windows 8 lovers.

8. Alessio Atzeni

loading-effect-tutorials-05

This guy’s cool tutorial is very easy to follow since it uses CSS3 and HTML only. It also comes in three different templates to choose from.

9. Facebook-ish Loading Animation

loading-effect-tutorials-07

If you want your website to look like Facebook, this loading effect will be a great help. It adapts the Facebook load effect (the one that shows up when you load photos).

10. Flickr Style

loading-effect-tutorials-08

Here’s one for the Flickr lovers! You’ll love to put this in your website because the effect looking simple and edgy.

Conclusion

With the ten effects you just saw, waiting could never be as bad as web designers think it to be. In fact, your viewers will find it otherwise. They will love your website because even at the moments where they have to wait, they could readily feel the creative juice you just gave in for your beloved website.

November 24 2013

14:00

10 Easy-to-Follow PSD to HTML/CSS Tutorials

Converting PSD to HTML/CSS might intimidate you at first, since it’s a brand new world, especially for long-time Photoshop users. A basic knowledge of HTML and CSS won’t hurt, but if you’re just starting to learn, then you might find the following tutorial a bit dizzying. And believe me when I say to you, finding a good teacher who will teach you for free (or for a small amount) is just another difficulty waiting next door.

These are ten easy to follow PSD (Photoshop file) to HTML (Hypertext Markup Language) and CSS (Cascading Style Sheet) tutorials. Learn the principles from these tutorials rather than the finished output themselves and I swear, you’ll learn web design in no time!

Here are they:

PSD to HTML Tutorial: Code a Photoshop File to a Working Website

psd-to-html-and-css-07

View Demo

How to Code a Stylish Portfolio Design in HTML/CSS

psd-to-html-and-css-06

View Demo

Designing a Highly-Professional Website, From the Sketch to the Code

psd-to-html-conversion-

Code an Awesome Minimal Design from PSD to XHTML & CSS

psd-to-html-and-css-04

Sleek Coming Soon Page Design

psd-to-html-and-css-03

View Demo

PSD/HTML Conversion: Code a Clean Business Website Design

psd-to-html-and-css-02

View Demo

Convert a Warm, Cheerful Web Design to HTML and CSS

psd-to-html-and-css-08

Converting a clean magazine-style PSD template to HTML/CSS

psd-to-html-and-css-09

View Demo

Simple & Cloudy Portfolio Layout

psd-to-html-and-css-10

View Demo

Convert Your Product Landing Page From PSD to HTML [Very Detailed]

psd-to-html-and-css-11

View Demo

Learning how to design a web page could be fun. It could also start as a hobby (to kill spare time) and eventually turn into a cool knowledge and in the end, a source of extra income. But it’s not that easy, it never is. You need to have persistence, a non-stop flare to know and learn, to tinker and make mistakes. For in the end, web design isn’t learned overnight. It requires sweat and a great amount of patience.

Good luck!

November 21 2013

16:01

An Introduction To Full-Stack JavaScript


  

Nowadays, with any Web app you build, you have dozens of architectural decisions to make. And you want to make the right ones: You want to use technologies that allow for rapid development, constant iteration, maximal efficiency, speed, robustness and more. You want to be lean and you want to be agile. You want to use technologies that will help you succeed in the short and long term. And those technologies are not always easy to pick out.

In my experience, full-stack JavaScript hits all the marks. You’ve probably seen it around; perhaps you’ve considered its usefulness and even debated it with friends. But have you tried it yourself? In this post, I’ll give you an overview of why full-stack JavaScript might be right for you and how it works its magic.

To give you a quick preview:

toptal-blog-500-opt
(Large view)

I’ll introduce these components piece by piece. But first, a short note on how we got to where we are today.

Why I Use JavaScript

I’ve been a Web developer since 1998. Back then, we used Perl for most of our server-side development; but even since then, we’ve had JavaScript on the client side. Web server technologies have changed immensely since then: We went through wave after wave of languages and technologies, such as PHP, ASP, JSP, .NET, Ruby, Python, just to name a few. Developers began to realize that using two different languages for the client and server environments complicates things.

In the early era of PHP and ASP, when template engines were just an idea, developers embedded application code in their HTML. Seeing embedded scripts like this was not uncommon:


<script>
    <?php
        if ($login == true){
    ?>
    alert("Welcome");
    <?php
        }
    ?>
</script>

Or, even worse:


<script>
    var users_deleted = [];
    <?php
        $arr_ids = array(1,2,3,4);
        foreach($arr_ids as $value){
    ?>
    users_deleted.push("<php>");
    <?php
        }
    ?>
</script>

For starters, there were the typical errors and confusing statements between languages, such as for and foreach. Furthermore, writing code like this on the server and on the client to handle the same data structure is uncomfortable even today (unless, of course, you have a development team with engineers dedicated to the front end and engineers for the back end — but even if they can share information, they wouldn’t be able to collaborate on each other’s code):


<?php
    $arr = array("apples", "bananas", "oranges", "strawberries"),
    $obj = array();
    $i = 10;
    foreach($arr as $fruit){
        $obj[$fruit] = $i;
        $i += 10;
    }
    echo json_encode(obj);
?>
<script>
    $.ajax({
        url:"/json.php",
        success: function(data){
            var x;
            for(x in data){
                alert("fruit:" + x + " points:" + data[x]);
            }
        }
    });
</script>

The initial attempts to unify under a single language were to create client components on the server and compile them to JavaScript. This didn’t work as expected, and most of those projects failed (for example, ASP MVC replacing ASP.NET Web forms, and GWT arguably being replaced in the near future by Polymer). But the idea was great, in essence: a single language on the client and the server, enabling us to reuse components and resources (and this is the keyword: resources).

The answer was simple: Put JavaScript on the server.

JavaScript was actually born server-side in Netscape Enterprise Server, but the language simply wasn’t ready at the time. After years of trial and error, Node.js finally emerged, which not only put JavaScript on the server, but also promoted the idea of non-blocking programming, bringing it from the world of nginx, thanks to the Node creator’s nginx background, and (wisely) keeping it simple, thanks to JavaScript’s event-loop nature.

(In a sentence, non-blocking programming aims to put time-consuming tasks off to the side, usually by specifying what should be done when these tasks are completed, and allowing the processor to handle other requests in the meantime.)

Node.js changed the way we handle I/O access forever. As Web developers, we were used to the following lines when accessing databases (I/O):


var resultset = db.query("SELECT * FROM 'table'");
drawTable(resultset);

This line essentially blocks your code, because your program stops running until your database driver has a resultset to return. In the meantime, your platform’s infrastructure provides the means for concurrency, usually using threads and forks.

With Node.js and non-blocking programming, we’re given more control over program flow. Now (even if you still have parallel execution hidden by your database (I/O) driver), you can define what the program should do in the meantime and what it will do when you receive the resultset:


db.query("SELECT * FROM 'table'", function(resultset){
   drawTable(resultset);
});
doSomeThingElse();

With this snippet, we’ve defined two program flows: The first handles our actions just after sending the database query, while the second handles our actions just after we receive our resultSet using a simple callback. This is an elegant and powerful way to manage concurrency. As they say, “Everything runs in parallel — except your code.” Thus, your code will be easy to write, read, understand and maintain, all without your losing control over program flow.

These ideas weren’t new at the time — so, why did they become so popular with Node.js? Simple: Non-blocking programming can be achieved in several ways. Perhaps the easiest is to use callbacks and an event loop. In most languages, that’s not an easy task: While callbacks are a common feature in some other languages, an event loop is not, and you’ll often find yourself grappling with external libraries (for example, Python with Tornado).

But in JavaScript, callbacks are built into the language, as is the event loop, and almost every programmer who has even dabbled in JavaScript is familiar with them (or at least has used them, even if they don’t quite understand what the event loop is). Suddenly, every startup on Earth could reuse developers (i.e. resources) on both the client and server side, solving the “Python Guru Needed” job posting problem.

So, now we have an incredibly fast platform (thanks to non-blocking programming), with a programming language that’s incredibly easy to use (thanks to JavaScript). But is it enough? Will it last? I’m sure JavaScript will have an important place in the future. Let me tell you why.

Functional Programming

JavaScript was the first programming language to bring the functional paradigm to the masses (of course, Lisp came first, but most programmers have never built a production-ready application using it). Lisp and Self, Javascript’s main influences, are full of innovative ideas that can free our minds to explore new techniques, patterns and paradigms. And they all carry over to JavaScript. Take a look at monads, Church numbers or even (for a more practical example) Underscore’s collections functions, which can save you lines and lines of code.

Dynamic Objects and Prototypal Inheritance

Object-oriented programming without classes (and without endless hierarchies of classes) allows for fast development — just create objects, add methods and use them. More importantly, it reduces refactoring time during maintenance tasks by enabling the programmer to modify instances of objects, instead of classes. This speed and flexibility pave the way for rapid development.

JavaScript Is the Internet

JavaScript was designed for the Internet. It’s been here since the beginning, and it’s not going away. All attempts to destroy it have failed; recall, for instance, the downfall of Java Applets, VBScript’s replacement by Microsoft’s TypeScript (which compiles to JavaScript), and Flash’s demise at the hands of the mobile market and HTML5. Replacing JavaScript without breaking millions of Web pages is impossible, so our goal going forward should be to improve it. And no one is better suited for the job than Technical Committee 39 of ECMA.

Sure, alternatives to JavaScript are born every day, like CoffeeScript, TypeScript and the millions of languages that compile to JavaScript. These alternatives might be useful for development stages (via source maps), but they will fail to supplant JavaScript in the long run for two reasons: Their communities will never be bigger, and their best features will be adopted by ECMAScript (i.e. JavaScript). JavaScript is not an assembly language: It’s a high-level programming language with source code that you can understand — so, you should understand it.

End-to-End JavaScript: Node.js And MongoDB

We’ve covered the reasons to use JavaScript. Next, we’ll look at JavaScript as a reason to use Node.js and MongoDB.

Node.js

Node.js is a platform for building fast and scalable network applications — that’s pretty much what the Node.js website says. But Node.js is more than that: It’s the hottest JavaScript runtime environment around right now, used by a ton of applications and libraries — even browser libraries are now running on Node.js. More importantly, this fast server-side execution allows developers to focus on more complex problems, such as Natural for natural language processing. Even if you don’t plan to write your main server application with Node.js, you can use tools built on top of Node.js to improve your development process; for example, Bower for front-end package management, Mocha for unit testing, Grunt for automated build tasks and even Brackets for full-text code editing.

So, if you’re going to write JavaScript applications for the server or the client, you should become familiar with Node.js, because you will need it daily. Some interesting alternatives exist, but none have even 10% of Node.js’ community.

MongoDB

MongoDB is a NoSQL document-based database that uses JavaScript as its query language (but is not written in JavaScript), thus completing our end-to-end JavaScript platform. But that’s not even the main reason to choose this database.

MongoDB is schema-less, enabling you to persist objects in a flexible way and, thus, adapt quickly to changes in requirements. Plus, it’s highly scalable and based on map-reduce, making it suitable for big data applications. MongoDB is so flexible that it can be used as a schema-less document database, a relational data store (although it lacks transactions, which can only be emulated) and even as a key-value store for caching responses, like Memcached and Redis.

Server Componentization With Express

Server-side componentization is never easy. But with Express (and Connect) came the idea of “middleware.” In my opinion, middleware is the best way to define components on the server. If you want to compare it to a known pattern, it’s pretty close to pipes and filters.

The basic idea is that your component is part of a pipeline. The pipeline processes a request (i.e. the input) and generates a response (i.e. the output), but your component isn’t responsible for the entire response. Instead, it modifies only what it needs to and then delegates to the next piece in the pipeline. When the last piece of the pipeline finishes processing, the response is sent back to the client.

We refer to these pieces of the pipeline as middleware. Clearly, we can create two kinds of middleware:

  • Intermediates
    An intermediate processes the request and the response but is not fully responsible for the response itself and so delegates to the next middleware.
  • Finals
    A final has full responsibility over the final response. It processes and modifies the request and the response but doesn’t need to delegate to the next middleware. In practice, delegating to the next middleware anyway will allow for architectural flexibility (i.e. for adding more middleware later), even if that middleware doesn’t exist (in which case, the response would go straight to the client).

user-manager-500-opt
(Large view)

As a concrete example, consider a “user manager” component on the server. In terms of middleware, we’d have both finals and intermediates. For our finals, we’d have such features as creating a user and listing users. But before we can perform those actions, we need our intermediates for authentication (because we don’t want unauthenticated requests coming in and creating users). Once we’ve created these authentication intermediates, we can just plug them in anywhere that we want to turn a previously unauthenticated feature into an authenticated feature.

Single-Page Applications

When working with full-stack JavaScript, you’ll often focus on creating single-page applications (SPAs). Most Web developers are tempted more than once to try their hand at SPAs. I’ve built several (mostly proprietary), and I believe that they are simply the future of Web applications. Have you ever compared an SPA to a regular Web app on a mobile connection? The difference in responsiveness is in the order of tens of seconds.

(Note: Others might disagree with me. Twitter, for example, rolled back its SPA approach. Meanwhile, large websites such as Zendesk are moving towards it. I’ve seen enough evidence of the benefits of SPAs to believe in them, but experiences vary.)

If SPAs are so great, why build your product in a legacy form? A common argument I hear is that people are worried about SEO. But if you handle things correctly, this shouldn’t be an issue: You can take different approaches, from using a headless browser (such as PhantomJS) to render the HTML when a Web crawler is detected to performing server-side rendering with the help of existing frameworks.

Client Side MV* With Backbone.js, Marionette And Twitter Bootstrap

Much has been said about MV* frameworks for SPAs. It’s a tough choice, but I’d say that the top three are Backbone.js, Ember and AngularJS.

All three are very well regarded. But which is best for you?

Unfortunately, I must admit that I have limited experience with AngularJS, so I’ll leave it out of the discussion. Now, Ember and Backbone.js represent two different ways of attacking the same problem.

Backbone.js is minimal and offers just enough for you to create a simple SPA. Ember, on the other hand, is a complete and professional framework for creating SPAs. It has more bells and whistles, but also a steeper learning curve. (You can read more about Ember.js here.)

Depending on the size of your application, the decision could be as easy as looking at the “features used” to “features available” ratio, which will give you a big hint.

Styling is a challenge as well, but again, we can count on frameworks to bail us out. For CSS, Twitter Bootstrap is a good choice because it offers a complete set of styles that are both ready to use out of the box and easy to customize.

Bootstrap was created in the LESS language, and it’s open source, so we can modify it if need be. It comes with a ton of UX controls that are well documented. Plus, a customization model enables you to create your own. It is definitely the right tool for the job.

Best Practices: Grunt, Mocha, Chai, RequireJS and CoverJS

Finally, we should define some best practices, as well as mention how to implement and maintain them. Typically, my solution centers on several tools, which themselves are based on Node.js.

Mocha and Chai

These tools enable you to improve your development process by applying test-driven development (TDD) or behavior-driven development (BDD), creating the infrastructure to organize your unit tests and a runner to automatically run them.

Plenty of unit test frameworks exist for JavaScript. Why use Mocha? The short answer is that it’s flexible and complete.

The long answer is that it has two important features (interfaces and reporters) and one significant absence (assertions). Allow me to explain:

  • Interfaces
    Maybe you’re used to TDD concepts of suites and unit tests, or perhaps you prefer BDD ideas of behavior specifications with describe and should. Mocha lets you use both approaches.
  • Reporters
    Running your test will generate reports of the results, and you can format these results using various reporters. For example, if you need to feed a continuous integration server, you’ll find a reporter to do just that.
  • Lack of an assertion library
    Far from being a problem, Mocha was designed to let you use the assertion library of your choice, giving you even more flexibility. You have plenty of options, and this is where Chai comes into play.

Chai is a flexible assertion library that lets you use any of the three major assertion styles:

  • assert
    This is the classic assertion style from old-school TDD. For example:
    
    assert.equal(variable, "value");
    
  • expect
    This chainable assertion style is most commonly used in BDD. For example:
    
    expect(variable).to.equal("value");
    
  • should
    This is also used in BDD, but I prefer expect because should often sounds repetitive (i.e. with the behavior specification of “it (should do something…)”). For example:
    
    variable.should.equal("value");
    

Chai combines perfectly with Mocha. Using just these two libraries, you can write your tests in TDD, BDD or any style imaginable.

Grunt

Grunt enables you to automate build tasks, anything including simple copying-and-pasting and concatenation of files, template precompilation, style language (i.e. SASS and LESS) compilation, unit testing (with Mocha), linting and code minification (for example, with UglifyJS or Closure Compiler). You can add your own automated task to Grunt or search the registry, where hundreds of plugins are available (once again, using a tool with a great community behind it pays off). Grunt can also monitor your files and trigger actions when any are modified.

RequireJS

RequireJS might sound like just another way to load modules with the AMD API, but I assure you that it is much more than that. With RequireJS, you can define dependencies and hierarchies on your modules and let the RequireJS library load them for you. It also provides an easy way to avoid global variable space pollution by defining all of your modules inside functions. This makes the modules reusable, unlike namespaced modules. Think about it: If you define a module like Demoapp.helloWordModule and you want to port it to Firstapp.helloWorldModule, then you would need to change every reference to the Demoapp namespace in order to make it portable.

RequireJS will also help you embrace the dependency injection pattern. Suppose you have a component that needs an instance of the main application object (a singleton). From using RequireJS, you realize that you shouldn’t use a global variable to store it, and you can’t have an instance as a RequireJS dependency. So, instead, you need to require this dependency in your module constructor. Let’s see an example.

In main.js:


  define(
      ["App","module"],
      function(App, Module){
          var app = new App();

          var module = new Module({
              app: app
          })

          return app;
      }
  );

In module.js:


  define([],
      function(){
          var module = function(options){
              this.app = options.app;
          };
          module.prototype.useApp = function(){
              this.app.performAction();
          };
          return module
      }
  );

Note that we cannot define the module with a dependency to main.js without creating a circular reference.

CoverJS

Code coverage is a metric for evaluating your tests. As the name implies, it tells you how much of your code is covered by your current test suite. CoverJS measures your tests’ code coverage by instrumenting statements (instead of lines of code, like JSCoverage) in your code and generating an instrumented version of the code. It can also generate reports to feed your continuous integration server.

Conclusion

Full-stack JavaScript isn’t the answer to every problem. But its community and technology will carry you a long way. With JavaScript, you can create scalable, maintainable applications, unified under a single language. There’s no doubt, it’s a force to be reckoned with.

(al, ea)


© Alejandro Hernandez for Smashing Magazine, 2013.

Tags: Coding

November 20 2013

17:14

Reinventing The Tech Conference Experience


  

If you had to name one thing that could have been better at the last conference or meetup you attended, what would it be? I bet you’d say that the speaker or content could have been better in some way. Think back to the last boring presentation you had to sit through. Maybe that investment of time could have paid off if the presenter knew you felt this way.

I created Onslyde to solve this problem. It’s a free service and open-source project that (hopefully) will make public speaking easier and conferences better.

The motivation for the project came from my own speaking engagements in the tech industry. I wanted to see how many people in the audience actually agreed or disagreed with what I was saying. I also wanted to leverage their experience and knowledge to create a better learning environment.

Presentations today are mostly unidirectional, with a single presenter giving information to the audience. But it doesn’t have to be that way. Now, with the ubiquity of mobile devices, everyone in the room can contribute to the conversation and make it better. Many books have been written on the topic of collective wisdom. In The Wisdom of Crowds, James Surowiecki states:

“… a diverse collection of independently deciding individuals is likely to make certain types of decisions and predictions better than individuals or even experts.”

For the past year, I have been putting this thesis to the test, enabling people to interact with and change the content that I deliver. It’s been a lot of fun and work, and now you get to see the result.

Ratings And Feedback

When we look at current systems of rating and feedback at conferences, most of them are reactive, meaning that participants rate the session after it’s over. This is why most people don’t even rate sessions, unless they are asked or forced to by the doorkeeper. Those who do rate sessions might not care to be accurate (giving all 5s or 4s and then hurrying to the coffee line). Other attendees might have enjoyed the majority of the talk, but then got upset by the last slide or by the way the speaker ended the talk.

If these people decided to rate the presentation, how many stars do you think they would give? Perhaps 3 or 4 stars because of their anger at the end, but who really knows? Without context, a low rating doesn’t tell the speaker which part of the talk an attendee didn’t like.

Real-time feedback gives context to a rating, making traditional feedback unnecessary. Conference organizers and speakers no longer have to rely solely on Twitter hash tags and reactive ratings to see how well things went. We can now visualize exactly how the audience felt at any millisecond of a presentation.

Real-Time Ratings With Onslyde

Giving a presentation that allows for real-time feedback is like riding a bicycle down a really steep hill for the first time. You have no idea whether you will crash and burn or come out with an adrenaline-filled scream at the other end. And it’s just as much fun for the audience.

With Onslyde, you get an accurate measure from the audience while you’re speaking. If you see that a lot of people are disagreeing with what you’re saying, you can adapt: Ask audience members for their thoughts, or maybe move on to another topic. When you’re presenting, actually seeing how the audience collectively feels in real time and then responding accordingly is a very cool experience.

The worst thing a presenter can do is tell the audience something it already knows or say something totally wrong. The audience wants to be intrigued and entertained by you. But too many speakers seem to think this requires GIFs of cats. Well, I don’t want to spoil anyone’s fun but I’m an adult now, and I don’t go to thousand-dollar conferences to look at funny pictures of cats. I do want to be able to challenge you, engage with the content and tell you when you’re wrong.

So, let’s talk about how the audience indicates whether you’re right or wrong. Onslyde’s remote control gives audience members between two and four ways to interact, depending on the type of session. It works for both normal presentations and panel discussions.

For presentations:

  • Any slide can be agreed or disagreed with.
  • Presenters can poll the audience by asking a question and giving two answers to choose from.
  • Remote devices are updated with content for each slide.

pres-remote-250-opt

For panel discussions:

  • Anyone (either panelist or audience member) can be agreed or disagreed with.
  • Votes are on a rolling average, meaning that they expire after 15 seconds (or a time specified by the moderator).
  • Any audience member can request to speak. This option will add them to a queue on the main screen.

panel-remote-250-opt

All remote devices receive real-time updates based on the slide or current speaker. In individual presentations, the speaker can specify content or images to send to all remotes per slide. For example, if a bulleted list is on the current slide, you could send it to the remotes by adding class="send" to the markup:


<section class="slide">
	<ul class="send">
	<li>Onslyde makes you a better presenter.</li>
	</ul>
</section>

We’ll go over the technical details in the next section. For now, note how easy it is to broadcast markup to everyone’s remotes.

Edge Conf, hosted at Google’s New York office in October 2013, was the first time ever when the audience participated in real time (via Onslyde) during an entire conference.

edge1-panel-detail-500-opt

What If There’s No Wi-Fi?

At this point, you might be thinking that this is a cool idea, but what about the 99% of conferences that have shaky or no Wi-Fi connectivity?

A presentation will work with or without an Internet connection; however, with no connection, it will just be like a normal presentation, and the audience would not be able to interact.

If you have a smartphone with a tetherable data plan, you can tether your laptop and enable Onslyde to connect from a mobile phone. It might sound crazy, but the lightweight messages that are sent across the wire still allow the presentation to run smoothly.

Even if Wi-Fi at a conference is unstable, the audience can always connect to the presentation with their mobile device’s data plan. I’ve personally given six or seven presentations with Onslyde and haven’t run into any issues so far.

Below you can see the network statistics from a talk I gave earlier this year. Even though the conference had stable Wi-Fi, attendees were connecting through their mobile data plan:

visits-by-provider-opt

Some have suggested bringing SMS into the picture, as a fallback. This would enable attendees to vote by sending text messages to the server. If you have other ideas, please leave them in the comments!

Building An Interactive Presentation

The Onslyde framework plugs into any HTML-based presentation system. For now, it supports presentations written with the following:

Currently, there is no nice WYSIWYG tool for building a slide deck. A tiny bit of HTML knowledge is required to build a presentation, but that’s it — no JavaScript or other programming. This is good because you don’t have to log into the system to edit your deck minutes before going on stage. You can change the markup at any time, and you will be ready to present after refreshing the browser.

The following use cases show how to add interactive elements to a presentation.

Audience Members Vote on (i.e. Agree or Disagree With) Any Slide

This comes out of box, no set-up required. Onslyde is built around “slide groups.” “Agree” and “Disagree” votes can only be given once in a slide group. This lets you focus on a particular topic with multiple slides and not be bombarded with votes on each screen.


<section class="slide-group">
	<section class="slide">
		<p>Onslyde makes you a better presenter.</p>
	</section>
</section>

buttons-opt
The buttons above will become active once the presenter moves to a new slide group.

Ask Unlimited Questions With Two Answers

To create an onscreen poll, use the following markup:


<section class="slide-group">

	<section class="slide" data-option="master">
		<h3 class="send">
		What is your favorite JavaScript framework?
		</h3>
	</section>

	<section class="slide" data-option="jquery">
		<div class="send">
		Time to talk about jQuery!
		</div>
	</section>

	<section class="slide" data-option="querySelector">
		<div class="send">
		Let’s go into more detail about core JavaScript!
		</div>
	</section>

</section>

By adding the markup above to your presentation, the bar chart in the following image will be automatically created and populated as votes are submitted:

favorite-javascript-framework-500-opt

The buttons at the top will illuminate on the remote controls, and attendees may start voting:

start-voting-opt

Send Content to All Devices at Any Time

Notice how the remote control’s image shows the title of the poll? As mentioned, this is because we added class="send" to the parent element of the heading.


<section class="slide">
	<h3 class="send">
	What is your favorite JavaScript framework?
	</h3>
…

start-voting-2-opt
The sent markup will appear below the button options.

You can also send something completely different than what is shown by the projector. Instead of sending the title of the poll, you could send the names of other JavaScript libraries for attendees to research:


<section class="slide">
	<h3>
	What is your favorite JavaScript framework?
	</h3>

	<div class="none">
		<ul class="send">
		<li>Zepto</li>
		<li>xui</li>
		<li>…</li>
		</ul>
	</div>

Raffle or Giveaway: Random Selection of Audience Member

To me, one of the funnest parts of this tool is giving away swag at the end of a talk. You can randomly select anyone who is connected by adding the following link to your slide deck:


<a href="javascript:onslyde.slides.roulette()">Pick a winner</a>

winner-opt

Just place the link on the final slide and click it at the end of your talk. Everyone’s mobile device will flash red; then, the winner’s will turn yellow. Continue clicking to pick more people at random.

Best Practices

The easiest way to get started with each feature is to focus on how you want the audience to interact with you and your content. Here are some good rules of thumb that I have learned using this tool:

  • Start each presentation with a poll on the first or second slide. This allows the audience to get their device ready, get connected and start giving their opinion. It’s fun to pose a controversial question about your presentation topic and watch the votes pour in.
  • Polls can solicit a “Yes” or “No” feeling from the audience or go deeper. If you ask a question like, “Which JavaScript framework do you use?” and then give options for “AngularJS” or “Backbone”, you will be able to fork the slides. This is why preparing the slides beforehand is more rewarding. Onslyde will detect the majority vote, and when you go to the next slide, the losing slides will be removed from the presentation (temporarily) and only the slides from the winning option will be shown.

Getting Started

Presentations can be created at onslyde.com. And the open-source back end that manages a presentation’s feedback is hosted on GitHub. Anyone can run the server and host their own instance of the entire Onslyde stack if they wish.

All of the features mentioned are integrated in each demo to show use cases. To begin your first presentation, go to the “Getting Started” section, where you will have the option to run a demo presentation or sign in and create a private one.

onslyde.getting-started-500-opt
(Large view)

After you’ve signed in, the “Getting Started” page will give you options for setting up a poll. After hitting the “Preview” button, you must save the HTML to your computer in order to edit and present the slides.

setting-up-500-opt
(Large view)

An Onslyde presentation can be run anywhere. Essentially, it’s the same as saving a Keynote or PowerPoint presentation on your computer. Just click on the HTML file and run it from a Web browser — no need to install anything or run it from a server.

Regarding Keynote and PowerPoint

For those of you who use non-HTML slide decks, such as Keynote and PowerPoint, there are plans to create a transparent overlay on top of Windows and Mac OS X. This will allow presenters to use any presentation tool and to collect feedback in real time.

Understanding Audience Feedback

After your talk is over, you can view the analytics for your session. The votes for each slide are recorded so that you can track your performance at a fine-grained level.

chart1-500-opt

For questions that the audience voted on, each option is recorded, along with the “Agree” and “Disagree” votes for each poll.

chart2-500-opt

The other important pieces of data come from Google Analytics, including engagement times and what kinds of devices and mobile networks were used.

analytics-500-opt
Google Analytics showed that real-time feedback was provided not just from the people in the room. (Large view)

From the above image, we can see that 281 iOS devices and approximately 113 Android devices were used at Edge Conf. Because the event was streamed live, over twice as many unique devices appeared as there were people in the room — people were participating and providing real-time feedback as they watched the live stream from their home or office around the world!

One other cool piece to this analytics puzzle is mapping voting times to the video. Edge Conf was streamed live, and the videos were subsequently added to YouTube. After finding a reference point in the video and calculating the starting time, we were able to map the charts to the videos. If you clicked on any data point from a session, the video would skip to that voting period. Try it out for yourself.

voting-period-500-opt
We were able to map the voting times to the conference videos. (Large view)

Conclusion

As we’ve seen, so much more valuable data can be pulled from the conferences we attend. Not only can we get an accurate snapshot of how speakers performed and how audience members felt, but we can change the learning experience for everyone.

This project is a use case for the mobile Web and a playground for the new technologies that are available to modern Web browsers, such as WebSockets and WebRTC. Many other companies and conferences provide similar services using native apps and proprietary hardware, but the convenience of opening a Web browser and being exposed to a new level of education and interaction is immeasurable.

Many thanks to Andrew Betts for helping me with this article and providing great feedback. He is also responsible for most of the features in the Onslyde panel and remote interfaces.

(al, ea)


© Wesley Hales for Smashing Magazine, 2013.

Tags: Coding

November 18 2013

13:20

The Future Of Video In Web Design


  

Federico was the only other kid on the block with a dedicated ISDN line, so I gave him a call. It had taken six hours of interminable waiting (peppered with frantic bouts of cursing), but I had just watched 60 choppy seconds of the original Macintosh TV commercial in Firefox, and I had to tell someone. It blew my mind.

Video on the Web has improved quite a bit since that first jittery low-res commercial I watched on my Quadra 605 back in 7th grade. But for the most part, videos are still separate from the Web, cordoned off by iframes and Flash and bottled up in little windows in the center of the page. They’re a missed opportunity for Web designers everywhere.

But how do you integrate video into an app or a marketing page? What would it look like, and how do you implement it? In this article, you will find inspiration, how-tos and a few technical goodies to get you started with modern video on the Web.

When Video Leaves Its Cage

Video combined with animation is a powerful tool for innovative and compelling user experiences. Imagine interactive screencasts and tutorials in which DOM elements flow and move around the page in sync with the instructor. Why not combine video with animation to walk new users through your app? Or what about including videos of your product on your marketing page, instead of static JPEGs? Getting carried away is easy — video can become little more than sophisticated blink tags if you’re not careful. But there are plenty of beautiful, inspiring examples of video tightly integrated in a design.

Apple’s new marketing page for the Mac Pro is a stunning example of video reaching out from its cage into the surrounding content. The new Mac Pro is featured in the center of the page, and as you scroll, it swoops and spins and disassembles itself. Supporting copy fades in to describe what you are seeing.


A static screenshot of the new landing page doesn’t do the new Mac Pro justice. (larger view)

Another great example of interactive video is Adrian Holovaty’s Soundslice. Soundslice is filled with YouTube videos of music sliced and diced into tablature (or tabs), which is notation that guitar players use to learn music.


The musical bars at the bottom stay in sync with the video. (larger view)

When you watch a music video, the tabs are animated at the bottom in time with the music, so that you can play along with your guitar. You can even slow down the video or loop selections to practice difficult sections, and the tab animation will stay in sync.

How Do You Add Video To A Design?

If you venture into video and animation in your next project, you won’t have many resources to lean on for implementation. No canonical, easy-to-use, open-source library for syncing video with animation exists, so every implementation is a bit different. Should you use a JavaScript animation library or pure CSS keyframes and transitions? Should you host the videos yourself and take advantage of HTML5’s video tag events or use YouTube or Vimeo? And then how exactly do you tie animations to a video?

Together, we will explore answers to the above-mentioned questions and more as we build our own micro JavaScript framework. Charlie.js provides an easy-to-use API for building pages with synchronized video and CSS3 animation.


Charlie.js, named in honor of Charlie Chaplin. (Image source)

The best way to learn is by doing, so let’s dive in.

What Does Charlie.js Do?

We need a way to create animations and then trigger them at certain moments in a video. We also need to pause the animations if the video stops, and we’ll need a way to handle the user jumping around to different times in the video.

To limit the scope of this article, we’ll have Charlie.js use only CSS animations. JavaScript animation libraries are more flexible and powerful than CSS animations, but wrapping one’s head around the straightforward, declarative syntax of keyframes is pretty easy, and the effects are hardware-accelerated. Sticking with only CSS animations is a pretty good choice for small projects.

To keep it simple, Charlie.js will support only one video per page.

As we go through the exercise of building this library, remember that we’re using the framework just to learn about CSS animation and video on the Web. The goal is to learn, not to create production-quality code.

Define The API

For our little framework, defining the API first makes sense. In other words, we need to figure out how someone would use the library and then write the JavaScript to implement the API.

A video and animation library could work in many ways, but the main interface puzzle is to figure out how to couple the animation to the video. How should a developer specify which animations should appear on which elements and at which times they should start in the video?

One option is to suck down the data in JSON or XML. The opposite solution is to have no separate data files and to put all of the configuration into pure JavaScript function calls. Both are fine, but there is a middle road.

Normally, CSS animation is defined in a style sheet. Ideally, that’s where it should be defined for Charlie.js, not in a JSON file. It just makes sense, and doing it this way has advantages. When the animation is in a style sheet, rather than a JavaScript or JSON file, you can test it without loading the entire video and animation library.

The animations are coupled to an element with data attributes. The data attributes define the animation names and also specify the start times.

Let’s say you have a CSS animation named fade for dialing down the opacity, and another named fling for moving elements off the page. And you want a div on the page to use both animations three seconds into the video. Your markup would look like this:


<div class="charlie" data-animations="fade, fling" data-times="3, 3">
...
</div>

Charlie.js will see this and know to run the fade and fling animations once the video hits three seconds.

The fade and fling animations are defined in a style sheet that is linked to the document.

Here is what the fade animation might look like (browser prefixes are excluded here but are required for Chrome and Safari):


.fade {
	animation-name: fade;
	animation-duration: 3s;
	animation-timing-function: linear;
	animation-iteration-count: 1;
	animation-direction: normal;
	animation-fill-mode: forwards;
}

@keyframes fade {
	0% {
		opacity: 1;
	}

	100% {
		opacity: 0;
	}
}

The .fade class is what Charlie.js applies to the animated element, which will trigger the fade animation.

Host The Videos: HTML5 Vs. Flash And Silverlight

With the API out of the way, the next decision is how to host the video. The host will determine what kind of container the video is stuffed into, and the container will determine what is possible with the video.

Video embedded with Flash or Silverlight will limit your design options, so the video-hosting service should ideally support HTML5’s video tag. The video tag is easier to style and move around on the page. You can apply CSS filters and transforms and even use CSS animation on the video itself. Plus, the standard media events are robust and provide plenty of places and ways to hook your code into the video. The big downside of the video tag is compatibility. It doesn’t work in Internet Explorer 8.

What kinds of video-hosting should Charlie.js support? Building a library that supports multiple hosting options is feasible. For example, Popcorn.js (an awesome library for syncing content with video) supports several hosting options and APIs. But to keep it simple, our little library will support only a vanilla video tag. Anything in an iframe or Flash container won’t be supported.

That’s nice for Charlie.js, but what if you are stuck supporting old browsers and have to deal with a video stuffed in an iframe? Most video-hosting companies have decent APIs. At the very least, you should be able to use those APIs to sync up your animation — you’ll just be stuck working with an embedded Flash object. YouTube and Vimeo are the most popular services, and both offer extensive APIs. Wistia is another great option but less well known.

If you want to use a pure video tag but don’t want to host the video yourself, take a look at Vid.ly. Once you upload your video, Vid.ly will encode it in every format you need and give you a universal URL that you can use in your video tag, which will automatically choose the correct video type according to the user agent.


<video id="video" src="http://vid.ly/4m4e2n?content=video" controls="" preload="none">
Your browser does not support the HTML5 video element.
</video>

Heads Up

The JavaScript in most of these snippets uses Underscore; stuff like _.forEach and _.toArray are utility functions from that library. Underscore encourages a functional style of programming that might look strange if you’ve never seen it before, but a little time invested in learning Underscore can save you a lot of time and lines of code. It’s worth checking out. For this article, you’ll find comments in the code to tell you what’s going on, and it should be pretty easy to understand.

One other caveat: The code here will run in most modern browsers, but no attempt has been made to make this completely cross-browser compatible. If your business really needs CSS animation to be synced with video and to run in almost every browser, then this library will not help you out. But for my business, and perhaps for yours, supporting only modern browsers is fine. And even with this restriction, plenty of material here is still worth learning.

Control CSS Animations With JavaScript

JavaScript is the glue between video and CSS animation. There is no way to couple an animation to a video purely with CSS. Animation doesn’t start until a style is applied, and CSS gives you only so many ways to trigger extra styles (such as :hover). In order to sync animation to video, we will need to pause, stop, resume, skip to the middle, and even reverse running animations.

All of this is possible with JavaScript. So, the first step is to get the CSS animation out of the style sheet and into JavaScript. Every CSS animation has two parts. The first part is the keyframe and the properties used to configure how the animation behaves, such as duration, iteration and direction. The second part is what triggers the animation. Charlie.js will need to find both parts in the style sheets.

The first thing we need is a utility function to search through style sheets that are loaded on the page.


findRules = function(matches){

		//document.stylesheets is not an array by default.
		// It's a StyleSheetList. toArray converts it to an actual array.
		var styleSheets = _.toArray(document.styleSheets),
		rules = [];

		// forEach iterates through a list, in this case passing
		//every sheet in styleSheets to the next forEach
		_.forEach(styleSheets, function(sheet){

		//This foreach iterates through each rule in the style sheet
		//and checks if it passes the matches function.
		_.forEach(_.toArray(sheet.cssRules), function(rule){
			if (matches(rule)){
				rules.push(rule);
			}
		});
	});
return rules;
}

The findRules function iterates through every rule of every style sheet and returns a list of rules that match the passed-in matches function. To get all of the keyframe rules, we pass in a function to findRules that checks whether the rule is a keyframe:


// A little code to handle prefixed properties
	var KEYFRAMES_RULE = window.CSSRule.KEYFRAMES_RULE
		|| window.CSSRule.WEBKIT_KEYFRAMES_RULE
		|| window.CSSRule.MOZ_KEYFRAMES_RULE
		|| window.CSSRule.O_KEYFRAMES_RULE
		|| window.CSSRule.MS_KEYFRAMES_RULE,

		...

		var keyframeRules = findRules(function(rule){
			return KEYFRAMES_RULE === rule.type;
		}),

		...

At this point, we have the keyframes in JavaScript, but we still need the rest of the animation styles that define duration, iterations, direction and so on.

To find all of these classes, we again use the findRules function to go through every rule in every style sheet. This time, though, the matches function that we’ll pass in will check to see whether the rule has an animationName property.


	...

	var animationStyleRules = findRules(function(rule){
		return rule.style && rule.style[animationName(rule.style)];
	});

	...

The animationsName function is there to handle the prefixes, because the animationName property still requires prefixes in some browsers. That function looks like this:


...

if (style.animationName) {
	name = "animationName"; }
else if (style.webkitAnimationName) {
	name = "webkitAnimationName"; }
else if (style.mozAnimationName) {
	name = "mozAnimationName"; }
else if (style.oAnimationName) {
	name="oAnimationName"; }
else if (style.msAnimationName) {
	name = "msAnimationName"; }
else {
	name = "";
}
return name;

...

Once the correct prefix has been determined, the name is cached and used for future look-ups.

Once the keyframes and animation styles have been collected, they get stuffed into an instance of a helper class and stored for Charlie.js to use later.


var CSSAnimations = function(keyframes, cssRules){
	this.keyframes = keyframes;
	this.cssRules = cssRules;
};

Get The Timing Information From The Data Attributes

Timing information is attached to the element that will be animated using a data attribute (remember that we decided this when we were defining the API). So, we need to crawl the document and pull out the information. Any element that will be animated is marked with the class of charlie, which makes it pretty easy to find the data attributes we are looking for.


var scrapeAnimationData = function() {

	/* Grab the data from the DOM. */
	var data = {};
	_.forEach(
		//loop through every element that should be animated
		document.getElementsByClassName("charlie"),

		//for each element, pull off the info from the dataset
		function(element) {

			/*
			* Creates an object of animation name: time, e.g.
			*
			* { swoopy: [
			*    { element: domElement,
			*  time: 6522 },
			*    { element: anotherElement,
			*  time: 7834 }]
			* }
			*/

			//     var names = element.dataset.animations.split(/\s*,\s*/),
			times = element.dataset.times.split(/\s*,\s*/),

			// creates an array of arrays, each one called a "tuple"
			// basically ties the time to the
			// animation name, so it looks like this:
			//[["zippy", 1], ["fade", 2] ... ]
			tuples = _.zip(names, times);

			/*
			* turn the tuples into an object,
			* which is a little easier to work with.
			* We end up with an object that looks like this:
			* {
			*  fade: [ {element: domElement, time: "1.2s"}, ... ],
			*  fling: [ {element: domelement, time: "2.4s"}, ... ]
			* }
			* So, we can reuse an animation on different elements
			* at different times.
			*/

			_.forEach(tuples, function(tuple){
				var name = tuple[0],
				time = tuple[1];
				data[name] = data[name] || [];
				data[name].push({
					element: element,
					time: time
				})
			});
		});
	return data;
},

This stores all of the timing information in an object with the animation’s name as the key, followed by a list of times and elements. This object is used to create several Animation objects, which are then stuffed into various data structures to make it easy and fast to look up which animations should be running in the big loop.

The requestAnimationFrame Loop

The heart of Charlie.js is a loop that runs whenever the video runs. The loop is created with requestAnimationFrame.


tick: function(time){
	if (this.running){
		this.frameID = requestAnimationFrame(this.tick.bind(this));
		this.controller.startAnimations(time, video.currentTime);
	}
}

The requestAnimationFrame function is specifically designed to optimize any kind of animation, such as DOM manipulations, painting to the canvas, and WebGL. It’s a tighter loop than anything you can get with setTimeout, and it’s calibrated to bundle animation steps into a single reflow, thus performing better. It’s also better for battery usage and will completely stop running when the user switches tabs.

The loop starts when the video starts and stops when the video stops. Charlie.js also needs to know whether the video ends or jumps to the middle somewhere. Each of those events requires a slightly different response.


video.addEventListener("play", this.start.bind(this), false);
video.addEventListener("ended", this.ended.bind(this), false);
video.addEventListener("pause", this.stop.bind(this), false);
video.addEventListener("seeked", this.seeked.bind(this), false);

As the video plays, the loop keeps ticking. Each tick runs this code:


// allow precision to one tenth of a second
var seconds = roundTime(videoTime),
me = this;

//resume any paused animations
me.resumeAnimations();

/* start up any animations that should be running at this second.
* Don't start any that are already running
*/

if (me.bySeconds[seconds]){
	var animations = me.bySeconds[seconds],
	notRunning = _.filter(animations, function(animation){
		return !_.contains(me.running, animation);
	});

	/* requestAnimationFrame happens more than
	*  every tenth of a second, so this code will run
	*  multiple times for each animation starting time
	*/

	_.forEach(notRunning, function(animation){
		animation.start();
		me.running.push(animation);
	});
}

Everything we have done up to this point has been to support these few lines of code. The seconds variable is just the video.currentTime value rounded to the nearest tenth of a second. The bySeconds property is created from the time data that is scraped from the HTML — it’s just a quick way to grab a list of animations to start at a given time. The running array is a list of animations that are currently running. The requestAnimationFrame loop is really fast and runs many, many times a second, and Charlie.js only supports a resolution of one tenth of a second.

So, if one animation starts at the 2-second mark, then requestAnimationFrame will try to start it several times until the video has progressed to the next tenth of a second. To prevent animations from starting over and over again during that tenth of a second, they get put into the running array so that we know what is running and don’t start it again unnecessarily.

To start a CSS animation, just add the animation properties to an element’s style. The easiest way to do this is to just add the animation class to the element’s classList, and that is exactly what the animation’s start method does.


start: function(){
	var me = this;
	//The name of the animation is the same as the class name by convention.
	me.element.classList.add(me.name);
	onAnimationEnd(me.element, function(){
		me.reset();
	});
}

The name of the animation is the same as the class name by convention.

Pause And Resume Animations

When the video stops, the animations should stop with it. There is a pretty straightforward way to do this using CSS animations: We just set the animationPlayState property of the element to paused.


...

//method on the animation object
pause: function(){
	this.element.style.webkitAnimationPlayState = "paused";
	this.element.style.mozAnimationPlayState = "paused";
	this.element.style.oAnimationPlayState = "paused";
	this.element.style.animationPlayState = "paused";
},

resume: function(){
	this.element.style.webkitAnimationPlayState = "running";
	this.element.style.mozAnimationPlayState = "running";
	this.element.style.oAnimationPlayState = "running";
	this.element.style.animationPlayState = "running";
}

...

//called on the video "pause" event
while(animation = me.running.pop()){
	animation.pause();
	//keep track of paused animations so we can resume them later ...
	me.paused.push(animation);
}

The only trick here is to keep track of which animations have been paused, so that they can resume once the video starts again, like so:


while (animation = me.paused.pop()){
	animation.resume();
	me.running.push(animation);
}

How To Start An Animation In The Middle

What if someone skips ahead in the video and jumps right into the middle of an animation? How do you start a CSS animation in the middle? The animationDelay property is exactly what we need. Normally, animationDelay is set to a positive number. If you want an animation to start three seconds after the animation style has been applied, then you’d set animationDelay to 3s. But if you set animationDelay to a negative number, then it will jump to the middle of the animation. For example, if an animation lasts three seconds, and you want the animation to start two seconds in, then set the animationDelay property to -2s.

Whenever a user scrubs to the middle of the video, Charlie.js will need to stop all of the animations that are currently running, figure out what should be running, and then set the appropriate animationDelay values. Here is the high-level code:


// 1. go through each to start
// 2. set the animation delay so that it starts at the right spot
// 3. start 'em up.

var me = this,
seconds = roundTime(videoTime),
toStart = animationsToStart(me, seconds);

// go through each animation to start
_.forEach(toStart, function(animation){

	//set the delay to start the animation at the right place
	setDelay(animation, seconds);

	//start it up
	animation.start();

	/* If the move is playing right now, then let the animation
	* keep playing. Otherwise, pause the animation until
	* the video resumes.
	*/

	if (playNow) {
	me.running.push(animation);

	} else {
		me.paused.push(animation);
		animation.pause();
	}
});

The animationsToStart function loops through a sorted list of animations and looks for anything that should be running. If the end time is greater than the current time and the start time is less than the current time, then the animation should be started.


var animationsToStart = function(me, seconds) {

	var toStart = [];

	for(var i = 0; i < me.timeModel.length; i++) {

		var animation = me.timeModel[i];

		//stop looking, nothing else is running
		if (animation.startsAt > seconds) {
			break;
		}

		if (animation.endsAt > seconds) {
			toStart.push(animation);
		}
	}
	return toStart;
};

The timeModel is a list of animations sorted by the times when the animations should end. This code loops through that list and looks for animations that start before the current time and end after the current time. The toStart array represents all of the animations that should be running right now.

Those values get passed up to the higher-level code, which then computes and sets the delay in the setDelay function.


setDelay = function(animation, seconds) {
	var delay = -(seconds - animation.startsAt);
	delay = delay < 0 ? delay : 0,
	milliseconds = Math.floor(delay * 1000) + "ms";
	animation.element.style.webkitAnimationDelay = milliseconds;
	animation.element.style.mozAnimationDelay = milliseconds;
	animation.element.style.oAnimationDelay = milliseconds;
	animation.element.style.msAnimationDelay = milliseconds;
	animation.element.style.animationDelay = milliseconds;
};

The seconds parameter is the current time in the video. Let’s say that the video is at 30 seconds, that the animation starts at 24 seconds and that it lasts for 10 seconds. If we set the delay to -6s, then it will start the animation 6 seconds in and will last another 4 seconds.

Look At The Code For Yourself

We’ve covered here how to use requestAnimationFrame to create a tight, optimized loop for animations, how to scrape keyframes and animation styles from the style sheet, how to start and stop animations with the video, and even how to start CSS animations in the middle. But to get to the point, we’ve skipped over quite a bit of glue code. Charlie.js is only a couple of hundred lines of code, and it is open source and commented thoroughly. You are welcome to grab the code and read it.

You can even use it if you want, with a few caveats:

  • Charlie.js was made for educational purposes. It was made to be read and for you to learn about CSS animations, videos, requestAnimationFrame, etc. Don’t just plug it into your production code unless you really know what you are doing.
  • Cross-browser support for animation is pretty good, and Charlie.js tries to be friendly to all the browsers when it can be. However, it hasn’t been heavily tested.
  • It eats up CPU, even if the video is paused. (This has something to do with CSS animations still rendering.)
  • The user can’t drag the seek bar while the video is unpaused. If they do, then the animations will start and overlap each other.
  • Charlie.js does not respond to changes in frame rate. So, if the video stutters or you want to slow down the rate of the video, then the animations will fall out of sync. Also, you can’t run video backwards.
  • Animations won’t start if the current tab isn’t set to the video, due to requestAnimationFrame not running unless the video tab is active. This could confuse users who switch back and forth between tabs.

Some of these limitations can be fixed pretty easily, but Charlie.js was made for a very limited use case. I’ve put together a demo of Charlie.js in action so that you can see what it can do.

The future of video in Web design is filled with possibilities, and I for one can’t wait to see what happens.

Additional Resources

(al, ea, il)


© Sean Fioritto for Smashing Magazine, 2013.

Tags: Coding

November 13 2013

14:32

Laying The Groundwork For Extensibility


  

The Web has succeeded at interoperability and scale in a way that no other technology has before or since. Still, the Web remains far from “state of the art”, and it is being increasingly threatened by walled gardens. The Web platform often lags competitors in delivering new system and device capabilities to developers. Worse, it often hobbles new capabilities behind either high- or low-level APIs, forcing painful choices (and workarounds) on developers.

Despite browser versions being released much faster, new capabilities still take a long time to materialize, and often do so in forms that are at best frustrating and at worst nearly useless to large swathes of the developer community for solving real-world needs.

The best recent improvements to the platform have been the result of collaborative discussions between developers and browser vendors. Sometimes these lead to big new features. More often than not, they lead to small changes that make existing systems suitable for a wider range of uses. In the absence of an intellectual framework for making these changes, we get a hodgepodge approach to design, where good ideas are not carried through and discredited patterns live on far longer than they should.

Building on the successes of tight collaboration between Web developers and browser-makers, folks who have iterated on proposals and straddled both sides of the line (including this article’s authors, Yehuda Katz, Dimitri Glazkov, Erik Arvidson, Dave Herman and others) have taken a longer look at what gives Web features longevity and utility.

image-4
The result of collaborative discussions between developers and browser vendors lead to small changes that make existing systems suitable for a wider range of uses. (Image source)

Over a decade of JavaScript library work, the progressive-enhancement revolution, the advent of polyfills, and the effort to birth the “Web Components” and “Shadow DOM” specifications have taught us surprising lessons: In every period, being able to use features in both high- and low-level forms has always been desirable.

HTML is great, until it isn’t. And JavaScript-only has predictable (and thankfully, now acknowledged) drawbacks.

Thinking that there is a “right way” to build new Web features is seductive. Just define The Way To Do It™ and make all standard-bearers comply, right? Turns out, it’s not that simple. New proposals are organic and stem from needs, not from pure speculation. Low-level needs demand low-level solutions. HTML elements and CSS rules aren’t natural fits for all work. And the existence of JavaScript creates a need for new APIs near the language level.

The process of introducing new features is usually an either-or proposition (i.e. either declarative features or low-level APIs) in the short run. But in the long run, nearly all features need expression in both domains. Moreover, we have to realize that proposals for new standards are hard work. The people doing that hard work are generally trying to do the right thing and can’t wait forever to ship features. A pragmatic, realistic approach to increasing the power and quality of Web APIs is needed, one that doesn’t presuppose infinite time, effort or understanding on the part of participants — just goodwill and a willingness to build bridges.

To support this goal, the standards process needs an intervention.

The Extensible Web Manifesto is a document that we have drafted to build consensus among standards participants around a few core ideas:

  • High-level APIs and markup should provide direct extension points via JavaScript.
  • Where the platform already provides high-level systems, related low-level additions should be used to explain how the high-level bits would have been written in terms of these new lower-level APIs.
  • When adding new raw power to the platform, prefer lower-level to higher-level APIs because they enable experimentation and iteration ahead of broad adoption.

The core insight is that the Web has come this far on the back of largely declarative, largely high-level features: HTML elements for forms, CSS for layout and styling, and <a> for defining relationships between documents. While each of these adds APIs, little effort has been made so far to explain how they do their work and how they relate to each other.

While you can almost sense the many strata of APIs below Web features, they go unnamed, unexplained, unconnected and unavailable to you when the system doesn’t do exactly what you need.

image-2
It’s vital to know how APIs work and how they’re connected to each other. (Image source)

For example:

  • The HTML5 canvas element defines a programmatic 2-D bitmap API, while the long-standing img element is, not coincidentally, a way of rendering 2-D bitmap content. It’s easy to imagine that we could explain how JavaScript loads, unpacks and finally renders image content using the canvas API. Very strange that they’re separate elements and that the img element doesn’t have the canvas API, no?
  • Asking for camera access with <input type="file" accept="image/*;capture=camera"> as well as with getUserMedia() is possible, but the form element version isn’t explained in the HTML specification in terms of getUserMedia() (which, admittedly, was added later — but no one has bothered to connect them yet).
  • That’s better than the Geolocation API. There’s currently no way to do that with an input element. It’s a valuable feature entirely disconnected from markup.
  • Neither HTML nor the Web Audio API explains how the audio tag works, despite the Web Audio API clearly being capable of providing the audio element’s implementation.

This isn’t to pick on or single out any of the hard-working developers and authors who have poured their lives into building consensus and software to introduce these capabilities. Indeed, we are grateful for their accomplishments.

The high-order bit is that the job isn’t done when both declarative and script-driven versions of a feature appear. Building a platform that’s resilient and adaptive for the long haul hinges on giving developers the confidence to take what they learn about one area and apply it evenly across the system. And that means explaining how the system works and drawing connections between the pieces.

In the case of many low-level APIs without high-level equivalents (such as Geolocation), their duty to “explain themselves” ends at the point where they have exposed a good API to JavaScript. “Good” here could mean being idiomatic and not introducing more platform magic than necessary. But when there are also declarative versions, or when only high-level versions exist, then the question looms large: How does that thing work? What are the layers below it? What APIs are required to make it go? How would you explain that API in mostly-JavaScript terms, appealing as little as possible to magical new platform APIs?

In an earlier time, attempting such a sweeping cultural change might have been foolish. Starting at a declarative level was undoubtedly a good idea. However, explaining even a bit of the underlying magic goes a long way: Exposing a DOM tree JavaScript opened new worlds to developers and bolstered the competitiveness of the platform. It also enabled the community to adapt through experimentation and enabled libraries to compete. This allows valuable, popular API ideas to potentially be standardized. The community can do it faster and with less risk than browser vendors and standards organizations can.

The answers aren’t always obvious, but the process of asking “How does that work?” is often more fruitful than it first appears. Details come into focus and missing explanations are uncovered, layer by layer. At each layer, it’s tempting to throw up our collective hands and say “It’s too hard” to explain all the stuff down there. Throw it all out. Start over. At least we won’t make the same mistakes, right?

Perhaps. But we’d also be starting from zero. Zero users, zero developers and zero useful content. The Web is the open, extensible, multi-vendor, universal platform of our lifetime. Small, meaningful changes to the Web can have an outsized impact relative to the effort involved. It’s a straightforward way to do a great deal of good. Encouraging layering, bit by bit, doesn’t mean giving up or “slowing down.” Just the opposite: It’s our only credible hope of making a Web that’s worthy to succeed the Web we have today.

image-1
Always keep in mind to “make things better” as much as you can. (Image source)

(al, ea, il)


© Extensible Web Manifesto Team for Smashing Magazine, 2013.

Tags: Coding

November 12 2013

10:34

An Introduction To DOM Events


  

Click, touch, load, drag, change, input, error, resize — the list of possible DOM events is lengthy. Events can be triggered on any part of a document, whether by a user’s interaction or by the browser. They don’t just start and end in one place; they flow though the document, on a life cycle of their own. This life cycle is what makes DOM events so extensible and useful. As developers, we should understand how DOM events work, so that we can harness their potential and build engaging experiences.

Throughout my time as a front-end developer, I felt that I was never given a straight explanation of how DOM events work. My aim here is to give you a clear overview of the subject, to get you up to speed more quickly than I did.

I will introduce the basics of working with DOM events, then delve into their inner workings, explaining how we can make use of them to solve common problems.

Listening For Events

In the past, browsers have had major inconsistencies in the way they attach event listeners to DOM nodes. Libraries such as jQuery have been invaluable in abstracting away these oddities.

As we move ever closer to standardized browser environments, we can more safely use the APIs from the official specification. To keep it simple, I will describe how to manage events for the modern Web. If you are writing JavaScript for Internet Explorer (IE) 8 or below, I would advise using a polyfill or framework (such as jQuery) to manage event listeners.

In JavaScript, we can listen to events using this:


element.addEventListener(<event-name>, <callback>, <use-capture>);
  • event-name (string)
    This is the name or type of event that you would like to listen to. It could be any of the standard DOM events (click, mousedown, touchstart, transitionEnd, etc.) or even your own custom event name (we’ll touch on custom events later).
  • callback (function)
    This function gets called when the event happens. The event object, containing data about the event, is passed as the first argument.
  • use-capture (boolean)
    This declares whether the callback should be fired in the “capture” phase. (Don’t worry: We’ll explain what that means a little later.)

var element = document.getElementById('element');

function callback() {
  alert('Hello');
}

// Add listener
element.addEventListener('click', callback);

Demo: addEventListener

Removing Listeners

Removing event listeners once they are no longer needed is a best practice (especially in long-running Web applications). To do this, use the element.removeEventListener() method:


element.removeEventListener(<event-name>, <callback>, <use-capture>);

But removeEventListener has one catch: You must have a reference to the callback function that was originally bound. Simply calling element.removeEventListener('click'); will not work.

Essentially, if we have any interest in removing event listeners (which we should in “long-lived” applications), then we need to keep a handle on our callbacks. This means we cannot use anonymous functions.


var element = document.getElementById('element');

function callback() {
  alert('Hello once');
  element.removeEventListener('click', callback);
}

// Add listener
element.addEventListener('click', callback);

Demo: removeEventListener

Maintaining Callback Context

An easy gotcha is callbacks being called with the incorrect context. Let’s explain with an example.


var element = document.getElementById('element');

var user = {
 firstname: 'Wilson',
 greeting: function(){
   alert('My name is ' + this.firstname);
 }
};

// Attach user.greeting as a callback
element.addEventListener('click', user.greeting);

// alert => 'My name is undefined'

Demo: Incorrect callback context

Using Anonymous Functions

We expected the callback to correctly alert us with My name is Wilson. In fact, it alerts us with My name is undefined. In order for this.name to return Wilson, user.greeting must be called within the context (i.e. whatever is left of the dot when called) of user.

When we pass the greeting function to the addEventListener method, we are only passing a reference to the function; the context of user is not passed with it. Internally, the callback is called in the context of element, which means that this refers to element, not to user. Therefore, this.firstname is undefined.

There are two ways to prevent this context mismatch. First, we can call user.greeting() with the correct context inside an anonymous function.


element.addEventListener('click', function() {
  user.greeting();
  // alert => 'My name is Wilson'
});

Demo: Anonymous functions

Function.prototype.bind

The last method isn’t so good because now we don’t have a handle on the function when we want to remove it with .removeEventListener(). Plus, it’s pretty ugly. I prefer to use the .bind() method (built into all functions, as of ECMAScript 5) to generate a new function (bound) that will always run in the given context. We then pass that function as the callback to .addEventListener().


// Overwrite the original function with
// one bound to the context of 'user'
user.greeting = user.greeting.bind(user);

// Attach the bound user.greeting as a callback
button.addEventListener('click', user.greeting);

We also have a reference to the callback at hand, which we can use to unbind the listener if need be.


button.removeEventListener('click', user.greeting);

Demo: Function.prototype.bind

  • Check the support page for Function.prototype.bind and polyfill if you need it.
  • The Event Object

    The event object is created when the event first happens; it travels with the event on its journey through the DOM. The function that we assign as a callback to an event listener is passed the event object as its first argument. We can use this object to access a wealth of information about the event that has occurred:

    • type (string)
      This is the name of the event.
    • target (node)
      This is the DOM node where the event originated.
    • currentTarget (node)
      This is the DOM node that the event callback is currently firing on.
    • bubbles (boolean)
      This indicates whether this is a “bubbling” event (which we’ll explain later).
    • preventDefault (function)
      This prevents any default behaviour from occurring that the user agent (i.e. browser) might carry out in relation to the event (for example, preventing a click event on an <a> element from loading a new page).
    • stopPropagation (function)
      This prevents any callbacks from being fired on any nodes further along the event chain, but it does not prevent any additional callbacks of the same event name from being fired on the current node. (We’ll talk about that later.)
    • stopImmediatePropagation (function)
      This prevents any callbacks from being fired on any nodes further along the event chain, including any additional callbacks of the same event name on the current node.
    • cancelable (boolean)
      This indicates whether the default behaviour of this event can be prevented by calling the event.preventDefault method.
    • defaultPrevented (boolean)
      This states whether the preventDefault method has been called on the event object.
    • isTrusted (boolean)
      An event is said to be “trusted” when it originates from the device itself, not synthesized from within JavaScript.
    • eventPhase (number)
      This number represents the phase that the event is currently in: none (0), capture (1), target (2) or bubbling (3). We’ll go over event phases next.
    • timestamp (number)
      This is the date on which the event occurred.

    Many other properties can be found on the event object, but they are specific to the type of event in question. For example, mouse events will include clientX and clientY properties on the event object to indicate the location of the pointer in the viewport.

    It’s best to use your favorite browser’s debugger or a console.log to look more closely at the event object and its properties.

    Event Phases

    When a DOM event fires in your app, it doesn’t just fire once where the event originated; it embarks on a journey of three phases. In short, the event flows from the document’s root to the target (i.e. capture phase), then fires on the event target (target phase), then flows back to the document’s root (bubbling phase).

    eventflow
    (Image source: W3C)

    Demo: Slow motion event path

    Capture Phase

    The first phase is the capture phase. The event starts its journey at the root of the document, working its way down through each layer of the DOM, firing on each node until it reaches the event target. The job of the capture phase is to build the propagation path, which the event will travel back through in the bubbling phase.

    As mentioned, you can listen to events in the capture phase by setting the third argument of addEventListener to true. I have not found many use cases for capture phase listeners, but you could potentially prevent any clicks from firing in a certain element if the event is handled in the capture phase.

    
    var form = document.querySelector('form');
    
    form.addEventListener('click', function(event) {
      event.stopPropagation();
    }, true); // Note: 'true'
    

    If you’re unsure, listen for events in the bubbling phase by setting the useCapture flag to false or undefined.

    Target Phase

    An event reaching the target is known as the target phase. The event fires on the target node, before reversing and retracing its steps, propagating back to the outermost document level.

    In the case of nested elements, mouse and pointer events are always targeted at the most deeply nested element. If you have listened for a click event on a <div> element, and the user actually clicks on a <p> element in the div, then the <p> element will become the event target. The fact that events “bubble” means that you are able to listen for clicks on the <div> (or any other ancestor node) and still receive a callback once the event passes through.

    Bubbling Phase

    After an event has fired on the target, it doesn’t stop there. It bubbles up (or propagates) through the DOM until it reaches the document’s root. This means that the same event is fired on the target’s parent node, followed by the parent’s parent, continuing until there is no parent to pass the event onto.

    Think of the DOM as an onion and the event target as the core of the onion. In the capture phase, the event drills into the onion through each layer. When the event reaches the core, it fires (the target phase), and then reverses, working its way back up through each layer (the propagation phase). Once the event has returned to the surface, its journey is over.

    Bubbling is useful. It frees us from listening for an event on the exact element it came from; instead, we listen on an element further up the DOM tree, waiting for the event to reach us. If events didn’t bubble, we would have to, in some cases, listen for an event on many different elements to ensure that it is caught.

    Demo: Identifying event phases

    The majority of, but not all, events bubble. When events do not bubble, it is usually for a good reason. If in doubt, check the specification.

    Stopping Propagation

    Interrupting the path of the event at any point on its journey (i.e. in the capture or bubbling phase) is possible simply by calling the stopPropagation method on the event object. Then, the event will no longer call any listeners on nodes that it travels through on its way to the target and back to the document.

    
    child.addEventListener('click', function(event) {
     event.stopPropagation();
    });
    
    parent.addEventListener('click', function(event) {
     // If the child element is clicked
     // this callback will not fire
    });
    

    Calling event.stopPropagation() will not prevent any additional event listeners from being called on the current target if multiple listeners for the same event exist. If you wish to prevent any additional listeners from being called on the current node, you can use the more aggressive event.stopImmediatePropagation() method.

    
    child.addEventListener('click', function(event) {
     event.stopImmediatePropagation();
    });
    
    child.addEventListener('click', function(event) {
     // If the child element is clicked
     // this callback will not fire
    });
    

    Demo: Stopping propagation

    Prevent The Browser’s Default Behavior

    The browser has default behaviors that will respond when certain events occur in the document. The most common event is a link being clicked. When a click event occurs on an <a> element, it will bubble up to the document level of the DOM, and the browser will interpret the href attribute and reload the window at the new address.

    In Web applications, developers usually want to manage the navigation themselves, without causing the page to refresh. To do this, we need to prevent the browser’s default response to clicks and instead do our own thing. To do this, we call event.preventDefault().

    
    anchor.addEventListener('click', function(event) {
      event.preventDefault();
      // Do our own thing
    });
    

    We can prevent many other default behaviors in the browser. For example, we could prevent presses of the space bar from scrolling the page in an HTML5 game, or we could prevent clicks from selecting text.

    Calling event.stopPropagation() here will only prevent callbacks that are attached further down the propagation chain from being fired. It will not prevent the browser from doing its thing.

    Demo: Preventing default behavior

    Custom Events

    The browser is not the only thing that is able to trigger DOM events. We can create our own custom events and dispatch them on any element in the document. This type of event would behave just the same as a regular DOM event.

    
    var myEvent = new CustomEvent("myevent", {
      detail: {
        name: "Wilson"
      },
      bubbles: true,
      cancelable: false
    });
    
    // Listen for 'myevent' on an element
    myElement.addEventListener('myevent', function(event) {
      alert('Hello ' + event.detail.name);
    });
    
    // Trigger the 'myevent'
    myElement.dispatchEvent(myEvent);
    

    Synthesizing “untrusted” DOM events on elements (for example, click) to simulate user interaction is also possible. This can be useful when testing DOM-related libraries. If you’re interested, the Mozilla Developer Network has a write-up on it.

    Note the following:

    • The CustomEvent API is not available in IE 8 and below.
    • The Flight framework from Twitter makes use of custom events to communicate between modules. This enforces a highly decoupled, modular architecture.

    Demo: Custom events

    Delegate Event Listeners

    Delegate event listeners are a more convenient and performant way to listen for events on a large number of DOM nodes using a single event listener. For example, if a list contains 100 items that all need to respond to a click event in a similar way, then we could query the DOM for all of the list items and attach an event listener to each one. This would result in 100 separate event listeners. Whenever a new item is added to the list, the click event listener would have to be added to it. Not only does this risk getting expensive, but it is tricky to maintain.

    Delegate event listeners can make our lives a lot easier. Instead of listening for the click event on each element, we listen for it on the parent <ul> element. When an <li> is clicked, then the event bubbles up to the <ul>, triggering the callback. We can identify which <li> element has been clicked by inspecting the event.target. Below is a crude example to illustrate:

    
    var list = document.querySelector('ul');
    
    list.addEventListener('click', function(event) {
      while (target.tagName !== 'LI') {
        target = target.parentNode;
        if (target === list) return;
      }
    
      // Do stuff here
    });
    

    This is better because we have only the overhead of a single event listener, and we no longer have to worry about attaching a new event listener when an item is added to the list. The concept is pretty simple but super-useful.

    I wouldn’t recommend using such a crude implementation in your app. Instead, use an event delegate JavaScript library, such as FT Lab’s ftdomdelegate. If you’re using jQuery, you can seamlessly use event delegation by passing a selector as the second parameter to the .on() method.

    
    // Not using event delegation
    $('li').on('click', function(){});
    
    // Using event delegation
    $('ul').on('click', 'li', function(){});
    

    Demo: Delegate event listeners

    Useful Events

    load

    The load event fires on any resource that has finished loading (including any dependent resources). This could be an image, style sheet, script, video, audio file, document or window.

    
    image.addEventListener('load', function(event) {
      image.classList.add('has-loaded');
    });
    

    Demo: Image load event

    onbeforeunload

    window.onbeforeunload enables developers to ask the user to confirm that they want to leave the page. This can be useful in applications that require the user to save changes that would get lost if the browser’s tab were to be accidentally closed.

    
    window.onbeforeunload = function() {
      if (textarea.value != textarea.defaultValue) {
        return 'Do you want to leave the page and discard changes?';
      }
    };
    

    Note that assigning an onbeforeunload handler prevents the browser from caching the page, thus making return visits a lot slower. Also, onbeforeunload handlers must be synchronous.

    Demo: onbeforeunload

    Stopping Window bounce in Mobile Safari

    At the Financial Times, we use a simple event.preventDefault technique to prevent mobile Safari from bouncing the window when it is scrolled.

    
    document.body.addEventListener('touchmove', function(event) {
     event.preventDefault();
    });
    

    Be warned that this will also prevent any native scrolling from working ( such as overflow: scroll). To allow native scrolling on a subset of elements that need it, we listen for the same event on the scrollable element and set a flag on the event object. In the callback at the document level, we decide whether to prevent the default behavior of the touch event based on the existence of the isScrollable flag.

    
    // Lower down in the DOM we set a flag
    scrollableElement.addEventListener('touchmove', function(event) {
     event.isScrollable = true;
    });
    
    // Higher up the DOM we check for this flag to decide
    // whether to let the browser handle the scroll
    document.addEventListener('touchmove', function(event) {
     if (!event.isScrollable) event.preventDefault();
    });
    

    Manipulating the event object is not possible in IE 8 and below. As a workaround, you can set properties on the event.target node.

    resize

    Listening to the resize event on the window object is super-useful for complex responsive layouts. Achieving a layout with CSS alone is not always possible. Sometimes JavaScript has to help us calculate and set the size of elements. When the window is resized or the device’s orientation changes, then we would likely need to readjust these sizes.

    
    window.addEventListener('resize', function() {
      // update the layout
    });
    

    I recommended using a debounced callback to normalize the callback rate and prevent extreme thrashing in the layout.

    Demo: Window resizing

    transitionEnd

    Today we use CSS to power the majority of transitions and animations in our applications. Sometimes, though, we still need to know when a particular animation has finished.

    
    el.addEventListener('transitionEnd', function() {
     // Do stuff
    });
    

    Note the following:

    • If you’re using @keyframe animations, use the animationEnd event name, instead of transitionEnd.
    • Like a lot of events, transitionEnd bubbles. Remember either to call event.stopPropagation() on any descendant transition events or to check the event.target to prevent callback logic from running when it’s not supposed to.
    • Event names are still widely vendor-prefixed (for example, webkitTransitionEnd, msTransitionEnd, etc). Use a library such as Modernizr to get the event name’s correct prefix.

    Demo: Transition end

    animationiteration

    The animationiteration event will fire every time a currently animating element completes an iteration. This is useful if we want to stop an animation but not midway through.

    
    function start() {
      div.classList.add('spin');
    }
    
    function stop() {
      div.addEventListener('animationiteration', callback);
    
      function callback() {
        div.classList.remove('spin');
        div.removeEventListener('animationiteration', callback);
      }
    }
    

    If you’re interested, I’ve written about the animationiteration event in a little more detail on my blog.

    Demo: Animation iteration

    error

    If an error occurs when a resource loads, we might want to do something about it, especially if our users are on a flaky connection. The Financial Times uses this event to detect any images that might have failed to load in an article and instantly hide them. Because the “DOM Level 3 Events” specification has redefined the error event to “not bubble,” we can handle the event in one of two ways.

    
    imageNode.addEventListener('error', function(event) {
      image.style.display = 'none';
    });
    

    Unfortunately, addEventListener does not address all use cases. My colleague Kornel has kindly pointed me to an example that demonstrates that the only way, sadly, to guarantee the execution of image error event callbacks is to use (the often frowned upon) inline event handlers.

    
    <img src="http://example.com/image.jpg" onerror="this.style.display='none';" />
    

    The reason for this is that you cannot be sure that the code that binds the error event handler will be executed before the error event actually happens. Using inline handlers means that when the markup is parsed and the image is requested, our error listeners will be attached.

    Demo: Image error

    Lessons From The Event Model

    A lot can be learned from the success of the DOM events model. We can employ similar decoupled concepts in our own projects. Modules in an application can be as complex as they need to be, so long as that complexity is sealed away behind a simple interface. Many front-end frameworks (such as Backbone.js) are heavily event-based, solving cross-module communication in a publish and subscribe model that is very similar to the DOM.

    Event-based architectures are great. They give us a simple common interface in which to write applications that respond to physical interactions across thousands of devices! Via events, devices tell us exactly what has happened and when it occurred, letting us respond however we please. What goes on behind the scenes is not of concern; we get a level of abstraction that frees us to get on with building our awesome app.

    Further Reading

    Special thanks to Kornel for a brilliant technical review.

    (al, il)


    © Wilson Page for Smashing Magazine, 2013.

    Tags: Coding

    November 07 2013

    11:14

    An In-Depth Introduction To Ember.js


      

    With the release of Ember.js 1.0, it’s just about time to consider giving it a try. This article aims to introduce Ember.js to newcomers who want to learn about this framework.

    Users often say that the learning curve is steep, but once you’ve overcome the difficulties, then Ember.js is tremendous. This happened to me as well. While the official guides are more accurate and up to date than ever (for real!), this post is my attempt to make things even smoother for beginners.

    First, we will clarify the main concepts of the framework. Next, we’ll go in depth with a step-by-step tutorial that teaches you how to build a simple Web app with Ember.js and Ember-Data, which is Ember’s data storage layer. Then, we will see how views and components help with handling user interactions. Finally, we will dig a little more into Ember-Data and template precompiling.

    An In-Depth Introduction To Ember.js
    Ember’s famous little mascot, Tomster. (Image credits)

    The unstyled demo below will help you follow each step of the tutorial. The enhanced demo is basically the same but with a lot more CSS and animations and a fully responsive UX when displayed on small screens.

    Unstyled demo Source code Enhanced demo

    Table of Contents

    Definitions Of Main Concepts

    The diagram below illustrates how routes, controllers, views, templates and models interact with each other.

    ember-sketch

    Let’s define these concepts. And if you’d like to learn more, check the relevant section of the official guides:

    Models

    Suppose our application handles a collection of users. Well, those users and their informations would be the model. Think of them as the database data. Models may be retrieved and updated by implementing AJAX callbacks inside your routes, or you can rely on Ember-Data (a data-storage abstraction layer) to greatly simplify the retrieval, updating and persistence of models over a REST API.

    The Router

    There is the Router, and then there are routes. The Router is just a synopsis of all of your routes. Routes are the URL representations of your application’s objects (for example, a route’s posts will render a collections of posts). The goal of routes is to query the model, from their model hook, to make it available in the controller and in the template. Routes can also be used to set properties in controllers, to execute events and actions, and to connect a particular template to a particular controller. Last but not least, the model hook can return promises so that you can implement a LoadingRoute, which will wait for the model to resolve asynchronously over the network.

    Controllers

    At first, a controller gets a model from a route. Then, it makes the bridge between the model and the view or template. Let’s say you need a convenient method or function for switching between editing mode to normal mode. A method such as goIntoEditMode() and closeEditMode() would be perfect, and that’s exactly what controllers can be used for.

    Controllers are auto-generated by Ember.js if you don’t declare them. For example, you can create a user template with a UserRoute; and, if you don’t create a UserController (because you have nothing special to do with it), then Ember.js will generate one for you internally (in memory). The Ember Inspector extension for Chrome can help you track those magic controllers.

    Views

    Views represent particular parts of your application (the visual parts that the user can see in the browser). A View is associated with a Controller, a Handlebars template and a Route. The difference between views and templates can be tricky. You will find yourself dealing with views when you want to handle events or handle some custom interactions that are impossible to manage from templates. They have a very convenient didInsertElement hook, through which you can play with jQuery very easily. Furthermore, they become extremely useful when you need to build reusable views, such as modals, popovers, date-pickers and autocomplete fields.

    Components

    A Component is a completely isolated View that has no access to the surrounding context. It’s a great way to build reusable components for your apps. A Twitter Button, a custom select box and those reusable charts are all great examples of components. In fact, they’re such a great idea that the W3C is actually working with the Ember team on a custom element specification.

    Templates

    Simply put, a template is the view’s HTML markup. It prints the model data and automatically updates itself when the model changes. Ember.js uses Handlebars, a lightweight templating engine also maintained by the Ember team. It has the usual templating logic, like if and else, loops and formatting helpers, that kind of stuff. Templates may be precompiled (if you want to cleanly organize them as separate .hbs or .handlebars files) or directly written in <script type="text/x-handlebars"><script> tags in your HTML page. Jump to the section on templates precompiling to dig into the subject.

    Helpers

    Handlebars helpers are functions that modify data before it is rendered on the screen — for example, to format dates better than Mon Jul 29 2013 13:37:39 GMT+0200 (CEST). In your template, the date could be written as {{date}}. Let’s say you have a formateDate helper (which converts dates into something more elegant, like “One month ago” or “29 July 2013”). In this case, you could use it like so: {{formateDate date}}.

    Components? Helpers? Views? HELP!

    The Ember.js forum has an answer and StackOverflow has a response that should alleviate your headache.

    Let’s Build An App

    In this section, we’ll build a real app, a simple interface for managing a group of users (a CRUD app). Here’s what we’ll do:

    • look at the architecture we’re aiming for;
    • get you started with the dependencies, files structure, etc.;
    • set up the model with Ember-Data’s FixtureAdapter;
    • see how routes, controllers, views and templates interact with each other;
    • finally, replace the FixtureAdapter with the LSAdapter to persist data in the browser’s local storage.

    Sketch Our App

    We need a basic view to render a group of users (see 1 below). We need a single-user view to see its data (2). We need to be able to edit and delete a given user’s data (3). Finally, we need a way to create a new user; for this, we will reuse the edit form.

    app-sketch

    Ember.js strongly relies on naming conventions. So, if you want the page /foo in your app, you will have the following:

    • a foo template,
    • a FooRoute,
    • a FooController,
    • and a FooView.

    Learn more about Ember’s naming conventions in the guides.

    What You’ll Need to Get Started

    You will need:

    • jQuery,
    • Ember.js itself (obviously),
    • Handlebars (i.e. Ember’s templating engine),
    • Ember-Data (i.e. Ember’s data-persistence abstraction layer).
    
    /* /index.html
    */
     …
     <script src="//code.jquery.com/jquery-2.0.3.min.js"></script>
     <script src="//builds.emberjs.com/handlebars-1.0.0.js"></script>
     <script src="//builds.emberjs.com/tags/v1.1.2/ember.js"></script>
     <script src="//builds.emberjs.com/tags/v1.0.0-beta.3/ember-data.js"></script>
     <script>
       // your code
     </script>
    </body>
    </html>
    

    Ember’s website has a builds section, where you can find all of the links for Ember.js and Ember-Data. Currently, Handlebars is not there; you will find it on the official Handlebars website.

    Once we have loaded the required dependencies, we can get started building our app. First, we create a file named app.js, and then we initialize Ember:

    
    /* /app.js 
    */
    window.App = Ember.Application.create();
    

    Just to be sure everything is OK, you should see Ember’s debugging logs in the browser’s console.

    console-log-1

    Our Files Directory Structure

    There’s not much of a convention on how to organize files and folders. The Ember App Kit (a Grunt-based environment to scaffold Ember apps) provides a kind of standard for this because it is maintained by the Ember team. Even simpler, you could put everything in a single app.js file. In the end, it’s really up to you.

    For this tutorial, we will simply put controllers in a controller folder, views in a view folder and so on.

    
    components/
    controllers/
    helpers/
    models/
    routes/
    templates/
    views/
    app.js
    router.js
    store.js
    

    Precompile Templates or Not?

    There are two ways to declare templates. The easiest way is to add special script tags to your index.html file.

    
    <script type="text/x-handlebars" id="templatename">
      <div>I'm a template</div>
    </script>
    

    Each time you need a template, you’d add another script tag for it. It’s fast and easy but can become a real mess if you have too many templates.

    The other way is to create an .hbs (or .handlebars) file for each of your templates. This is called “template precompiling,” and a complete section is dedicated to it later in this article.

    Our unstyled demo uses <script type="text/x-handlebars"> tags, and all of the templates for our enhanced demo are stored in .hbs files, which are precompiled with a Grunt task. This way, you can compare the two techniques.

    Set Up the Model With Ember-Data’s FixtureAdapter

    Ember-Data is a library that lets you retrieve records from a server, hold them in a Store, update them in the browser and, finally, save them back to the server. The Store can be configured with various adapters (for example, the RESTAdapter interacts with a JSON API, and the LSAdapter persists your data in the browser’s local storage). An entire section is dedicated to Ember-Data later in this article.

    Here, we are going to use the FixtureAdapter. So, let’s instantiate it:

    
    /* /store.js 
    */
    App.ApplicationAdapter = DS.FixtureAdapter;
    

    In previous versions of Ember, you had to subclass the DS.Store. We don’t need to do that anymore to instantiate adapters.

    The FixtureAdapter is a great way to start with Ember.js and Ember-Data. It lets you work with sample data in the development stage. At the end, we will switch to the LocalStorage adapter (or LSAdapter).

    Let’s define our model. A user would have a name, an email address, a short bio, an avatarUrl and a creationDate.

    
    /* /models/user.js 
    */
    App.User = DS.Model.extend({
      name         : DS.attr(),
      email        : DS.attr(),
      bio          : DS.attr(),
      avatarUrl    : DS.attr(),
      creationDate : DS.attr()
    });
    

    Now, let’s feed our Store with the sample data. Feel free to add as many users as you need:

    
    /* /models/user.js 
    */
    App.User.FIXTURES = [{
      id: 1,
      name: 'Sponge Bob',
      email: 'bob@sponge.com',
      bio: 'Lorem ispum dolor sit amet in voluptate fugiat nulla pariatur.',
      avatarUrl: 'http://jkneb.github.io/ember-crud/assets/images/avatars/sb.jpg',
      creationDate: 'Mon, 26 Aug 2013 20:23:43 GMT'
    }, {
      id: 2,
      name: 'John David',
      email: 'john@david.com',
      bio: 'Lorem ispum dolor sit amet in voluptate fugiat nulla pariatur.',
      avatarUrl: 'http://jkneb.github.io/ember-crud/assets/images/avatars/jk.jpg',
      creationDate: 'Fri, 07 Aug 2013 10:10:10 GMT'
    }
    …
    ];
    

    Learn more about models in the documentation.

    Instantiate the Router

    Let’s define our Router with the routes we want (based on the diagram we made earlier).

    
    /* /router.js 
    */
    App.Router.map(function(){
      this.resource('users', function(){
        this.resource('user', { path:'/:user_id' }, function(){
          this.route('edit');
        });
        this.route('create');
      });
    });
    

    This Router will generate exactly this:

    URL Route Name Controller Route Template N/A N/A ApplicationController ApplicationRoute application / index IndexController IndexRoute index N/A users UsersController UsersRoute users /users users.index UsersIndexController UsersIndexRoute users/index N/A user UserController UserRoute user /users/:user_id user.index UserIndexController UserIndexRoute user/index /users/:user_id/edit user.edit UserEditController UserEditRoute user/edit /users/create users.create UsersCreateController UsersCreateRoute users/create

    The :user_id part is called a dynamic segment because the corresponding user ID will be injected into the URL. So, it will look like /users/3/edit, where 3 is the user with the ID of 3.

    You can define either a route or a resource. Keep in mind that a resource is a group of routes and that it allows routes to be nested.

    A resource also resets the nested naming convention to the last resource name, which means that, instead of having UsersUserEditRoute, you would have UserEditRoute. In other words, in case this confuses you, if you have a resource nested inside another resource, then your files name would be:

    • UserEditRoute instead of UsersUserEditRoute;
    • UserEditControler instead of UsersUserEditController;
    • UserEditView instead of UsersUserEditView;
    • for templates, user/edit instead of users/user/edit.

    Learn more about how to define routes in the guides.

    The Application Template

    Each Ember.js app needs an Application template, with an {{outlet}} tag that holds all other templates.

    
    /* /templates/application.hbs 
    */
    <div class="main">
      <h1>Hello World</h1>
      {{outlet}}
    </div>
    

    If you’ve decided to follow this tutorial without precompiling templates, here’s what your index.html should look like:

    
    /* /index.html 
    */
      …
      <script type="text/x-handlebars" id="application">
        <div class="main">
          <h1>Hello World</h1>
          {{outlet}}
        </div>
      </script>
    
      <script src="dependencies.js"></script>
      <script src="your-app.js"></script>
    </body>
    </html>
    

    The Users Route

    This route deals with our group of users. Remember we saw earlier, in the definitions, that a route is responsible for querying the model. Well, routes have a model hook through which you can perform AJAX requests (for retrieving your models, if you don’t use Ember-Data) or for querying your Store (if you do use Ember-Data). If you’re interested in retrieving models without Ember-Data, you can jump to the section in which I briefly explain how to do it.

    Now, let’s create our UsersRoute:

    
    /* /routes/usersRoute.js 
    */
    App.UsersRoute = Ember.Route.extend({
      model: function(){
        return this.store.find('user');
      }
    });
    

    Learn more about how to specify the routes model hook in the guides.

    If you visit your app at the URL http://localhost/#/users, nothing will happen, because we need a users template. Here it is:

    
    /* /templates/users.hbs 
    */
    <ul class="users-listing">
      {{#each user in controller}}
        <li>{{user.name}}</li>
      {{else}}
        <li>no users… :-(</li>
      {{/each}}
    </ul>
    

    The each loop iterates over the users collection; here, controller equals UsersController. Notice that the {{#each}} loop has an {{else}} statement; so, if the model is empty, then no users… :-( will be printed.

    Because we’ve followed Ember’s naming conventions, we can omit the declaration of the UsersController. Ember will guess that we are dealing with a collection because we’ve used the plural of “user.”

    Object vs. Array Controller

    An ObjectController deals with a single object, and an ArrayController deals with multiple objects (such as a collection). We just saw that, in our case, we don’t need to declare the ArrayController. But for the purpose of this tutorial, let’s declare it, so that we can set some sorting properties on it:

    
    /* /controllers/usersController.js 
    */
    App.UsersController = Ember.ArrayController.extend({
      sortProperties: ['name'],
      sortAscending: true // false = descending
    });
    

    Here, we’ve simply sorted our users alphabetically. Learn more about controllers in the guides.

    Displaying the Number of Users

    Let’s use UsersController to create our first computed property. This will display the number of users, so that we can see changes when adding or deleting users.

    In the template, we just need something as simple as this:

    
    /* /templates/users.hbs 
    */
    …
    <div>Users: {{usersCount}}</div>
    …
    

    In UsersController, let’s declare the usersCount property — but not like a regular property, because this one will be a function that returns the model’s length.

    
    /* /controllers/usersController.js 
    */
    App.UsersController = Em.ArrayController.extend({
      …
      usersCount: function(){
        return this.get('model.length');
      }.property('@each')
    });
    

    Basically, usersCount takes the .property('@each') method, which tells Ember.js that this function is in fact a property that is watching for any changes to one of the models in the collection (i.e. the users). Later, we will see usersCount incrementing and decrementing as we create and delete users.

    Computed Properties

    Computed properties are powerful. They let you declare functions as properties. Let’s see how they work.

    
    App.Person = Ember.Object.extend({
      firstName: null,
      lastName: null,
    
      fullName: function() {
        return this.get('firstName') + ' ' + this.get('lastName');
      }.property('firstName', 'lastName')
    });
    
    var ironMan = App.Person.create({
      firstName: "Tony",
      lastName:  "Stark"
    });
    
    ironMan.get('fullName') // "Tony Stark"
    

    In this example, the Person object has two static properties, which are firstName and lastName. It also has a fullName computed property, which concatenates a full name by retrieving the value of the two static properties. Note that the .property('firstName', 'lastName') method tells the function to re-execute if either firsName or lastName changes.

    Properties (whether static or computed) are retrieved with .get('property') and can be set with .set('property', newValue).

    If you find yourself setting multiple properties consecutively, a better way to do it is with one single .setProperties({}), rather than with multiple instances of .set(). So, instead of doing this…

    
    this.set('propertyA', 'valueA');
    this.set('propertyB', valueB);
    this.set('propertyC', 0);
    this.set('propertyD', false);
    

    … you would do this:

    
    this.setProperties({
      'propertyA': 'valueA',
      'propertyB': valueB,
      'propertyC': 0,
      'propertyD': false
    });
    

    The documentation has so much more information about how to bind data with computed properties, observers and bindings.

    Redirecting From the Index Page

    If you go to the home page of your app (http://localhost/), you might be asking yourself why nothing is happening. That’s because you are viewing the index page, and we don’t have an index template. Let’s add one, then. We’ll call it index.hbs.

    Ember.js will notice that you are creating the index template for IndexRoute; so, no need to tell it anything else about the index in the Router. This is called an initial route. Three of them are available: ApplicationRoute, IndexRoute and LoadingRoute. Learn more about them in the guides.

    Now, let’s add a link to the user’s page with the {{#link-to}}…{{/link-to}} block helper. Why a block helper? Because you’re able to write text between the opening and closing tags, as if it were a real custom HTML element.

    
    /* /templates/index.hbs 
    */
    {{#link-to "users"}} Go to the users page {{/link-to}}
    

    This takes the route’s name that you want to link to as the first argument (the second optional argument is a model). Under the hood, it’s just a regular <a> element, although Ember also handles for us the active class name when reaching the matching route. Those link-to’s are perfect for navigation menus. Learn more about them in the guides.

    Another approach would be to tell IndexRoute to redirect to UsersRoute. Again, pretty easy:

    
    /* /routes/indexRoute.js 
    */
    App.IndexRoute = Ember.Route.extend({
      redirect: function(){
        this.transitionTo('users');
      }
    });
    

    Now, when you visit the home page, your will immediately be redirected to the /#/users URL.

    Single User Route

    Before getting our hands dirty with building the dynamic segment, we need a way to link to each user from the users template. Let’s use the {{#link-to}} block helper inside the user’s each loop.

    
    /* /templates/users.hbs 
    */
    …
    {{#each user in controller}}
      <li>
        {{#link-to "user" user}}
          {{user.name}}
        {{/link-to}}
      </li>
    {{/each}}
    

    The second argument of link-to is the model that will be passed to UserRoute.

    OK, let’s get back to our single user template. It looks like this:

    
    /* /templates/user.hbs 
    */
    <div class="user-profile">
      <img {{bind-attr src="avatarUrl"}} alt="User's avatar" />
      <h2>{{name}}</h2>
      <span>{{email}}</span>
      <p>{{bio}}</p>
      <span>Created {{creationDate}}</span>
    </div>
    

    Note that you can’t use <img src="{{avatarUrl}}">, because data inside attributes are bound with the bind-attr helper. For instance, you could do something like <img {{bind-attr height="imgHeight}}"/>, where imgHeight is a computed property in the current controller.

    You’ll find all you need to know about binding attributes and class names in the guides.

    So far, so good. But nothing happens when you click on the user’s links, because we told the Router that we want UserRoute to be nested in UsersRoute. So, we need an {{outlet}} in which to render the user template.

    
    /* /templates/users.hbs 
    */
    …
    {{#each user in controller}}
    …
    {{/each}}
    
    {{outlet}}
    

    An {{outlet}} is like a dynamic placeholder into which other templates can be injected when {{#link-to}} tags are clicked. It allows for views to be nested.

    Now, you should be able to view the user template injected in the page when visiting the page at the URL /#/users/1.

    Hey, wait a minute! We have declared neither UserRoute nor UserController, but it’s still working! Why is that? Well, UserRoute is the singular of UsersRoute, so Ember has generated the route and the controller for us (in memory). Thank goodness for naming conventions!

    For the sake of consistency, let’s declare them anyway, so that we can see how they look:

    
    /* /routes/userRoute.js 
    */
    App.UserRoute = Ember.Route.extend({
      model: function(params) { 
        return this.store.find('user', params.user_id);
      }
    });
    
    
    /* /controllers/userController.js 
    */
    App.UserController = Ember.ObjectController.extend();
    

    Learn more about dynamic segments in the guides.

    Edit a User

    Moving on to the edit user form nested in the single user, the template looks like this:

    
    /* /templates/user/edit.hbs 
    */
    <div class="user-edit">
      <label>Choose user avatar</label>
      {{input value=avatarUrl}}
    
      <label>User name</label>
      {{input value=name}}
    
      <label>User email</label>
      {{input value=email}}
    
      <label>User short bio</label>
      {{textarea value=bio}}
    </div>
    

    Let’s talk about those {{input}} and {{textarea}} tags. This form’s goal is to enable us to edit the user’s data, and these custom input tags take the model’s properties as parameters to enable data-binding.

    Note that it’s value=model, without the " ". The {{input}} helper is a shorthand for {{Ember.TextField}}. Ember.js has those built-in views especially for form elements.

    If you visit your app at the URL /#/users/1/edit, nothing will happen, because, again, we need an {{outlet}} to nest the edit template into the single user template.

    
    /* /templates/user.hbs 
    */
    …
    {{outlet}}
    

    Now, the template is correctly injected in the page. But the fields are still empty, because we need to tell the route which model to use.

    
    /* /routes/userEditRoute.js 
    */
    App.UserEditRoute = Ember.Route.extend({
      model: function(){ 
        return this.modelFor('user');
      }
    });
    

    The modelFor method lets you use the model of another route. Here, we’ve told UserEditRoute to get the model of UserRoute. The fields are now correctly populated with the model data. Try to edit them — you will see the changes occur in the parent templates as well!

    Our First Action

    OK, now we need a button to click that redirects us from UserRoute to UserEditRoute.

    
    /* /templates/user.hbs 
    */
    <div class="user-profile">
      <button {{action "edit"}}>Edit</button>
      …
    

    We just added a simple button that triggers our first {{action}}. Actions are events that trigger associated methods in their current controller. If no method is found in the controller, then the action bubbles up through routes until it matches something. This is explained well in the guides.

    In other words, if we click on the button, then it will trigger the edit action found in the controller. So, let’s add it to UserController:

    
    /* /controllers/userController.js 
    */
    App.UserController = Ember.ObjectController.extend({
      actions: {
        edit: function(){
          this.transitionToRoute('user.edit');
        }
      }
    });
    

    Actions, whether in controllers or in routes, are stored in an actions hash. But this is not the case for default actions, such as click, doubleClick, mouseLeave and dragStart. The Ember.js website has a complete list.

    Here, basically, our edit action says, “Go to the user.edit route.” That’s pretty much it.

    TransitionTo or TransitionToRoute?

    On a side note, transitioning from routes is slightly different from transitioning from controllers:

    
    // from a route
    this.transitionTo('your.route')
    // from a controller
    this.transitionToRoute('your.route')
    

    Saving User Modifications

    Let’s see how to save modifications after a user’s data has been edited. By saving, we mean persisting the changes. With Ember-Data, this means telling Store to save() the new record of the modified user. The Store will then tell the adapter to perform an AJAX PUT request (if our adapter is the RESTAdapter).

    From our application’s point of view, this would be an “OK” button that saves modifications and then transitions to the parent route. Again, we’ll use an {{action}}.

    
    /* /templates/user/edit.hbs 
    */
    <button {{action "save"}}> ok </button>
    
    
    /* /controllers/userEditController.js 
    */
    App.UserEditController = Ember.ObjectController.extend({
      actions: {
        save: function(){
          var user = this.get('model');
          // this will tell Ember-Data to save/persist the new record
          user.save();
          // then transition to the current user
          this.transitionToRoute('user', user);
        }
      }
    });
    

    Our edit mode is working well. Now, let’s see how to delete a user.

    Delete a User

    We can add a delete button beside the edit button in the user template — again, with a delete {{action}}, this time defined in UserController.

    
    /* /templates/user.hbs 
    */
    <button {{action "delete"}}>Delete</button>
    
    
    /* /controllers/userController.js 
    */
    …
    actions: {
      delete: function(){
        // this tells Ember-Data to delete the current user
        this.get('model').deleteRecord();
        this.get('model').save();
        // then transition to the users route
        this.transitionToRoute('users');
      }
    }
    

    Now, when you click on the “Delete” button, the user is instantly trashed. A bit rough. We should add a confirm state, something like “Are you sure?” with “Yes” and “No” buttons. To do this, we need to change {{action "delete"}} to make it show confirm-box instead of immediately deleting the user. Then, we obviously need to put confirm-box in the user template.

    
    /* /templates/user.hbs 
    */
    {{#if deleteMode}}
    <div class="confirm-box">
      <h4>Really?</h4>
      <button {{action "confirmDelete"}}> yes </button>
      <button {{action "cancelDelete"}}> no </button>
    </div>
    {{/if}}
    

    We’ve just written our first Handlebars {{if}} statement. It prints div.confirm-box only if the deleteMode property is true. We need to define this deleteMode in the current controller and then change the delete action to make it toggle deleteMode’s value to true or false. Now, our UserController looks like this:

    
    /* /controllers/userController.js 
    */
    App.UserController = Ember.ObjectController.extend({
      // the deleteMode property is false by default
      deleteMode: false,
    
      actions: {
        delete: function(){
          // our delete method now only toggles deleteMode's value
          this.toggleProperty('deleteMode');
        },
        cancelDelete: function(){
          // set deleteMode back to false
          this.set('deleteMode', false);
        },
        confirmDelete: function(){
          // this tells Ember-Data to delete the current user
          this.get('model').deleteRecord();
          this.get('model').save();
          // and then go to the users route
          this.transitionToRoute('users');
          // set deleteMode back to false
          this.set('deleteMode', false);
        },
        // the edit method remains the same
        edit: function(){ 
          this.transitionToRoute('user.edit');
        }
      }
    });
    

    Deletion now works perfectly with the “Yes” and “No” buttons. Awesome! Finally, the last thing to build is the create route.

    Create a User

    To create a user, let’s do something fun: Let’s reuse the edit template, because the create form will be exactly the same as the edit user form. First, we declare the create route, which will return an empty object in its model hook:

    
    /* /routes/usersCreateRoute.js 
    */
    App.UsersCreateRoute = Ember.Route.extend({
      model: function(){
        // the model for this route is a new empty Ember.Object
        return Em.Object.create({});
      },
    
      // in this case (the create route), we can reuse the user/edit template
      // associated with the usersCreateController
      renderTemplate: function(){
        this.render('user.edit', {
          controller: 'usersCreate'
        });
      }
    });
    

    Note the renderTemplate method; it enables us to associate a particular template with a route. Here, we’re telling UsersCreateRoute to use the user and edit template with UsersCreateController. Learn more about renderTemplate in the guides.

    Now, let’s define another save action, but this time in UsersCreateController. (Remember that an action first tries to match a corresponding method in the current controller.)

    
    /* /controllers/usersCreateController.js 
    */
    App.UsersCreateController = Ember.ObjectController.extend({
      actions: {
        save: function(){
          // just before saving, we set the creationDate
          this.get('model').set('creationDate', new Date());
    
          // create a record and save it to the store
          var newUser = this.store.createRecord('user', this.get('model'));
          newUser.save();
    
          // redirects to the user itself
          this.transitionToRoute('user', newUser);
        }
      }
    });
    

    Finally, let’s add the {{#link-to}} helper in the users templates, so that we can access the creation form:

    
    /* /templates/users.hbs 
    */
    {{#link-to "users.create" class="create-btn"}} Add user {{/link-to}}
    …
    

    That’s all there is to creating users!

    Format Data With Helpers

    We’ve already defined what helpers are. Now, let’s see how to create one that will format an ugly date into a nice clean formatted one. The Moment.js library is awesome for this purpose.

    Grab Moment.js and load it in the page. Then, we’ll define our first helper:

    
    /* /helpers/helpers.js 
    */
    Ember.Handlebars.helper('formatDate', function(date){
      return moment(date).fromNow();
    });
    

    Modify the user template so that it uses the formatDate helper on the {{creationDate}} property:

    
    /* /templates/user.hbs 
    */
    …
    <span>Created {{formatDate creationDate}}</span>
    …
    

    That’s it! You should see the dates nicely formatted: “2 days ago,” “One month ago,” etc.

    Format Data With Bound Helpers

    In this case, our date is static data because it’s not going to change in the future. But if you have data that needs to be updated (for example, a formatted price), then you would have to use a BoundHelper instead of the regular helper.

    
    /* /helpers/helpers.js 
    */
    Ember.Handlebars.registerBoundHelper('formatDate', function(date){
      return moment(date).fromNow();
    });
    

    A bound helper is able to automatically update itself if the data changes. Learn more about bound helpers in the guides.

    Switch to the LocalStorage Adapter

    Our app looks to be working fine, so we are ready to switch to the real thing. We could enable the RESTAdapter, but then we would need a REST server on which we could perform GET, PUT, POST and DELETE requests. Instead, let’s use LSAdapter, a third-party adapter that you can download on GitHub. Load it in your page (just after Ember-Data), comment out all of the FIXTURE data, and change ApplicationAdapter to DS.LSAdapter:

    
    /* /store.js 
    */
    App.ApplicationAdapter = DS.LSAdapter;
    

    Now, your users’ data will persist in local storage. That’s all! Seriously, it’s that easy. Just to be sure, open the Developer Tools in your browser and go into the “Resource” panel. In the “Local Storage” tab, you should find an entry for LSAdapter with all of your users’ data.

    console-localstorage

    Playing With Views

    So far, we haven’t declared any views in our simple CRUD, only templates. Why would we care about views? Well, they are powerful for events handling, animations and reusable components.

    jQuery and the didInsertElement

    How can we use jQuery the way we are used to for Ember.js’ views? Each view and component has a didInsertElement hook, which assures us that the view has indeed been inserted into the DOM. With that, you have secure jQuery access to elements in the page.

    
    App.MyAwesomeComponent = Em.Component.extend({
      didInsertElement: function(){
        // this = the view
        // this.$() = $(the view)
        this.$().on('click', '.child .elem', function(){
          // do stuff with jQuery
        });
      }
    });
    

    If you’ve registered jQuery-like events from inside didInsertElement, then you can use willDestroyElement to clean them up after the view has been removed from the DOM, like so:

    
    App.MyAwesomeComponent = Em.Component.extend({
      didInsertElement: function(){
        this.$().on('click', '.child .elem', function(){
          // do stuff with jQuery
        });
      },
      willDestroyElement: function(){
        this.$().off('click');
      }
    });
    

    Side Panel Components With className Bindings

    The combination of computed property and className binding sounds like a scary technique, but it’s really not that bad. The idea is that we add or remove a CSS class on an element if a property is either true or false. Of course, the CSS class contains a CSS transition.

    Suppose we have a hidden div in the DOM. When this div has a class of opened, it slides in. When it has a class of closed, it slides out. A side panel is a perfect example for this, so let’s build one.

    Here’s a JS Bin so that you can test the code:

    Reusable Ember.js side panels

    Let’s go through each tab in turn:

    • JavaScript tab
      First, we declare our SidePanelComponent with default classNames. Then, classNameBindings is used to test whether isOpen is true or false, so that it returns closed or opened. Finally, component has a toggleSidepanel action that simply toggles the isOpen boolean.
    • HTML tab
      This is the side panel’s markup. Note the {{#side-panel}}…{{/side-panel}} block tags; we can put whatever we want between them, which makes our side panel incredibly reusable. The btn-toggle button calls the toggleSidepanel action located in the component. The {{#if isOpen}} adds some logic by checking the value of the isOpen property.
    • CSS tab
      Here, we are basically putting the side panel off screen. The opened class slides it in, and closed slides it out. The animation is possible because we are listening for translate2D changes (transition:transform .3s ease).

    The guides have a lot more examples on how to bind class names from components and from inside templates.

    Modals With Layout and Event Bubbling

    This technique is way more complicated than the previous one, because a lot of Ember.js features are involved. The idea is to make an event bubble from a view to a route so that we can toggle a property located in a controller somewhere in the app. Also, here we are using a View instead of a Component (remember that, under the hood, a component is an isolated view).

    Reusable Ember.js modals

    • JavaScript tab
      The modalView is the default layout for all of our modals. It has two methods, showModal and hideModal. The showModal method is called with an action that bubbles up, first through controller, then through routes, until it finds a corresponding showModal action. We’ve stored showModal in the highest route possible, the applicationRoute. Its only goal is to set the modalVisible property inside the controller that was passed in the action’s second argument. And yes, creating a property at the same time as we set it is possible.
    • HTML tab
      Each modal has its own template, and we’ve used the convenient {{#view App.ModalView}}…{{/view}} block tags to encapsulate them in modal_layout. The modal’s controllers are not even declared because Ember.js has them in memory. Note that the {{render}} helper takes parameters, which are the template’s name and a generated controller for this template. So, here we are calling a modal01 template and a modal01 controller (auto-generated).
    • CSS tab
      For the purpose of this example, modals need to be present in the DOM. This can feel like a constraint, but the main benefit is the reduced paint cost; otherwise, Ember has to inject and remove them every time we call them. The second benefit is CSS transitions. The shown class applies two transitions: first, the top position (because the modal is off screen by default), then, with a little delay, it transitions the opacity (which also has a reduced paint cost when transitioning). The hidden class does the same in reverse. Obviously, you can apply a lot of cool transitions to your modals if they stay in the DOM.

    The guides have a lot more information about events, event bubbling, layouts and the {{render}} helper tag.

    What Is Ember-Data?

    Ember-Data is in beta as of the time of writing, so please use it with caution.

    It is a library that lets you retrieve records from a server, hold them in a store, update them in the browser and, finally, save them back to the server. The store may be configured with various adapters, depending on your back end. Here’s a diagram of Ember-Data’s architecture.

    ember-data-sketch

    The Store

    The store holds data loaded from the server (i.e. records). Routes and controllers can query the store for records. If a given record is called for the first time, then the store tells the adapter to load it over the network. Then, the store caches it for the next time you ask for it.

    Adapters

    The application queries the store, and the adapter queries the back end. Each adapter is made for a particular back end. For example, the RESTAdapter deals with JSON APIs, and LSAdapter deals with local storage.

    The idea behind Ember-Data is that, if you have to change the back end, then you simply plug another adapter, without having to touch a single line of your application’s code.

    • FixtureAdapter
      FixtureAdapter is perfect for testing Ember and Ember-Data. Fixtures are just sample data that you can work with until your app reaches the production phase. We went over how to configure it in an earlier part of this article.
    • RESTAdapter
      RESTAdapter is the default adapter in Ember-Data. It lets you perform GET, PUT, POST and DELETE requests over a REST API. It also requires some specific JSON conventions in return. Enabling RESTAdapter looks like this:
      
      App.ApplicationAdapter = DS.RESTAdapter.extend({
        host: 'https://your.api.com'
      });
      

      There’s a lot more to discover about RESTAdapter in the guides.

    • Custom adapter
      You could use something other than the two default adapters (FixtureAdapter and RESTAdapter). A bunch of them are on GitHub. For instance, there’s the LocalStorage Adapter, which is demonstrated in the guides’ sample Todos app and is also the one I use in the demo.

    What About Not Using Ember-Data?

    In this article, I’ve chosen to cover Ember-Data because it’s almost stable and is probably one of the coolest thing happening these days in the JavaScript world. But perhaps you’re wondering whether getting rid of it is possible. The answer is yes! In fact, using Ember.js without Ember-Data is pretty easy.

    There are two ways to do it.

    You could use another library for your model’s retrieval and persistence. Ember-ModelEmber-ResourceEmber-Restless and the recent EPF are good alternatives. EmberWatch has written a great little article that sums up “Alternatives to Ember Data.”

    The other way would be to not rely on a library, in which case you would have to implement methods to retrieve models with AJAX calls. “Ember Without Ember Data,” by Robin Ward (the guy behind Discourse), is a great read. “Getting Into Ember.js, Part 3” by Rey Bango on Nettuts+ deals specifically with models.

    For instance, here’s a static method with reopenClass on a model:

    
    /* /models/user.js
    */
    // our own findStuff method inside the User model
    App.User.reopenClass({
      findStuff: function(){
        // use regular AJAX / Promises calls
        return $.getJSON("http://your.api.com/api").then(function(response) {
          var users = [];
          // creates new Ember objects and store them into the users Array
          response.users.forEach(function(user){
            users.push( App.User.create(user) );
          });
          // finally returns the array full of Ember Objects
          return users;
        });
      }
    });
    

    You would use your findStuff method in your routes’ model hook:

    
    /* /routes/usersRoute.js
    */
    App.UsersRoute = Em.Route.extend({
      model: function(){
        return App.User.findStuff();
      }
    });
    

    What Is Handlebars Template Precompiling?

    Basically, template precompiling entails grabbing all Handlebars templates, transposing them into JavaScript strings, and then storing them in Ember.TEMPLATES. It also entails an additional JavaScript file to load in your page, which will contain the JavaScript-compiled versions of all of your Handlebars templates.

    For very simple apps, precompiling can be avoided. But if you have too many <script type="text/x-handlebars"> templates in your main HTML file, then precompiling will help to organize your code.

    Furthermore, precompiling your templates will enable you to use the runtime version of Handlebars, which is lighter than the regular one. You can find both the runtime and standard versions on the Handlebars website.

    Template Naming Conventions

    Partials have to begin with a _. So, you will have to declare a _yourpartial.hbs file or, if you don’t precompile your templates, a <script type="text/x-handlebars" id="_yourpartial"> tag.

    Components have to begin with components/. So, you will have to store them in a components/ folder or, if you don’t precompile templates, declare a <script type="text/x-handlebars" id="components/your-component"> tag. Component names are hyphenated.

    Otherwise, views have a templateName property in which you can specify which template to associate with the view. Take this declaration of a template:

    
    <script type="text/x-handlebars" id="folder/some-template">
      Some template
    </script>
    

    You can associate it with a particular view:

    
    App.SomeView = Em.View.extend({
      templateName: 'folder/some-template'
    });
    

    Precompiling With Grunt

    If you use Grunt, then you probably use it for other building-related tasks (concatenation, compression, that kind of stuff), in which case you should be familiar with the package.json file, which comes with Node.js and Node Packaged Modules. I’ll assume you are already familiar with Grunt.

    As of the time of writing, two plugins are available for Grunt to transpose your .hbs files to a templates.js file: grunt-ember-handlebars and grunt-ember-templates. The latter seems a bit more up to date than the former.

    I’ve made a Gist for each of them, in case you need help with configuration:

    Once it’s configured, you should be able to run grunt in a command-line editor, which should produce the templates.js file. Load it into index.html (after ember.js), and then go into the browser’s console and type Em.TEMPLATES. You should see a hash containing all of the compiled templates.

    Be aware that Ember.js doesn’t need the template file’s complete path, nor the file’s extension. In other words, the template’s name should be users/create, not /assets/js/templates/users/create.hbs.

    Both plugins have options to handle this. Simply refer to the respective guide, or look at the Gists linked to above. You should end up with something like this:

    console-templates

    And this is exactly what we need to make everything work as intended. It’s all you need to know about precompiling with Grunt.

    Precompiling With Rails

    Precompiling with Rails is surely the easiest way to do it. The Ember-Rails gem handles pretty much everything for you. It almost works out of the box. Carefully follow the installation instructions in the readme file on GitHub, and you should be all good. Right now, in my humble opinion, Rails has the best Ember and Handlebars integration available.

    Tools, Tips And Resources

    Chrome Ember Extension

    Ember Extension is a very convenient Chrome extension. Once installed, an “Ember” tab will appear near the “Console” tab. Then, you can navigate through controllers, routes and views. And the “Data” tab will greatly help you to explore your records if you are using Ember-Data.

    console-ember-extension
    Exploring your app’s objects has never been so easy.

    Ember App Kit

    Maintained by the Ember team, the Ember App Kit lets you easily scaffold Ember.js apps. It contains Grunt for compiling assets, JSHint, QUnit, the Kharma test runner, Bower and ES6 Modules support.

    Ember Tools

    This GitHub project, Ember Tools, is a basic command-line interface for creating and scaffolding Ember apps. Take a minute to watch the animated GIF in the readme file, and you’ll see why it’s so cool.

    Development and Minified Version

    Always use the development build when developing because it contains a lot of comments, a unit-testing package and a ton of helpful error messages, all of which has been removed in the minified build. Find links to both in the builds section of the Ember.js website.

    Debugging Tips

    Ember.js usually gives you cool human-readable errors in the browser’s console (remember to use the development version). Sometimes, though, figuring out what’s going on is tricky. Some convenient methods are {{log something}} and {{controller}}, which helpfully prints the current controller for the template to which you’ve added this helper.

    Or you could log each Router transition like so:

    
    window.App = Ember.Application.create({
      LOG_TRANSITIONS: true
    });
    

    The guides have an exhaustive list of these handy little methods.

    Properly Comment Your Handlebars

    This one can be frustrating. Never ever comment a Handlebars tag with a regular HTML comment. If you do, you’ll completely break the app, without getting a clue about what’s happening.

    
    // never do this
    <!-- {{foo}} -->
    
    // instead do this
    {{!foo}}
    

    Conclusion

    I hope this long article has given you a better understanding of this awesome framework. But the truth is, we’ve only scratched the surface. There’s so much more to cover. For instance, we have the router and its asynchronous nature, which resolves model requests with promises (so that you can easily implement loading spinners). There is also the object model, with its class and instances inheritance, mixins, observers, filters, macros, collectionViews, components, dependencies managed between controllers, and testing package. And so much more!

    Obviously, I couldn’t cover everything. Fortunately, the guides will take you through all of these topics very well.

    Happy Ember.js coding, folks!

    Resources

    Acknowledgments

    Huge thanks to Mathieu Breton and Philippe Castelli, who both taught me everything they know about Ember.js while I was learning it. Also, a big thank you to Tom Dale, who helped me to revise this very long article.

    (al, il)


    © Julien Knebel for Smashing Magazine, 2013.

    Tags: Coding
    Older posts are this way If this message doesn't go away, click anywhere on the page to continue loading posts.
    Could not load more posts
    Maybe Soup is currently being updated? I'll try again automatically in a few seconds...
    Just a second, loading more posts...
    You've reached the end.

    Don't be the product, buy the product!

    Schweinderl