Friday, November 7, 2014

Delay loading javascript in sharepoint


SharePoint 2013 Lazy loading Javascript

In SharePoint 2013 the Javascript loading mechanism seems to be changed a bit. When porting a SharePoint 2010 solution to 2013 I found out that sometimes some weird script errors where occuring when calling SharePoint Javascript libraries. On some pages in SharePoint 2013 it happens that not all SharePoint Javascript libraries are loaded because of the built-in lazy loading mechanism. This reduces bandwidth when loading pages, because no unneeded libraries are downloaded to the client. But this causes issues when you want to use not loaded libraries. The following sample Javascript codes shows how you can load some Javascript Libraries and then automatically call your function where you want to use those libraries:
1//Register SOD's
2SP.SOD.registerSod('core.js''\u002f_layouts\u002fcore.js');
3SP.SOD.executeFunc('core.js'falsefunction(){});;
4SP.SOD.registerSod('sp.js''\u002f_layouts\u002fsp.js');
5SP.SOD.executeFunc('sp.js'falsefunction(){});
6SP.SOD.registerSod('sp.core.js''\u002f_layouts\u002fsp.core.js');
7SP.SOD.executeFunc('sp.core.js'falsefunction(){});
8 
9function doSomething() {
10   //Your Logic here which calls sp core libraries
11}
12 
13// Load asynchronous all needed libraries
14ExecuteOrDelayUntilScriptLoaded(function() { ExecuteOrDelayUntilScriptLoaded(doSomething, 'sp.core.js') }, 'sp.js');
In the example above we’re using the SP.SOD library provided with SharePoint. Those existed already in SharePoint 2010 and are still present in 2013. With the SOD library it is possible to lazy load Javascript files and load them on the moment you need them. The sample script exists of three parts. In the first step we register Sod’s (Script on Demand) where we define a key and as value the relative path to the Javascript file. We also call executeFunc to load the file using a dummy function. In the second step we create a custom function. This is the function where you want to call specific methods in the Javascript libraries loaded. Then we call ExecuteOrDelayUntilScriptLoaded. Because in this sample we want both sp.core.js and sp.js loaded, we nest it with another call to ExecuteOrDelayUntilScriptLoaded and finally let the callback call the function which needs the libraries loaded before executing. This method of loading scripts seems to work well in SharePoint 2013 and can also be used for other OOTB Libraries, like the Taxonomy Javascript Library. When your site is still running in SharePoint 2010 mode however, this doesn’t work properly. The registering of Sod’s seems to break with the 2010 way of loading the OOTB Javascript files so there you need only the ExecuteOrDelayUntilScriptLoaded calls. If you need to detect the mode in Javascript you can use the SP.Site.get_compatibilityLevel() function to retrieve that info using JSOM and then dynamically decide which method of loading to use.


SP.js not load on Sharepoint Pages

There is an issue I found when working on Sharepoint 2013. I got “SP.js” error right after published the page and cause some feature disable, such as ribbon and other scripts can not be executed. It seems that SP.js does not load properly for publishing page and anonymous users. Some articles said that sharepoint loads certain javascript files when it needs, after published the page, ribbon will close and several javasripts were unload automatically.
Actually Sharepoint provides some methods to call scripts for many conditions. So we could choose wisely among them to solved our problem. Take a look :
1) Script on Demand.
1
2
3
4
function sharePointReady(){
    // call this code after load SP.js
}
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', sharePointReady);
2) Delay until SP.js loaded.
1
2
3
4
function stuffToRun(){
   // code
}
ExecuteOrDelayUntilScriptLoaded(stuffToRun, "sp.js")
The different between 1 and 2:
SP.SOD.executeFunc(key, functionName, fn) method ensures that the specified file (key) that contains the specified function (functionName) is loaded and then runs the specified callback function (fn). Use case for “SP.SOD.executeFunc” can be that at some point you wish a JavaScript library like JQuery be loaded before you call a function defined inside it via callback function.
SP.SOD.executeOrDelayUntilScriptLoaded(func, depScriptFileName) method executes the specified function (func) if the file (depScriptFileName) containing it is loaded; otherwise, adds it to the pending job queue.
3) Load after other stuff finished.
1
2
3
4
function runAfterEverythingElse(){
    // code
}
_spBodyOnLoadFunctionNames.push("runAfterEverythingElse");
from m


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 ScriptsODemand) 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:

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

True will render:
?
1
<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 asSP.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 callednotifyScriptLoadedAndExecuteWaitingJobs(scriptName). All SharePoint built-in scripts will call notifyScriptLoadedAndExecuteWaitingJobswhen 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
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<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
?
1
2
3
4
5
6
7
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.
?
1
<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
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<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
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<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
?
1
2
3
4
5
6
7
8
alert('4');
 
function sayHello() {
alert('5');
sayHello2();
}
 
SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("my_script.js");
My_Script2.js
?
1
2
3
4
5
6
7
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).
var clientContext;
var website;

// Make sure the SharePoint script file 'sp.js' is loaded before your
// code runs.
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', sharePointReady);

// Create an instance of the current context.
function sharePointReady() {
    clientContext = SP.ClientContext.get_current();
    website = clientContext.get_web();

    clientContext.load(website);
    clientContext.executeQueryAsync(onRequestSucceeded, onRequestFailed);
}
function onRequestSucceeded() {
    alert(website.get_url());
}
function onRequestFailed(sender, args) {
    alert('Error: ' + args.get_message());
}
It seems in SP 2013 calling ExecuteOrDelayUntilScriptLoaded() doesnt work on published pages.
In SP2013 this is the correct way to do it according to MS @ http://msdn.microsoft.com/en-us/library/jj245759.aspx
// Make sure the SharePoint script file 'sp.js' is loaded before your // code runs.
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', startIt);

2 comments:

  1. This is realy a Nice blog post read on of my blogs It is really helpful article please read it too my blog Pinterest Button Not Working. you can visits our websites or toll free no +1-866-558-4555. solve your problem fastly.

    ReplyDelete
  2. Such a nice blog Thanks for sharing information. It is really helpful article please read it to my blog Pinterest button not working.

    ReplyDelete