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

Posted

技术标签:

【中文标题】SharePoint 2016:尽管有 Ajax 和/或 MDS,如何强制 JS 在每个网站页面上执行?【英文标题】:SharePoint 2016: how to force JS to execute on every site page despite Ajax and/or MDS? 【发布时间】:2018-08-12 23:53:33 【问题描述】:

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

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

简单,你会说。嗯,不。远非如此,就像 SharePoint 一样。

重现步骤:

创建开箱即用的发布网站 使用我在下面描述的任何方法在下面包含自定义 javascript 转到该站点,转到主页。这是一个发布网站,因此默认情况下,您应该在左侧导航窗格中至少包含“Home”和“Documents”。 第一次加载页面时,javascript 会执行。现在,单击“文档”。页面发生了变化,但 Javascript 没有被执行。

这是因为 SharePoint 使用 Ajax。即使 MDS 被禁用。它通过 URL 中的哈希 (#) 使用 Ajax。

例如,它转换了一个非常无害的链接,如下所示:

点击后进入该网址:

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 的方法:

    通过用户自定义操作。我使用 this very handy page 添加它,但这不相关。该操作已添加到站点,我可以在第一次加载时在 DOM 中看到 JS。但是当我点击页面中的一个链接后,SP使用Ajax后,它就不再执行了。 通过修改母版页 -- 即:seattle.html。起初我以这种方式将其包含在其他本地包含项下:

...

但后来我读到了 AjaxDelta(这里:https://msdn.microsoft.com/fr-fr/library/office/dn456543.aspx),我将我的包含(仍在标题中)移动到 中,如下所示:

...

...但没有任何效果。通过单击 SharePoint 的“托管”链接在同一站点的页面之间切换时,永远不会执行 Javascript。

我正在寻找一种能够优雅地处理 SharePoint 的 Ajax 的解决方案,而不是劫持页面上每个超链接的繁重且有风险的解决方案。例如,我尝试将我的代码挂钩到 ajaxNavigate 方法(例如:addNavigate),但我不确定我是否了解那里实际发生的情况以及是否可以对我有任何帮助。

编辑:

似乎有一种共识(例如,最底部的here),即无论如何都会执行用户自定义操作 -- 因为据称 SharePoint 将他们的 ScriptLink 放入AjaxDelta 出于某种原因。好吧,这不是我亲眼目睹的。

另一个共识是,可以使用“RegisterModuleInit”解决此问题。这对我也不起作用。

我非常困惑。我认为当用户单击链接然后单击“返回”时,这两种解决方案确实解决了导航问题。但它并没有解决 SharePoint 巧妙的“托管”、充满 Ajax 的超链接。

【问题讨论】:

【参考方案1】:

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

简答:使用 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

完整的解决方案(对象“LefeCycleHelper”),由 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); 

【讨论】:

以上是关于SharePoint 2016:尽管有 Ajax 和/或 MDS,如何强制 JS 在每个网站页面上执行?的主要内容,如果未能解决你的问题,请参考以下文章

Sharepoint 2013 中的 Ajax 连接 Web 部件

SharePoint 2013 REST API AJAX 更新工作流任务

尽管有索引,但网页加载缓慢 ajax 查询检索时间过长

SharePoint REST API。在 SharePoint 列表上进行 AJAX 调用时出现 400 错误请求

如何在 Sharepoint 2010 中使用 HTTP 处理程序发出 jQuery AJAX 请求?

Sharepoint 2013:通过 REST API 确保用户