• No results found

tools | tech | trends HTML, JavaScript, RequireJS

uilding websites can often lead to head tags stuff ed with tens of script tags, some of which may depend on each other, others of which may be standalone. Managing these files and the order in which they’re loaded can be time consuming, and browsers stop rendering when parsing these files. This can, at worst, cause a highly unresponsive UI.

Like a knight in shining armour, RequireJS comes to the rescue. Unlike other programming languages, JavaScript doesn’t have a package management system but Asynchronous Module Definitions (AMD) are part of a solution to this. RequireJS is a module loader, which, as the name might suggests, loads in modules that you specify asynchronously (ie without blocking the page from rendering).

The RequireJS project also has a few helpful plug-ins and tools: domReady, text, a Coff eeScript adapter, i18n, and r.js. In particular, i18n makes converting

international strings a doddle and r.js is a separate tool to help minify files or projects, concatenating dependencies into a single file to reduce HTTP requests. We will be using domReady, text, and r.js.

In this tutorial you’ll learn how to use RequireJS to manage library dependencies by building a simple application that’ll pull in photos from Flickr and display them in a slideshow.

B

005

app.js

006

slides.html

007

jquery.js

008

main.js

009

styles

010

main.css

011

index.html

03 Require configuration

The first thing we’ll do in our main.js file is set up some custom configuration options. The baseUrl defaults to the folder that data-main points to in our HTML, but in this case most libraries are housed under components. We can also set up paths for each dependency separately Now RequireJS will look for domReady at scripts/components/requirejs/ domReady.js.

001

require.config({

002

baseUrl: ‘scripts/components’,

003

paths: {

004

domReady: ‘requirejs/domReady’

005

}

006

});

04 Shim configuration

Many scripts will need to be ‘shimmed’ so that RequireJS can work with them. Scripts that declare themselves in the global scope are ‘legacy’ and RequireJS needs to be told what they call themselves (eg Underscore.js is _). We can also set dependencies so if we define a module which requires Backbone, then it’ll automatically grab jQuery and Underscore too.

001

shim: {

002

handlebar: {

003

exports: ‘Handlebars’

004

},

005

‘jquery.slideshow’: [‘jquery’],

006

backbone: {

007

deps: [‘underscore’, ‘jquery’],

008

exports: ‘Backbone’

009

},

010

underscore: {

011

exports: ‘_’

012

}

013

}

05 Define a module

Defining a module is pretty simple but there is actually a lot going on here. First we call define and pass it three things: a module ID (‘main’), its dependencies (if any) and a function that’ll contain the rest of the code. The dependencies get passed to the function in the order that you specify them so you could write function(require, app).

001

define(‘main’, [‘require’, ‘app’], function(require) {});

002

data-main attribute. This sets the base URL to the scripts folder and runs the code found in scripts/main.js – with most RequireJS definitions you can omit the file extension.

001

<!doctype html>

002

<html>

003

<head>

004

<meta charset=”utf-8”>

005

<title>Flickr Slideshow</title>

006

<link rel=”stylesheet” href=”styles/ main.css”>

007

</head>

008

<body>

009

<div class=”slideshow”></div>

010

<script data-main=”scripts/main” src=”scripts/components/ require.js”></ script>

011

</body>

012

</html>

02 Directory structure

Our directory structure will look like the following code, as RequireJS encourages a shallow structure. However, it can be customised when it is being configured to match your preferences. Instead of thinking of your scripts by filename, think of them like IDs. For example, if we require jQuery as an ID it’ll request scripts/components/jQuery.js.

001

.

002

scripts

003

app.build.js

004

components

01 Data-main

We’ll start with our HTML page. The most interesting part here is the script tag. We load Require.js as we would any other JavaScript file – but this one has a

Source files

available

sIUUQXXX ŢJMFTJMPDPVL CLT

Get faster, smarter code with RequireJS

06 Requiring dependencies

Within our module we’ll require domReady, a RequireJS plug-in. domReady is a cross-browser solution to waiting for the DOMContentLoaded event without having to wait for jQuery to be loaded. It’s especially important waiting for domReady with asynchronous scripts because they could attempt to change the DOM before it’s finished parsing.

001

‘use strict’;

002

require([‘domReady’, ‘http://api.flickr.com/ services/ rest/?method=flickr.interestingness. getList&api_key=dfe82aea164aa1 83a555938 136493c82&format=json&extras=url_l&jsoncallback= define’, ‘app’], function (domReady, data, app) {

003

domReady(function() {

004

app.setSlides(data);

005

});

006

});

07 External dependencies

RequireJS can also make AJAX calls to diff erent domains, making it very useful for API calls. In this case we’ll get a JSONP feed containing Flickr’s most

interesting photos and images; note that the callback method is called define. Importantly, this means that we can depend on external services alongside other dependencies, such as libraries from content delivery networks.

001

‘use strict’;

002

require([‘http://api.flickr. com/services/rest/?method=flickr. interestingness.getList&api_key=API_ KEY&format=json&extras=url_l&jso ncallback=define’]);

08 Custom dependencies

Requiring ‘app’ will make a request to scripts/ components/app.js where we’ll define our own module. Again, we pass through the arguments in order so: domReady, the Flickr response we’ll call ‘data’, and the ‘app’ will be an object with diff erent methods attached to it. If you get script errors it could be because the file cannot be found.

001

‘use strict’;

require([‘app’], function (domReady, data, app) {

002

console.log(domReady, data, app);

003

});

09 Define another module

Now we’ll write app.js. Along with domReady, another RequireJS plug-in is text.js. This is used to load simple text files, we’ll use it in conjunction with Handlebars, a JavaScript templating engine, to load in some HTML. It’s simple to use, just ‘text!’ suff ixed with the filename. We’ve already told RequireJS that jquery.slideshow is dependent on jQuery, so it’ll load both.

001

/*global define */

002

define(‘app’, [‘text!slides.html’, ‘handlebar’, ‘jquery. slideshow’], function (slides) {

003

});

10 Text.js and Handlebars

slides.html contains this morsel of HTML. The curly brackets are where Handlebars will replace the text with the value of the data that we’ll pass to it. The great thing about Handlebars is that it’s highly readable. It goes through each photo if there’s a length greater than 0, else it displays the message.

001

<ul class=”slides”>

002

{{#each photos.photo}}

003

<li><img src=”{{url_l}}” alt=”{{title}}” title=”{{title}}”></ li>

004

{{else}}

005

<li><p>There were no results!</p></li>

006

{{/each}}

007

</ul>

008

11

setSlides method

Within our app module definition we can set up a method that will take the Flickr data (although because of the implementation abstracting the source away it could be any service). We then return the method so that our service will be able to be used by other parts of the application.

001

var setSlides = function(data){/* next step */};

002

return {

003

setSlides: setSlides

004

};

12

Compile Handlebars template

We’ve made a request to get slides.html with the Handlebars brackets and passed it in as a variable called slides. To interpolate our data with the template we need to compile the HTML (Handlebars.compile) and then pass the data to the template, appending it all to our slideshow element.

001

var setSlides = function(data) {

<Right> s)BOEMFCBSTIBTVTFGVMIFMQFST JOUIJTDBTFXFSFEJTQMBZJOHB NFTTBHFJGOPSFTVMUTXFSFSFUVSOFEVTJOHJUT\\FMTF^^IFMQFS <Below> s/PUFUIBUUIFTMJEFTIPXQMVHJOJTOUDBMMFEVOUJMUIFK2VFSZ EFQFOEFODZIBTCFFOTBUJTŢJFE <Left> s5IF3FRVJSFJSTJUFJTBO JOWBMVBCMFSFTPVSDFUIBOLTUPJUT FYIBVTUJWFEPDVNFOUBUJPOBOE MJOLTUPJUTIFMQGVMDPNNVOJUZ <Below> s5BEB8IFOBMMUIF EFQFOEFODJFTBSFNFU PVSCBTJD TMJEFSJTEJTQMBZFEBOECSPVHIU UPMJGF

17

Listen for submit

We’ll use jQuery to listen for the form to be submitted. When it is submitted we’ll get the text of what they typed in (by reading .value) and pass it through encodeURI, this converts special characters e.g. ‘&’ becomes ‘%26’, so that the search term can be passed to a URL parameter.

001

$(‘.search-flickr’).on(‘submit’, function (e) {

002

e.preventDefault();

003

var term = encodeURIComponent(e. target[0].value);

004

// next step

005

return false;

006

});

18

Get images

This time we’ll use jQuery to make the request using $.getJSON and adding the term in as the tags value. When there’s a response we call the setSlides method exactly as we did before. As the data format is the same we don’t need to change anything else, and you should see the images update.

001

$.getJSON(‘http://api.flickr.

com/services/rest/?method=flickr. photos.search&tags=’ + term + ‘&api_ key=API_KEY&format=json&extras=u rl_l&jsoncallback=?’, function (data) {

002

app.setSlides(data);

003

});

19

RequireJS command line

RequireJS isn’t limited to just handling

dependencies, a spinoff project of RequireJS is r.js, which can be loaded either from Node or Java (Node is recommended as it’s much faster). Note that to install it you’ll need to have Node installed and you may have to use sudo if you get permission errors.

001

$ npm install -g requirejs

15

Form HTML

Now we’ll add search functionality to our page. We’ll build a simple form with a search input and a button so that people can type in a search term and we’ll ask Flickr for all of the images that are tagged with that term.

001

<form>

002

<input type="search"

placeholder="Search for

something" required>

003

<button>Find</button>

004

</form>

16

Flexibility of asynchronicity

In our original main.js file we’ll require domReady, app.js and jQuery. Newer versions of jQuery register themselves as a named AMD module called ‘jquery’. Again we’ll wait for domReady but here’s where the asynchronicity of RequireJS comes in to play – because we’re not waiting for a response from Flickr, this function will fire before the one above it.

001

require([‘domReady’, ‘app’, ‘jquery’], function (domReady, app) {

002

domReady(function() {

003

// next step

004

});

005

});

002

var template = Handlebars.compile(slides);

003

$(‘.slideshow’).html( template(data) );

004

};

13

Responsive slides plug-in

We’ll use Viljami Salminen’s ResponsiveSlides.js to easily convert our list of images into a slideshow that simply fades between each one. Again, because of our implementation, it’s trivial to swap this out with another library if needs change. Because we call this after the Handlebars template has compiled and been appended, we can trust that .slides exists.

001

$(‘.slides’).responsiveSlides();

14

Use app module

That’s all there is to it! At this point the most interesting Flickr images will populate the slideshow. By arranging individual pieces of functionality into separate files, RequireJS encourages a more modular approach to your front-end software architecture. This is

something that other languages have had for some time and comes into its own in large-scale projects.

001

require([ ... ‘app’], function (domReady, data, app) {

002

console.log(domReady, data, app);

003

app.setSlides(data);

004

});

How

Related documents