Issues with Minimal Download Strategy and _spBodyOnLoadFunctionNames.push
-
06-10-2020 - |
Question
I noticed that on the sites that have MDS activated, functions that I queue up with _spBodyOnLoadFunctionNames.push
do not get executed. I can step through my code and I see _spBodyOnLoadFunctionNames.push
being run; no errors before or after but my function is never called.
A cursory search on the internet seems to indication that my code should work. However, when I turn MDS on the following code displays nothing. When MDS is off it displays the alert. I would like to keep MDS on. Does anyone have an idea why this is not working?
function test(){
alert('hi');
}
_spBodyOnLoadFunctionNames.push("test");
Solution 2
I wish there was a more elegant solution but this is what I came up with (look for Final Answer).
I tried RegisterModuleInit it works if I can hard code the path. However, this throws and error: spPageContextInfo undefined. I see many examples claiming that it works but not for me. Possibly an important factor is that my ScriptLink is on the Master Page; I don't know.
RegisterModuleInit(_spPageContextInfo.siteServerRelativeUrl + "/Scripts/SPSplash.js", myFunction.Start);
This doesn't work; MDS thinks there is no code to run.
MyFunction = function(){
function modalCreate(){
options = {...};
SP.SOD.execute('sp.ui.dialog.js', 'SP.UI.ModalDialog.showModalDialog', options);
}
return{
start(){
modalCreate();
}
};
}
_spBodyOnLoadFunctionNames.push("MyFunction.start");
This is better... in the sense that MDS lets my script load; I'm not happy about putting out a globally available function that's sole purpose is to trick MDS. MyFunction.start gets called but the SP.SOD.execute doesn't work, despite sp.ui.dialog.js obviously having been loaded; after the page finishes loading I can manually call showModalDialog.
MyFunction = function(){
function modalCreate(){
options = {...};
SP.SOD.execute('sp.ui.dialog.js', 'SP.UI.ModalDialog.showModalDialog', options);
}
return{
start(){
modalCreate();
}
};
}
// kludge - start
function Dummy(){
var kludge = 'Without this function, MDS thinks there is no code here.';
}
_spBodyOnLoadFunctionNames.push("Dummy");
// kludge - end
_spBodyOnLoadFunctionNames.push("MyFunction.start");
Final Answer: Because SP.UI.ModalDialog isn't available when I run this, I used setTimeout to keep checking for it.
MyFunction = function(){
function modalCreate(){
options = {...};
SP.SOD.execute('sp.ui.dialog.js', 'SP.UI.ModalDialog.showModalDialog', options);
}
// wait for SP.UI.ModalDialog - start
function modalCreate_helper(options){
var delay = 10;
if(SP != null
&& SP.UI != null
&& SP.UI.ModalDialog != null
){
SP.UI.ModalDialog.showModalDialog(options);
}else{
window.setTimeout(
function(){
modalCreate_helper(options);
}
, delay
);
}
}
// wait for SP.UI.ModalDialog - end
return{
start(){
modalCreate();
}
};
}
// kludge - start
function Dummy(){
var kludge = 'Without this function, MDS thinks there is no code here.';
}
_spBodyOnLoadFunctionNames.push("Dummy");
// kludge - end
_spBodyOnLoadFunctionNames.push("MyFunction.start");
Again, not super happy with this solution but it works. If someone can tell me why my Script loads before SP.UI.ModalDialog is ready, I would like to know.
OTHER TIPS
On sites that use MDS, simply adding a function into _spBodyOnLoadFunctionNames
is not enough to make it execute each time the page is (partially) re-loaded. This is because MDS doesn't do a full page load when moving from one MDS enabled page to another, so the events that cause things like jQuery.ready
and _spBodyOnLoadFunctionNames
to work are not available.
There are a few ways I can think of to make sure that your script will run under MDS, these are mostly repeats of an answer I gave to a related question but with a different scenario for how they were trying to call their functions:
First, you should load your scripts using the ScriptLink or ScriptBlock controls
<SharePoint:ScriptLink language="javascript" name="~site/SiteAssets/testModule.js" runat="server" />
Note that the 'name' property is the path to the file using a URL token (more info on those can be found in this msdn article). Without the URL token, the ScriptLink control will want to look in _layouts
and your local language folder.
or you can use a ScriptBlock:
<SharePoint:ScriptBlock runat="server" >
// Your JavaScript code here.
</SharePoint:ScriptBlock>
Or you could also set up a custom action to look in ScriptLink and run JS that way across your entire site -- you can see how you could implement something like this using this MSDN article about custom actions in add-ins or doing things a bit more programatically with UserCustomActions
using a sample from the OfficeDev Patterns and Practices repo.
As an aside -- in your JavaScript within your ScriptLink or ScriptBlock controls you can use dynamic script loaders such as SharePoint's own SOD
framework or jQuery's jQuery.getScript
but nothing that loads scripts via creating script
tags (such as require.js) since script
tags will break MDS and make pages load even slower than if you had the feature turned off.
Next you'll need to register your function with MDS using one of a few techniques:
RegisterModuleInit(scriptFileName, initFunc)
(see my other linked answer for an example as well as a more thorough explanation from Wictor Wilen and an excellent article on the SharePoint Developer Support Team Blog)RegisterBeginEndFunctions(script, tag, beginFunc, endFunc, loadFunc)
(RegisterModuleInit
wraps this function, simply providing thebeginFunc
)- Attaching your code to the MDS events:
initializeRequest
,beginRequest
, andendRequest
-- using theasyncDeltaManager
objects corresponding functions (add_initializeRequest
,add_beginRequest
, andadd_endRequest
). Make sure you check for and handle if theasyncDeltaManager
is unavailable, as this means you're not in an MDS page load scenario
At any rate, MDS programming in JavaScript isn't quite as easy as it could be -- partially because there aren't many great resources or how-tos out there... hopefully this helps steer you in the right direction but you'll ultimately still have some more work to do to get things implemented in your situation.
Are you including language="javascript" type="text/javascript" attributes in your script tags? I've noticed this behavior in MDS sites when using script and/or content editor web parts. Usually once i add those attributes the script starts to execute.