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");
Was it helpful?

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:

  1. 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)
  2. RegisterBeginEndFunctions(script, tag, beginFunc, endFunc, loadFunc) (RegisterModuleInit wraps this function, simply providing the beginFunc)
  3. Attaching your code to the MDS events: initializeRequest, beginRequest, and endRequest -- using the asyncDeltaManager objects corresponding functions (add_initializeRequest, add_beginRequest, and add_endRequest). Make sure you check for and handle if the asyncDeltaManager 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.

Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top