В прошедшем недавно семинаре про клиентской разработке в SharePoint (записи – по ссылке) я показывал как сделать клиентский скрипт, который выполняет некоторый код после окончания загрузки страницы в SharePoint.
Эта, казалось бы, простая задача далеко не тривиально реализуется в SharePoint.
Причины
- SharePoint формирует интерфейс динамически. Многие блоки добавляются на страницу по событию body.onload. Это событие возникает позже, чем DOMContentLoaded. Именно это событие перехватывает jQuery.ready. Поэтому использование jQuery часто не приводит к хорошему результату. Подробнее об использовании jQuery в SharePoint.
- Minimal Download Strategy (MDS), появившийся в SharePoint 2013, загружает страницу один раз, потом обновляет блоки страницы, поэтому нужно выполнять дополнительные действия, чтобы скрипт выполнился после загрузки страницы под MDS.
- Механизм загрузки скриптов, о котором я писал ранее, требует чтобы скрипт самостоятельно оповещал об окончании выполнения.
Для скриптов в виртуальной файловой системе SharePoint
Чаще всего скрипты SharePoint деплоятся как файлы в виртуальной файловой системе. Это прекрасно работает как в on premises, так и в online.
Для размещения скриптов на странице используется контрол ScriptLink, в таком виде:
<SharePoint:ScriptLink Name="autofill.js" runat="server" OnDemand="true" LoadAfterUI="true" Localizable="false" />
или с помощью CustomAction ScriptLink в элементе решения
<CustomAction Location="ScriptLink" ScriptSrc="~site/Extensions/typescripttemplates.js" />
Внутри скрипта нужно выполнить следующие действия:
- Оповестить SharePoint об окончании загрузки.
- Добавить функцию, которую необходимо вызывать после загрузки страницы, в массив _spBodyOnLoadFunctions.
- Добавить зарегистрировать скрипт в системе MDS.
Код:
// IIFE для изоляции (function () { "use strict"; // Имя скрипта должно совпадать с тем, что указано в ScriptLink var scriptLink = "~site/Scripts/myscript.js"; // Код здесь будет выполняться ДО окончания загрузки страницы if (_spBodyOnLoadCalled) { // Если событие body.onload уже обработано // напрмиер при LoadAfterUI="true" или OnDemand="true" // то сразу вызываем функцию init init(); } else { // Иначе добавляем функцию в массив _spBodyOnLoadFunctions.push(init); } function init() { // заменяем токены "~site" и "~sitecollection" на реальные URL // с помощьюв SPClientRenderer.ReplaceUrlTokens // но эта функция доступна после загрузки CSR // поэтому нужно выполнить зарузку CSR и дождаться её окончания // если CSR уже был загружен, то функция выполнится сразу SP.SOD.executeFunc("clientrenderer.js", "SPClientRenderer.ReplaceUrlTokens", function() { // тепрь регистрируем скрипт в MDS RegisterModuleInit(SPClientRenderer.ReplaceUrlTokens(scriptLink), init); }); // Код здесь будет выполняться ПОСЛЕ окончания загрузки страницы } SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs(scriptLink); })();
Если вы сами деплоите страницу, на которой будет работать ваш скрипт, то можете воспользоваться библиотекой mQuery и функцией m$.ready. Эта функция делает тоже самое, что и блок if\else в коде выше.
Для скриптов в layouts
Если вы размещаете свой скрипт в _layouts, то есть несколько особенностей:
- При добавлении скрипта надо указывать только имя файла.
- SOD автоматически регистрирует в MDS вызов функции с именем $_global_имя_файла_без_js
Код для файла в _layouts
"use strict"; // эта функция должна быть в глобальном пространстве имен function $_global_myscript() { // Код здесь будет выполняться ПОСЛЕ окончания загрузки страницы } if (_spBodyOnLoadCalled) { // Если событие body.onload уже обработано // напрмиер при LoadAfterUI="true" или OnDemand="true" // то сразу вызываем функцию init init(); } else { // Иначе добавляем функцию в массив _spBodyOnLoadFunctions.push(init); } // этот вызов автоматически добавит регистрацию в MDS SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("myscript.js");
Универсальные скрипты
Если ваши скрипты будут вызываться вне страниц SharePoint, то обращение к “пространству имен” SP вызовет ошибку. Кроме того есть свой механизм оповещения о загрузке скриптов в MsAjax библиотеке, поэтому в конце скрипта надо использовать такой блок:
// Оповестить MsAjax об окончании загрузки if (typeof Sys != "undefined" && Sys && Sys.Application) { Sys.Application.notifyScriptLoaded(); } // Оповестить SharePoint об окончании загрузки if (typeof SP != "undefined" && SP && SP.SOD) { SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("myscript.js"); }
Заключение
Более подробно о загрузке скриптов, TypeScript, особенностях SharePoint и Client Side Rendering можете узнать подробнее, посмотрев записи семинара - http://blog.gandjustas.ru/shop/spclient/