0

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");
  • Have you tried also using RegisterModuleInit()? I'm thinking hypothetically the MDS engine could be registering your code as "already executed" when it loads, even though you haven't explicitly called it yet, so by the time it gets actually called by _spBodyOnLoad, the MDS engine negates the call. – Dylan Cristy Dec 17 '15 at 14:22
2

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.

1

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.

-1

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.

  • Manually adding a <script> tag to any page with MDS enabled will break MDS - forcing a reload of the page at it's non MDS URL (i.e. not //site/start.aspx#/pagepath/) – John-M Dec 17 '15 at 17:08
  • Ah, okay, yes i did notice that and figured that was the case. I'm assuming it only breaks it for that element though? So in theory, if i wanted MDS on the site but was okay if it didn't function on say one view, since I have a custom script running there, i could just add the attributes and override it on that view only? I'm rather new to MDS so just curious. I'm mostly thinking in terms of using content editor and/or script editor web parts on views or forms. – Nate Dec 17 '15 at 17:15
  • That would give the behavior you describe (a single page where MDS doesn't work) but this will give users a generally bad experience: when you have a page that breaks MDS the page will try to load running under MDS, realize it cant, then re-load the entire page -- which can take a significant amount of time .I really don't recommend doing things that way, but you could :) – John-M Dec 17 '15 at 17:23
  • Good to know. Thanks for the info! I'll look through your suggestions and and try to implement. – Nate Dec 17 '15 at 17:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.