One principal which always had a shroud over it for to long with me was SharePoint’s SP.SOD functionality. The MSDN isn’t all to helpful, but it still serves as a starting point for where to find the SOD methods. I thought I’d take a step back tonight and play with the system and document how to use this system effectively as well as understanding both sides of the equation when it comes to your custom script and SharePoint’s script .

What is SOD exactly?

Exactly what it spells out, script on demand. SOD provides a lightweight client side framework. JavaScript is dynamically loaded if a script flags that something is needed. It also helps with the loading by understanding if a script is dependent on another script, to include those dependencies as well. It’s namespace is SP.SOD — go ahead and type SP.SOD in your console on a SP page to look inside.

What does this solve? Take the old approach. Lets say you have a large amount of JavaScript related controls, possibly related to your custom web parts, scattered about your site collection. You have amassed 20 fair sized scripts. You could simply  add the following to the master, or custom actions (better for SP Online) which would have the same effect of adding script tags like such:

<script src="myscript1.js"></script>
<script src="myscript2.js"></script>
<script src="myscript3.js"></script>
...

The downside of this is the fact that quite a few of these scripts may end up never being touched, causing a performance loss. Certain scripts which may be utilized on the page the user is on may not actually be run until a user needs to click a button. Getting this load time away from the page load will help boost the page load time. An important thing to know if not already: once the browser gets to your script tags no other content on your page will load until your JS files have completely loaded!

The SP.SOD framework helps with this. As a quick example, lets assume myscript1.js is a behemoth 500KB script (not particularly uncommon) and that the functionality is only ever executed when a user clicks a particular button on the page. We could implement this using SOD like so:


<script type='text/javascript'>

//This line just tells SOD that we *might* use myscript1.js. The browser will not load this file just yet. 
SP.SOD.registerSod('myscript1.js', '/Style Library/Mikes Files/myscript1.js'); 

//Lets make the function to handle the button press to load myscript1.js and run its code.
function RunScript1(){
	//executeFunc signals the browser to load myscript1.js and the function is the callback when its loaded
	SP.SOD.executeFunc('myscript1.js', null, function(){
		//myscript1 is loaded now! its functions are available to use.
		someFunctionInScript1();
	});

    //Since executeFunc is only calling a method with no arguments, we could simplify the call to:
    //SP.SOD.executeFunc('myscript1.js', null, someFunctionInScript1);
}

</script>

<a href="#" onclick="RunScript1();">Run script!</a>

One thing to keep in mind here is that the load is asynchronous, it doesn’t block other JavaScript from running. Ideally it would be nice to have a little loading icon, but generally smaller scripts won’t have noticeable loads when by themselves. Even bigger to keep in mind is that any code immediately after the executeFunc will most likely run before the JavaScript loads and completes the callback. In fact, I believe it will always complete first.

The Complete Arsenal

SP.SOD isn’t a large namespace, but you will likely end up using most of the functionality so lets take a peek at what is offered.

registerSod(key,url)

As stated and explained before, this just registers a script to wait around to reinforce your code when needed. Key is just a name to give the script you are loading for future referencing. The URL can be absolute or relative. In cases where you are not using /_layouts you are likely using the Style Library. Here you could reference the style library best by using _spPageContextInfo to help avoid relative or hardcoded URLS. For example

//_spPageContextInfo.siteServerRelativeUrl returns the site collection relative root. I.e. if I was on contoso.com/sites/mike, it would be set to /sites/mike
SP.SOD.registerSod("script.js",_spPageContextInfo.siteServerRelativeUrl + "/Style Library/MikesFiles/script.js");

registerSodDep(key,dep)

registerSodDep simply tells SOD “Hey, this one script that I already registered HAS to be run before another script I registered… so please run the former before running the latter”. If, say, your script requires use of jQuery’s fancy dollar sign:

SP.SOD.registerSod("script.js", "/Style Library/Mikes Files/script.js"); //This uses $.getScript -- we need to make sure jQuery is loaded first!
SP.SOD.registerSod("jquery.js", "/Style Library/Mikes Files/jquery.js");

//Tell SOD to load jquery.js first if anyone tries to use script.js!
SP.SOD.registerSodDep("script.js", "jquery.js");

//{...later on a user clicks a button or a webpart was added to the page which uses script.js}
function someButtonClicked(){
	SP.SOD.executeFunc('script.js', null, function(){ 
		//This will run, but not until SP loads its jquery dependency!
		MikesJqueryGetScriptFromScriptJs();
	}
}

Just in case anyone is wondering why we couldn’t just do something like this:

SP.SOD.registerSod("script.js", "/Style Library/Mikes Files/script.js");
SP.SOD.registerSod("jquery.js", "/Style Library/Mikes Files/jquery.js");

//User clicked something that needs to run MikesJqueryGetScriptFromScriptJs()
function someButtonClicked(){
	SP.SOD.executeFunc('jquery.js', null, null);
	SP.SOD.executeFunc('script.js', null, MikesJqueryGetScriptFromScriptJs);
	//Error! $.getScript is not recognized as a function!
}

Being asynchronous the script.js could have loaded before jquery and run before $.getScript existed! You could however chain the executeFuncs like so if you really wanted and not use registerSodDep:

SP.SOD.executeFunc('jquery.js', null, function(){
   SP.SOD.executeFunc('script.js', null, MikesJqueryGetScriptFromScriptJs);
});

executeFunc(key, functionName, fn)

As seen before, this causes SOD to fetch your JS file (referenced by key which points to a specific registerSod). Also, the last parameter fn is simply a callback to allow you to run code once the file has loaded. functionName is a bit of an odd one however. I’ll explain how the code reacts, and its up to you how you want to use this.

functionName will typically be either null, or ‘someFunctionName’. If you leave this as null, the callback will still run, but only once. That means if you, or SharePoint have loaded the script elsewhere, and you have an executeFunc in some other JS file elsewhere with functionName set to null, its callback will not run. If however you set functionName to a valid function, it will always call back, even if the file is loaded. For now that is enough to satisfy my curiosity, but one day I may have to pry inside to see what is actually happening.

script.js

function MikesFunction(){
   alert("Ran");
}

Elsewhere


function IsItCallingBack(){
   console.log("Called back!");
}

registerSod("script.js", "/Style Library/Mikes Files/script.js");
SP.SOD.executeFunc('script.js', null, IsItCallingBack);            //Called back!
SP.SOD.executeFunc('script.js', 'MikesFunction', IsItCallingBack); //Called back!
SP.SOD.executeFunc('script.js', null, IsItCallingBack);
SP.SOD.executeFunc('script.js', 'MikesFunction', IsItCallingBack); //Called back!
//Lets try calling a function name that doesn't exist in MikesFunction
SP.SOD.executeFunc('script.js', 'NoSuchFunction', IsItCallingBack);

//NOTE: Running the initial call with functionName set to MikesFunction, then the second was one with null provided, that null call would not generate a callback. 

Due to this, its almost always better to supply a function name as you may have other scripts with similar code that would also load (executeFunc) the same file beforehand.

execute(key, functionName, args)

This is much like executeFunc however the difference is that this actually appears to run the function inside the file being loaded, as well as being able to pass in parameters to that function. Here’s an example:

script.js

function MikesFunction(args){
   alert("Hi " + args.myName + "!");
}

Elswhere

registerSod("script.js", "/Style Library/Mikes Files/script.js");
var args = { "myName":"Mike"};
SP.SOD.execute(key, MikesFunction, args); //alert: Hi Mike!

But Wait, There’s More!

What you have reviewed above should be enough to get most developers in the right mindset and to get going. The easy stuff is done, but there are a few more methods which will help coordinate your script.

Coming soon! This ate up my night and there is just too much more goodness to come.

 

No Comments

There are no comments related to this article.

Leave a Reply

Your email address will not be published. Required fields are marked *