Oh well, MDS is a beast on first contact.
Here are 2 things to keep in mind when working with MDS:
- Protect your (and your libraries) NameSpace
MDS is cleaning the window
object on every MDS-Navigation. This will cause your global values to get cleaned by their garbage collector.
You can Protect a namespace like this:
// for libraries
// 1. Ensure the library has defined its global objects
// 2. Call this code
if (window.Function != 'undefined' && typeof (Function.registerNamespace) == 'function') {
Function.registerNamespace('jQuery');
Function.registerNamespace('$');
Function.registerNamespace('etc');
}
// for your code
// use an IIFE to create a scope and dont dirty the global scope
(function (_) {
// _ is our object we use to put our code in
// use strict to ensure we dont code stupid
'use strict';
// implement your code like this
var yourFunctionPublic = function () {
//code
console.log('yourFunctionPublic');
//you can call internal code here
yourFunctionInternal();
};
var yourFunctionInternal = function () {
//code
console.log('yourFunctionInternal');
};
// every function / variable that should be callable from public you have to publish like below
_.YourFunction = yourFunctionPublic;
// attach our object at the namespace we want (this should be unique)
// THIS WILL PROTECT YOUR GLOBAL VAR FROM THE GARBAGE COLLECTOR
window.YourNameSpace = _;
// protect our namespace by registering it
// this must be done after defining the target object
if (window.Function != 'undefined' && typeof (Function.registerNamespace) == 'function') {
Function.registerNamespace('YourNameSpace');
}
})({});
// call your public functions / variables
window.YourNameSpace.YourFunction();
- Attach to the MDS-Lifecycle
Since you don't have a true reload, you won't have new events (load, etc). If you want your code to run after a navigation (even a MDS one), here's a little utility and how you can attach (the last lines show how to use it):
//use an IIFE to create a scope and dont dirty the global scope
(function (_) {
// use strict to ensure we dont code stupid
'use strict';
var initHandlers = [];
var initMDSHandlers = [];
var ensureSharePoint = function (handler) {
var sodLoaded = typeof (_v_dictSod) !== 'undefined' && _v_dictSod['sp.js'] != null && _v_dictSod['sp.js'].state === Sods.loaded;
if (sodLoaded) {
handler();
} else {
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', function () { });
SP.SOD.executeOrDelayUntilScriptLoaded(handler, 'sp.js');
}
};
var initMDS = function () {
for (var i = 0; i < initMDSHandlers.length; i++) {
initMDSHandlers[i]();
}
};
var init = function () {
// Register MDS handler
if ('undefined' != typeof g_MinimalDownload && g_MinimalDownload && (window.location.pathname.toLowerCase()).endsWith('/_layouts/15/start.aspx') && 'undefined' != typeof asyncDeltaManager) {
asyncDeltaManager.add_endRequest(initMDS);
} else {
for (var i = 0; i < initHandlers.length; i++) {
initHandlers[i]();
}
}
};
var registerInit = function (handler) {
initHandlers.push(handler);
};
var registerInitMDS = function (handler) {
initMDSHandlers.push(handler);
};
var domReady = (function (handler) {
var fns = [];
var listener;
var loaded = (document.documentElement.doScroll ? /^loaded|^c/ : /^loaded|^i|^c/).test(document.readyState);
if (!loaded) {
document.addEventListener('DOMContentLoaded', listener = function () {
document.removeEventListener('DOMContentLoaded', listener);
loaded = 1;
while (listener = fns.shift()) listener();
});
}
return function (fn) {
loaded ? setTimeout(fn, 0) : fns.push(fn);
};
})();
var attachToLoad = function (functionToAttach) {
registerInit(functionToAttach);
registerInitMDS(functionToAttach);
domReady(function () {
init();
});
};
_.AttachToLoad = attachToLoad;
// THIS WILL PROTECT YOUR GLOBAL VAR FROM THE GARBAGE COLLECTOR
window.LifeCycleHelper = _;
if (window.Function != 'undefined' && typeof (Function.registerNamespace) == 'function') {
Function.registerNamespace('LifeCycleHelper');
}
})({});
// call your public functions / variables
var theCodeYouWantToRun = function () {
alert('theCodeYouWantToRun');
};
window.LifeCycleHelper.AttachToLoad(theCodeYouWantToRun);