Using RequireJs to manage script dependencies based on attributes in your Sitecore Solution

A common architectural issue I hear amongst developers is how to prevent Sitecore loading all and sundry when it comes to javascript dependencies. Indeed it is often the case that front end developers don’t inform us or assume that we will use a single page type model and therefore don’t really need to worry about the number of script dependencies. The reality is that in actual fact if we included every script that a front end developer used throughout all of the scripts within the site – we would have 20 odd script tags in our markup, something you may well be keen to avoid.

As a solution to this, myself and many other developers including former colleague and Sitecore Solution Architect Richard Seal in his post Using Require to Organize your Sitecore Javascript opt to use an advanced module loader such as RequireJs. If you haven’t used it before, I recommend first reading their documentation as there are definite ways to write your javascript (and in particular jQuery) code to maximise your success with it.

In Richards post he gives a nice quick introduction on how to get up and running quickly using Require in your solution. I noted that the way I have done this myself differs a little in that I also use data attributes in markup to control what is called where rather than Richards approach of inline script tags. It’s important to note that RequireJs only allows a script to be loaded once per page by default, so however you load your required scripts is much the same.

Attributes – data-require and data-require-inline

In short:

data-require-inline: Requires the named pipe delimited script(s) as they are found in the document
data-require: Requires the named pipe delimited script(s) as a javascript function call at the end of the document loading.

In the following example you can see how I use data attributes to govern what is required by which elements. In reality when you broke the page up into renderings A, B and C you may well any of the 3 grouped together in a regular page. Its worth noting that data-require and data-require inline can be put on any element in the document, so in theory at least, you could even add it to a script tag (though not sure why you would want to!). The body tag is very commonplace though.

<!DOCTYPE html>
<html>
	<head>
		<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
		<title>Example Require Js</title>
		<script src="/assets/js/vendor/require.js"></script>
		<script src="/assets/js/main.js"></script>
	</head>
	<body>
                <!-- Rendering A - Requires featherlight and featherlight-start at document ready-->
		<div data-require="featherlight|featherlight-start">
			<a href="">Link 1</a>
		</div>

                <!-- Rendering B - Requires silly-advertising-script immediately -->
                <div class="something-to-respond-to" data-require-inline="silly-advertising-script">
                </div>

                <!-- Rendering C - Requires just script-a at document ready -->
                <a data-require="script-a" href="www.asite.com"></a>
	</body>
	
</html>

In use, this means that simply copying and pasting the markup from your front end developers is enough for the scripts to load, and that each piece of markup is in charge of its javascript requirements. You can use this in conjunction with other data attributes or css classes to allow your plugins to behave accordingly.

RequireJs configuration

The way (in this case for continuity) I have achieved this, is to use a modified version of Richard Seal’s main.js

require.config({
  baseUrl: "/assets/js",
  paths: {
    jquery: "vendor/jquery-1.11.3.min",
    "featherlight": "vendor/featherlight.min",
    "featherlight-start": "modules/featherlight-start.min"
  },
  shim: {
    "featherlight": {
      deps: ["jquery"]
    },
    "featherlight-start": {
      deps: ["featherlight"]
    }
  }
});

// Now run any initialization javascript for the site
require(["jquery"], function ($) {
	
	// load scripts that MUST be done where they appear
	processRequirements('require-inline');
	
	// load in things that can be done at document.ready
	$(function () {
		processRequirements('require');
	});
	
	function processRequirements(selector){
		if(!selector){
			return;
		}
		$('[data-' + selector + ']').each(
			function(){
				loadScripts($(this).data(selector));
			}
		);
	}
	
	function loadScripts(requiredScripts){
		if(requiredScripts == null || requiredScripts.length == 0){
			return;
		}
		var elems = requiredScripts.split('|');
		for(var i = 0; i < elems.length; i++){
			require([elems[i]]);
		}
	}
});

Conclusion

This of course won’t work for every situation where you have javascript requirements, but to date has for me at least covered a large majority of the scenario’s I require.

Advertisements

2 thoughts on “Using RequireJs to manage script dependencies based on attributes in your Sitecore Solution

  1. We are very actively using require with Sitecore as well and have created a simple @Html helper to require a module and also pass in parameters (things that the rendering knows and wants the JavaScript to know as well – e.g. its unique id to segregate multiple instances of the same component on the same page). Had to go beyond data- attributes and write a little JSON metadata in an invisible span (or Page Editor-inspired code element). The rest is the same – deferred initialization of all “required” modules by actually requiring them 🙂 Maybe we will put it up on github later this fall.

    • I achieve similar with a mix of wrapping js files that I can require in, and attributes that would govern things like the rendering id which can be picked up on in the javascript components. Would be good to see your approach when it goes up.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s