Aug 20, 2010

SharePoint Scripts On Demand (SP.SOD)

When you are working with Ribbon UI extensions, Client OM or with complex SharePoint 2010 java scripts, you may noticed a Java Script class called SP.SOD. Built-in SharePoint scripts use this class extensively. Unfortunately SP.SOD (I guess it stands for SharePoint Scripts On Demand) is very rarely documented – until yet :)

SharePoint 2010 makes extensive use of java scripts. These scripts are located in several different files. And commonly these scripts have dependencies to functions and classes defined in other script files. Means the built-in SharePoint scripts and for sure your own scripts have to care about when which script will be loaded. And exactly this is the job of SP.SOD class! SP.SOD is defined in init.js and will be load in the HTML head section automatically.

ScriptLink

SP.SOD works tightly together with the ScriptLink server control. ScriptLink is a SharePoint server control for registering java script files.
<SharePoint:ScriptLink ID="scriptLink1" runat="server" Name="My_Script.js" LoadAfterUI="true" OnDemand="false" />

In the “Name” property you should enter the name of the script file. The control assumes that the script is located in the layout’s root folder. You could also use a relative URL instead of the script name, but cause problems with OnDemand=true. The intention is definitely to use the script name. Putting your scripts directly into the layouts folder without a solution/feature specific subfolder seems to be against the best practice, but is required! I recommend using solution/feature prefix for the files instead of a folder.

Localizable” true instructs the control to search the script in the language specific subfolders of the layout’s folder (e.g. _layouts/1033/MyScript.js).

LoadAfterUI” determines where the script reference will be rendered in the HTML document. False inserts the reference into the HTML head. True inserts the reference at the end of the HTML body.

OnDemand” true specifies that the script should be loaded on demand. False will render the following script code:

<script type="text/javascript">
// <![CDATA[
document.write('<script type="text/javascript" src="/_layouts/my_script.js"></' + 'script>');
// ]]>
</script>

True will render:
<script type="text/javascript">RegisterSod("my_script.js", "\u002f_layouts\u002fmy_script.js");</script>

As you see “true” doesn’t insert the script reference directly, instead it calls the function RegistedSod. RegisterSod is the same as SP.SOD.registerSod(key, url). The script will not be loaded until it get explicitly requested (see SP.SOD.executeFunc)!

Instead of using SPScriptLink in a declarative way to register scripts you could also do this with some static methods of SPScriptLink in the code behind:


SP.SOD.executeOrDelayUntilScriptLoaded

SP.SOD.executeOrDelayUntilScriptLoaded(func, scriptName) schedules an asynchronous callback function (func) which will be called when the script has signaled finished loading. Signaled finished loading means that the script has called notifyScriptLoadedAndExecuteWaitingJobs(scriptName). All SharePoint built-in scripts will call notifyScriptLoadedAndExecuteWaitingJobs when they have finished loading. ExcuteOrDelayUntilScriptLoaded does not trigger loading an on demand script (SOD)!

There is a very similar function pair called executeOrDelayUntilEventNotified/notifyEventAndExecuteWaitingJobs. The only diffrence is that  the executeOrDelayUntilScriptLoaded/executeOrDelayUntilScriptLoaded function pair prefixes internally the scriptName with “sp.scriptloaded-“.

Example 1:

My_ApplicationPage.aspx
<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<SharePoint:ScriptLink ID="sl" runat="server" Name="My_Script.js" LoadAfterUI="true" OnDemand="false" />
<script type="text/javascript" language="javascript">

function myCallback() {
//sayHello is defined in MyScript.js
sayHello();
}

alert('1');

ExecuteOrDelayUntilScriptLoaded(myCallback, "my_script.js");

alert('2');

</script>
</asp:Content>

My_Script.js
alert('3');

function sayHello() {
alert('4');
}

SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("my_script.js");

We have defined a script link for My_Script.js which will be inserted at the end of the HTML body (LoadAfterUI=true). In the inline script in the head we have registered the function myCallback to get executed when MyScript.js has signaled finished loading. The alerts will popup in the sequence 1-2-3-4. You could also wait for any built-in script to have finished loading e.g. executeOrDelayUntilScriptLoaded(myCallback, “sp.ui.js”).

You could also use the static method ScriptLink.RegisterDelayedExecutionScript of SPScriptLink to register a delayed script execution (instead of the line: ExecuteOrDelayUntilScriptLoaded(myCallback, "my_script.js");

SP.SOD.registerSod

With SP.SOD.registerSod(key, url) you can manual register an on demand script via JavaScript.
<script type='text/javascript'>RegisterSod('my_script.js', '/_layouts/my_script.js'); </script>

SP.SOD.executeFunc

SP.SOD.executeFunc(key, functionName, fn) is used to load on demand scripts (ScriptLink.OnDemand=true).
The “key” parameter must match to the ScriptLink’s Name property (Use small letters for key, because an issue with string normalizing in RegisterSodDep).

functionName” awaits a type name of an ASP.NET AJAX JavaScript class. ExecuteFunc first checks if the AJAX class has already been registered, when not it checks additionally if a SOD with this key has already been loaded and finally it will load the SOD. Load means adding dynamically the corresponding script tag to the HTML head. The check for the type helps to ensure that the script has not been already loaded via an usual script tag before. When you don’t want to work with AJAX JavaScript classes you can use “null” value for the functionName.

fn defines a callback that will be executed when the SOD has signaled finished loading.

Example 2:

My_ApplicationPage.aspx
<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<SharePoint:ScriptLink ID="sl" runat="server" Name="My_Script.js" LoadAfterUI="true" OnDemand="false" />
<!-- RegisterSod('my_script.js', '/_layouts/My_Script.js'); -->

<script type="text/javascript" language="javascript">

function myCallback() {
sayHello();       
}

alert('1');

SP.SOD.executeFunc("my_script.js", null, myCallback);

alert('2');

</script>
</asp:Content>

My_Script.js keeps unchanged.

The example is similar to example 1. The difference is that ScriptLink registers a SOD (ScriptLink.OnDemand=true) script. MyScript.js will not be loaded until it will be explicitly demanded with “SP.SOD.executeFunc(‘my_script.js’);”. When the SOD has signaled finished loading with “SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs(‘my_script.js’);”  myCallback will be executed. The alerts will popup in the sequence 1-2-3-4 again.

SP.SOD.registerSodDep

SP.SOD.registerSodDep(key, dep) can register SOD dependency. Key defines the SOD that depends on the SOD defined in dep. When the SOD in key will be requested, the SOD dependency will ensure that the SOD defined in dep will loaded before the actual SOD defined in key. You can also define a chain of dependencyies. This means the SOD in dep can also have dependencies, and the dependencies can have dependencies too and so on. The SOD loading mechanism keeps care to resolve all the required dependencies.

Example 3:

My_ApplicationPage.aspx
<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<script type="text/javascript" language="javascript">
function myCallback() {
sayHello();       
}

alert('1');

function myInit(){
SP.SOD.executeFunc("my_script.js", null, myCallback);
alert('2');
}

_spBodyOnLoadFunctionNames.push("myInit");

RegisterSod('my_script.js', '/_layouts/My_Script.js');
RegisterSod('my_script2.js', '/_layouts/My_Script2.js');

RegisterSodDep('my_script.js', 'my_script2.js');

</script>
</asp:Content>


My_Script.js
alert('4');

function sayHello() {
alert('5');
sayHello2();
}

SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("my_script.js");
My_Script2.js
alert('3');

function sayHello2() {
alert('6');
}

SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("my_script2.js");

The example registers two SODs via JavaScript (you could do this with the ScriptLink control as well) and defines that my_script.js depends on my_script2.js. When my_script.js will be requested via “SP.SOD.executeFunc(‘my_script.js’, null, myCallback);” SP.SOD will first load my_script2.js and then my_script.js. The alerts will popup in the sequence 1-2-3-4-5-6.

Summary

The idea of on demand script loading make really sense. SharePoint 2010 loads really a lot of JavaScripts - this takes time! So the idea is: first load the HTML and let it render by the browser so that the user is able to read the requested information as fast as possible. And in the second step load the behavior (the JavaScripts).

16 comments:

tenax_technologies said...
This comment has been removed by a blog administrator.
Steve Saunders said...

Note that SP.SOD is documented in the SharePoint Foundation SDK, including a code example:

http://msdn.microsoft.com/en-us/library/ff410742.aspx

Travis Lingenfelder said...

I found that you *CAN* place your script files in a subfolder of the layout folder. For example, if I had a script located at: .../14/TEMPLATE/LAYOUTS/MyCompany/Scripts/MyScript.js, the ScriptLink control would be: <SharePoint:ScriptLink ID="ScriptLink1" language="javascript" name="MyCompany/Scripts/MyScript.js" LoadAfterUI="true" Localizable="false" runat="server"/>. Without the Localizable="false" the server would look for a script at: .../14/TEMPLATE/LAYOUTS/1033/MyCompany/Scripts/MyScript.js, which does not exist and throws an exception. Adding Localizable="false" fixes the issue.

Christian said...

Hi Travis,
sure you can. But you could get into trouble when you use SOD dependencies. However, keep in mind that SP.SOD seems not to be designed for the usage of subfolders, expects of the language specific foldes.
Bye, Christian

Abe Miessler said...

Very nice post. Microsoft does not provide very good documentation on this stuff. Nice to see how/where/where everything is rendered.

Gaurav said...

Great post and a very good summary of scriptlink and scripts on demand!

Anonymous said...

Great Post

Jorge said...

Nice post!!

Colin Thornton said...

Hi Christian, this post is awesome. Few years old now but still providing excellent and relevant information.
I am struggling getting SharePoint to call SP.SOD.executeOrDelayUntilScriptLoaded twice on a single page (separate Script Editor web parts in SP13).
I have tried waiting for notification from different, dependant SODs like:

SP.SOD.executeOrDelayUntilScriptLoaded(myfunc, 'sp.ui.dialog.js');

in the first web part and then:

SP.SOD.executeOrDelayUntilScriptLoaded(myotherfunc, 'sp.js');
in the second.
Not working for me.
Do you have any pointers here?
Thanks again for the great post!

basma gaber said...

شركة تنظيف خزانات بالدمام

شركة تنظيف بيارات بالدمام

شركة تنظيف منازل بالمدينة

شركة تنظيف منازل بالدمام

نقل عفش بجدة

شركه نقل اثاث بمكة

شركة نقل اثاث بالدمام

شركة تنظيف منازل بالمدينة المنورة

شركات عزل مائي

شركة مكافحة حشرات جدة

تنظيف فلل بالدمام

شركات مكافحة الحشرات فى الرياض

افضل شركة تنظيف بالخرج




here

here

here

here

here

westerdaled said...

Hi

I am having issues similar to Colin. I noticed that some of my SP2013 pages are firing the SP.SOD.executeOrDelayUntilScriptLoaded(myotherfunc, 'sp.js'); . Weirdly, they all seem to work when I check a page out but stop when I publish

I have had a look on Firebug and I can't see sp.js loaded in certain circumstances.
Anyone seen this.

digital signature Microsoft said...

Grateful to check out your website, I seem to be ahead to more excellent sites and I wish that you wrote more informative post for us. Well done work.

Staygreen Academy said...

Attractive information, I had come to know about your blog from my friend, and let me tell you, your website gives the best and the most interesting information. Regards, SharePoint Administration Training in Hyderabad

Rakesh Parval said...

Really good piece of information, I had come to know about your site from my friend, and let me tell you, your site gives the best and the most interesting information. Regards, SharePoint Developer Training in Hyderabad

Manideep Agarwal said...

Awesome information, I had come to know about your website from my friend, and let me tell you, your blog gives the best and the most interesting information. Regards, SharePoint Online Training in Hyderabad

Kiran Joshya said...

Interesting information, I had come to know about your web-page from my friend, and let me tell you, your blog gives the best and the most interesting information. Regards, SharePoint Training Institutes in Hyderabad