SharePoint 2016:尽管 Ajax and/or MDS,如何强制 JS 在每个网站页面上执行?

SharePoint 2016: how to force JS to execute on every site page despite Ajax and/or MDS?

我正在实现一个非常简单的用例,但我不仅没有找到解决方案,而且我找不到任何谈论它的文章,好像我是唯一的。

我希望我的自定义 Javascript 在给定 SharePoint 网站的每个页面 上执行。

容易,你会说。好吧,不。远非如此,就像 SharePoint 一样。

Steps to reproduce :

  • Create a out-of-the-box publishing site
  • Include the custom javascript below using any of the means I describe below
  • Go to the site, to the home page. It's a publishing site, so by default you should have the left navigation pane with at least "Home" and "Documents" by default.
  • The first time you load the page, the javascript executes. Now, click on "documents". The page changes but the Javascript is not executed.

那是因为 SharePoint 使用 Ajax。即使 MDS 被禁用。它通过 URL 中的散列 (#) 使用 Ajax。

例如,它转换一个非常无害的 link 像这样:

< a href src="/SitePages/Home.aspx">

单击此 URL 时:

https://your-url/sites/your-site/_layouts/15/start.aspx#/SitePages/Home.aspx

这是我的Javascript:

if (ExecuteOrDelayUntilScriptLoaded && _spBodyOnLoadFunctionNames) {
    _spBodyOnLoadFunctionNames.push(ExecuteOrDelayUntilScriptLoaded(
        function () {
            alert("It's working!");
        }, "sp.js"));
} 

所以,我尝试了以下包含 Javascript 的方法:

  1. 通过用户自定义操作。 我已经使用 this very handy page 添加它,但这无关紧要。该操作已添加到站点,我可以在第一次加载时在 DOM 中看到 JS。但是后来我在页面中点击了一个link之后,SP使用了Ajax之后,就没有再执行了
  2. 通过修改母版页——即:seattle.html。起初我以这种方式包含它,只是在其他本地包含物下:

<head runat="server">

...

<!--SPM:<SharePoint:ScriptLink language="javascript" name="suitelinks.js" OnDemand="true" runat="server" Localizable="false"/>-->

<!--SPM:<SharePoint:ScriptLink language="javascript" Name="~sitecollection/SiteAssets/MYJAVASCRIPT.js" runat="server"/>-->

但后来我阅读了有关 AjaxDelta(此处:https://msdn.microsoft.com/fr-fr/library/office/dn456543.aspx)的内容,然后我移动了我的收录(仍在 header ) 到 < AjaxDelta >,像这样:

<head runat="server">

...

<!--SPM:<SharePoint:AjaxDelta id="DeltaPlaceHolderAdditionalPageHead" Container="false" runat="server">-->

<!--SPM:<asp:ContentPlaceHolder id="PlaceHolderAdditionalPageHead" runat="server"/>-->

<!--SPM:<SharePoint:DelegateControl runat="server" ControlId="AdditionalPageHead" AllowMultipleControls="true"/>-->

<!--SPM:<SharePoint:ScriptLink language="javascript" Name="~sitecollection/SiteAssets/MYJAVASCRIPT.js" runat="server"/>-->

<!--SPM:</SharePoint:AjaxDelta>-->

...但没有任何效果。通过单击 SharePoint 的 "managed" links.

在同一站点的页面之间切换时,永远不会执行 Javascript

我正在寻找一种能够优雅地处理 SharePoint 的 Ajax 的解决方案,而不是劫持页面上每个 hyperlink 的繁重和冒险的东西。例如,我试图将我的代码挂接到 ajaxNavigate 方法(例如:addNavigate),但我不确定我是否了解实际情况在那里,如果它对我有任何帮助。

编辑:

我非常困惑。我认为当用户单击 link 然后单击“back”时,这两个解决方案确实解决了导航问题。但它没有解决 SharePoint 的聪明 "managed"、Ajax-riddled、hyperlinks.

我终于找到了一个迄今为止似乎从未失败过的解决方案。这真是一种解脱。

简答:使用asyncDeltaManager.add_endRequest

这个 MSDN 讨论提出了一种简单的实现方法: https://social.msdn.microsoft.com/Forums/office/en-US/1ae292b4-3589-46f6-bedc-7bd9dc741f1b/javascript-code-to-execute-after-all-the-elements-and-css-are-loaded?forum=appsforsharepoint

$(function () {
  ExecuteOrDelayUntilScriptLoaded(function () {
      if (typeof asyncDeltaManager != "undefined")
           asyncDeltaManager.add_endRequest(MYCUSTOMCODE); //execute it after any ajax event
      else 
           MYCUSTOMCODE(); //execute it at first load
  }, "start.js");
});

这显示了如何将其正确包含在 SharePoint 的周期中(使用 ExecuteOrDelayUntilScriptLoaded ) https://sharepoint.stackexchange.com/questions/171490/javacript-only-executed-on-first-page-load

Full-blown 解决方案 (objet "LefeCycleHelper"), by Mx https://sharepoint.stackexchange.com/questions/192974/where-to-place-a-js-script-with-whom-i-need-to-get-an-div-id/193009#193009

//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');
}
})({});

var theCodeYouWantToRun = function () {
    alert('theCodeYouWantToRun');
};

window.LifeCycleHelper.AttachToLoad(theCodeYouWantToRun);