Модификации на стороне клиента через JavaScript
С помощью JavaScript
можно произвольным образом менять внешний вид КП на стороне клиента, в том числе добавлять свои элементы элементы управления с нужным вам поведением.
Этот способ обладает рядом ограничений, которые вы должны принимать во внимание:
- При включенном объединении
JS
в настройках главного модуля появление вашего скрипта на клиенте не полностью под вашим контролем. Требуется организовывать свой код определенным образом - При очередном обновлении Битрикс24, верстка может измениться. Вам необходимо следить за тем, чтобы обновление не привело к ошибкам в вашем коде и своевременно его актуализировать
- Код исполняется на стороне клиента, вы точно не знаете какой именно на той стороне браузер, по этой причине код должен быть кроссбраузерным
- С различными фрагментами
DOM
, может быть связана штатная клиентская логика, это может приводит к динамическому изменениюDOM-дерева
, ваш код должен корректно реагировать на эти изменения - Некоторыми штатными функциями Битрикс24, можно воспользоваться из нескольких мест. Например, задачу можно завершить с детальной страницы, и из списка и пр. Встраиваемые элементы должны подчиняться той же логике, то есть быть продублированы в разных интерфейсах. Это особенно актуально, если задача связана с ограничением доступа
Общий подход
Для следующих примеров, нам понадобится несколько файлов в определенных директориях:
/local/js/custom_stuff.js
скрипты/local/css/custom_stuff.css
стили/local/lang/ru/custom_stuff.js.php
ланговые константы/local/tools/custom_stuff/disk_templates.php
файл для Ajax запроса
Рекомендуемый способ подключения, использовать класс CJSCore
. Сначала необходимо зарегистрировать расширение с помощью метода CJSCore::RegisterExt
, а затем его подключить с помощью CJSCore::Init
, для простоты код можно выполнить в init.php
:
init.phpCJSCore::RegisterExt('custom_stuff',
array(
'js' => '/local/js/custom_stuff.js',
'lang' => '/local/lang/' . LANGUAGE_ID . '/custom_stuff.js.php',
'css' => '/local/css/custom_stuff.css',
'rel' => array(
'ajax',
'popup'
)
)
);
CJSCore::Init('custom_stuff');
Для проверки напишем в файле custom_stuff.js
простую функцию которая выведит фразу в модальном окне Привет, Мир!
, цель этой бесполезной функции, проверить что все корректно подключилось. Весь код поместим в пространство имен CustomStuff
:
/local/js/custom_stuff.jslet CustomStuff = BX.namespace('CustomStuff');
CustomStuff.helloWorld = function() {
alert(BX.message('CUSTOM_STUFF_HELLO_WORLD'));
};
В файле custom_stuff.js.php
языковых констант добавим нашу фразу. Обратите внимание, что CJSCore
автоматически передает на клиент необходимые языковые строки в зависимости от текущего языка:
/local/lang/ru/custom_stuff.js.php$MESS['CUSTOM_STUFF_HELLO_WORLD'] = 'Привет, Мир!';
Получить доступ к языковой константе, можно с помощью JS-функции BX.message
.
Перезагрузите страницу, откройте панель инструментов разработчика в браузере и выполните следующий JS-код в консоли, должны увидеть приветственное сообщение:
BX.CustomStuff.helloWorld();
Весь JS-код, добавленный с помощью классов CJSCore
, Asset
или функций AddHeadScript
, SetAdditionalCSS
, будет объединен в один файл, при условии что это включено в настройках главного модуля. При этом удаление вызова CJSCore::Init
не приведет к удалению вашего кода из файла с объединенным кодом. Это значит, что вы не можете управлять загрузкой вашего кода на сторону клиента путем вызова или не вызова CJSCore::Init
, следует применять другой подход.
Другой подход, заключается в том, что в custom_stuff.js
следует лишь объявить все необходимые функции и классы, которые понадобятся для решения задачи. Например, добавить кнопку или еще что-нибудь.
Если необходим запуск функций при загрузке страницы, то следует добавить на страницу небольшой скрипт, который это сделает. Добавить этот скрипт можно с помощью метода Asset::addString
который пришел на замену AddHeadString
. Он не будет объединен с другим JS-кодом, таким образом, вы можете контролировать запуск кода из custom_stuff.js
со стороны сервера.
Например, чтобы метод helloWorld
автоматически вызывался после загрузки каждой страницы, нужно сделать следующее:
index.php$asset = Bitrix\Main\Page\Asset::getInstance();
$asset->addString('<script>BX.ready(function () { BX.CustomStuff.helloWorld(); });</script>');
Пример создания кнопки
Добавим кнопку в верхнее меню раздела Лента:
Добавим в custom_stuff.js
:
/local/js/custom_stuff.js// пространство имен CustomStuff
let CustomStuff = BX.namespace('CustomStuff');
// функция выводит hello world
CustomStuff.helloWorld = function() {
alert(BX.message('CUSTOM_STUFF_HELLO_WORLD'));
};
// функция создает элемент и вставляет в меню
CustomStuff.createButton = function() {
// когда DOM готов, запускаем колбек функцию
BX.ready(function(){
// находим верхнее меню по id
let panelWidget = BX('feed-add-post-form-tab');
// создаем элемент a
let elementButton = BX.create('a', {
attrs : {
className : 'feed-add-post-form-link hmarketing-button',
href : '#'
},
html : BX.message('CUSTOM_STUFF_BUTTON')
});
// добавляем элемент
panelWidget.appendChild(elementButton);
// добавляем обработчик события клик и открываем модальное окно
BX.bind(elementButton, 'click', CustomStuff.helloWorld);
});
};
Добавим в custom_stuff.css
:
/local/css/custom_stuff.css.hmarketing-button {
margin-left: 42px;
}
Добавим в custom_stuff.js.php
:
/local/lang/ru/custom_stuff.js.php<?php
$MESS['CUSTOM_STUFF_HELLO_WORLD'] = 'Привет, Мир!';
$MESS['CUSTOM_STUFF_BUTTON'] = 'Кнопка Hmarketing';
Добавим в init.php
:
/local/php_interface/init.php<?php
use Bitrix\Main\Page\Asset;
use \Bitrix\Main\Application;
// получаем url хита
$context = Application::getInstance()->getContext();
$request = $context->getRequest();
$rDir = $request->getRequestedPageDirectory();
// подключаем расширение
CJSCore::RegisterExt('custom_stuff',
array(
'js' => '/local/js/custom_stuff.js',
'lang' => '/local/lang/' . LANGUAGE_ID . '/custom_stuff.js.php',
'css' => '/local/css/custom_stuff.css',
)
);
// инициализируем расширение
CJSCore::Init('custom_stuff');
// проверяем на директорию и инициализируем функцию объекта createButton()
if($rDir == '/stream/') {
$asset = Asset::getInstance();
$asset->addString('<script>BX.ready(function () { BX.CustomStuff.createButton(); });</script>');
}
Форма с popup от Битрикс
Немного усложним нашу логику, будем при клике на кнопку вызывать модальное окно Битрикс.
Добавим в custom_stuff.js
:
/local/js/custom_stuff.js// пространство имен CustomStuff
let CustomStuff = BX.namespace('CustomStuff');
// функция выводит hello world
CustomStuff.helloWorld = function() {
alert(BX.message('CUSTOM_STUFF_HELLO_WORLD'));
};
// функция создает элемент и вставляет в меню
CustomStuff.createButton = function() {
// когда DOM готов, запускаем колбек функцию
BX.ready(function(){
// находим верхнее меню по id
let panelWidget = BX('feed-add-post-form-tab');
// создаем элемент a
let elementButton = BX.create('a', {
attrs : {
className : 'feed-add-post-form-link hmarketing-button',
href : '#'
},
html : BX.message('CUSTOM_STUFF_BUTTON')
});
// добавляем элемент
panelWidget.appendChild(elementButton);
// добавляем обработчик события клик и открываем модальное окно
BX.bind(elementButton, 'click', function (event){
BX.PreventDefault(event);
CustomStuff.showPopup()
}
);
});
};
// вызов модального окна
CustomStuff.showPopup = function() {
// если окно уже существует, закрываем и убиваем окно
if (CustomStuff.popup) {
CustomStuff.popup.close();
CustomStuff.popup.destroy();
}
// генерируем окно по параметрам
CustomStuff.popup = new BX.PopupWindow('hmarketing-popup', null, {
width : 600,
height : 400,
closeByEsc : true,
closeIcon : true,
overlay : {
opacity : 50,
backgroundColor : '#000'
},
titleBar : BX.message('CUSTOM_STUFF_POPUP_TITLE'),
content : BX.create('div', {
html : '<p>' + BX.message('CUSTOM_STUFF_POPUP_SUBTITLE') + '</p><textarea></textarea>'
}),
buttons : [ new BX.PopupWindowButton({
text : BX.message('CUSTOM_STUFF_POPUP_BUTTON'),
className : 'popup-window-button-accept',
events : {
click : CustomStuff.helloWorld
}
}) ]
});
// выводим окно
CustomStuff.popup.show();
};
Добавим в custom_stuff.css
:
/local/css/custom_stuff.css.hmarketing-button {
margin-left: 42px;
}
#hmarketing-popup textarea {
width: 100%;
min-height: 100px;
}
Добавим в custom_stuff.js.php
:
/local/lang/ru/custom_stuff.js.php<?php
$MESS['CUSTOM_STUFF_HELLO_WORLD'] = 'Привет, Мир!';
$MESS['CUSTOM_STUFF_BUTTON'] = 'Кнопка Hmarketing';
$MESS['CUSTOM_STUFF_POPUP_TITLE'] = 'Заголовок модального окна';
$MESS['CUSTOM_STUFF_POPUP_SUBTITLE'] = 'Подзаголовок модального окна';
$MESS['CUSTOM_STUFF_POPUP_BUTTON'] = 'Кнопка модального окна';
Добавим в init.php
:
/local/php_interface/init.php<?php
use Bitrix\Main\Page\Asset;
use \Bitrix\Main\Application;
// получаем url хита
$context = Application::getInstance()->getContext();
$request = $context->getRequest();
$rDir = $request->getRequestedPageDirectory();
// подключаем расширение
CJSCore::RegisterExt('custom_stuff',
array(
'js' => '/local/js/custom_stuff.js',
'lang' => '/local/lang/' . LANGUAGE_ID . '/custom_stuff.js.php',
'css' => '/local/css/custom_stuff.css',
'rel' => array(
'popup'
)
)
);
// инициализируем расширение
CJSCore::Init('custom_stuff');
// проверяем на директорию и инициализируем функцию объекта createButton()
if($rDir == '/stream/') {
$asset = Asset::getInstance();
$asset->addString('<script>BX.ready(function () { BX.CustomStuff.createButton(); });</script>');
}
На событие клика по кнопке Кнопка модального окна
запускается функция helloWorld
. В риальном примере, можно сделать AJAX-запрос, который выполняет настоящую отправку сообщения.
Пример Ajax запроса на событиях
Для примера возьмем кнопку из примера выше, добавим логику обработки Ajax запроса. В примере генерируем кастомное событие через функцию BX.onCustomEvent()
, можно обойтись без кастомного события, добавлено для наглядности как отлавливать события в Битрикс24.
Добавим в custom_stuff.js
:
/local/js/custom_stuff.js// пространство имен CustomStuff
let CustomStuff = BX.namespace('CustomStuff');
// функция вызывает костомное событие popupOpen
CustomStuff.ajax = function () {
BX.onCustomEvent('popupOpen')
}
// функция создает элемент и вставляет в меню
CustomStuff.createButton = function() {
// когда DOM готов, запускаем колбек функцию
BX.ready(function(){
// находим верхнее меню по id
let panelWidget = BX('feed-add-post-form-tab');
// создаем элемент a
let elementButton = BX.create('a', {
attrs : {
className : 'feed-add-post-form-link hmarketing-button',
href : '#'
},
html : BX.message('CUSTOM_STUFF_BUTTON')
});
// добавляем элемент
panelWidget.appendChild(elementButton);
// добавляем обработчик события клик и открываем модальное окно
BX.bind(elementButton, 'click', function (event){
BX.PreventDefault(event);
CustomStuff.showPopup()
}
);
});
};
// вызов модального окна
CustomStuff.showPopup = function() {
// если окно уже существует, закрываем и убиваем окно
if (CustomStuff.popup) {
CustomStuff.popup.close();
CustomStuff.popup.destroy();
}
// генерируем окно по параметрам
CustomStuff.popup = new BX.PopupWindow('hmarketing-popup', null, {
width : 600,
height : 400,
closeByEsc : true,
closeIcon : true,
overlay : {
opacity : 50,
backgroundColor : '#000'
},
titleBar : BX.message('CUSTOM_STUFF_POPUP_TITLE'),
content : BX.create('div', {
html : '<p>' + BX.message('CUSTOM_STUFF_POPUP_SUBTITLE') + '</p><textarea></textarea>'
}),
buttons : [ new BX.PopupWindowButton({
text : BX.message('CUSTOM_STUFF_POPUP_BUTTON'),
className : 'popup-window-button-accept',
events : {
click : CustomStuff.ajax
}
}) ]
});
// выводим окно
CustomStuff.popup.show();
};
// отлавливаем кастомное событие popupOpen и выполняем Ajax запрос на сервер
BX.addCustomEvent('popupOpen', function () {
// получаем модальное окно
let objectPopup = BX('hmarketing-popup');
// в модальном окне находим по тегу textarea
let textarea = BX.findChildren(objectPopup, {
tag: "textarea",
}, true);
// ajax запрос на сервер
BX.ajax({
// передаем параметр sessid и проверять его в обработчике запроса
sessid: BX.bitrix_sessid(),
url: '/local/tools/custom_stuff/disk_templates.php', // файл на который идет запрос
method: 'GET',
// положительный ответ сервера лежит в data
onsuccess: function (data) {
// вставляем в textarea сообщение
textarea[0].value = data.trim();
},
// отрицательный ответ сервера лежит в error
onfailure: function (error) {
// вставляем в textarea сообщение
textarea[0].value = 'Извините, произошла ошибка!'
}
})
});
В данном случае мы генерируем кастомное событие BX.onCustomEvent('popupOpen')
, в реальной жизни мы не понимаем на что можно подписаться методом BX.addCustomEvent()
, для этого есть специальный скрипт получения событий Битрикс, для этого достаточно вызвать код в браузере и выполнить действие, например всплытие модального окна.
Добавим в custom_stuff.css
:
/local/css/custom_stuff.css.hmarketing-button {
margin-left: 42px;
}
#hmarketing-popup textarea {
width: 100%;
min-height: 100px;
}
Добавим в custom_stuff.js.php
:
/local/lang/ru/custom_stuff.js.php<?php
$MESS['CUSTOM_STUFF_HELLO_WORLD'] = 'Привет, Мир!';
$MESS['CUSTOM_STUFF_BUTTON'] = 'Кнопка Hmarketing';
$MESS['CUSTOM_STUFF_POPUP_TITLE'] = 'Заголовок модального окна';
$MESS['CUSTOM_STUFF_POPUP_SUBTITLE'] = 'Подзаголовок модального окна';
$MESS['CUSTOM_STUFF_POPUP_BUTTON'] = 'Получить данные с сервера';
Добавим в init.php
:
/local/php_interface/init.php<?php
use Bitrix\Main\Page\Asset;
use \Bitrix\Main\Application;
// получаем url хита
$context = Application::getInstance()->getContext();
$request = $context->getRequest();
$rDir = $request->getRequestedPageDirectory();
// подключаем расширение
CJSCore::RegisterExt('custom_stuff',
array(
'js' => '/local/js/custom_stuff.js',
'lang' => '/local/lang/' . LANGUAGE_ID . '/custom_stuff.js.php',
'css' => '/local/css/custom_stuff.css',
'rel' => array(
'ajax',
'popup'
)
)
);
// инициализируем расширение
CJSCore::Init('custom_stuff');
// проверяем на директорию и инициализируем функцию объекта createButton()
if($rDir == '/stream/') {
$asset = Asset::getInstance();
$asset->addString('<script>BX.ready(function () { BX.CustomStuff.createButton(); });</script>');
}
Доработка готовых функций и методов
Существуют задачи, которые на событиях сделать невозможно, такое бывает когда действие не генерирует событие, следовательно у нас нет возможности подписаться и отследить выполнение. JS
особенный язык, в нем есть возможность заменить любую функцию или метод на свою реализацию.
Перед тем, как пользователь зайдет в профиль, нам нужно вывести предупреждение:
Для кастомизации нам не надо знать реализацию открытия. Мы заменим оригинальный метод на свой, который будет сначала запрашивать подтверждение, далее работать по своей логике.
Прежде всего нам нужно узнать, к какому пространсту имен, классу относится метод и какая у него сигнатура. Нужно узнать, какие аргументы принимает метод, для этого нужно открыть исходный код.
За открытие отвечает файл /bitrix/components/bitrix/intranet.user.profile.button/templates/.default/script.js
, в файле есть метод init()
, полны путь до метода с пространством имен BX.Intranet.UserProfile.Widget.init
.
Теперь мы можем просто присвоить BX.Intranet.UserProfile.Widget.init
новую версию метода.
Добавим в custom_stuff.js
:
/local/js/custom_stuff.js// пространство имен CustomStuff
let CustomStuff = BX.namespace('CustomStuff');
// переопределенный метод
CustomStuff.overrideShare = function() {
// пространство имен BX.Intranet.UserProfile.Widget, где будет заменена функция init()
BX.Intranet.UserProfile.Widget.init = function (node, options) {
let _this2 = this;
let onclick = function onclick() {
// вставляем alert с предупреждением
alert('Внимание! В конце рабочего дня, обязательно выйдете из профиля.');
if (!node['popupSymbol']) {
node['popupSymbol'] = new _this2(node, options);
}
node['popupSymbol'].toggle();
_this2.instance = node['popupSymbol'];
};
node.addEventListener('click', onclick);
}
}
Добавим в custom_stuff.js.php
:
/local/lang/ru/custom_stuff.js.php<?php
$MESS['CUSTOM_STUFF_PROFIL_INFO'] = 'Внимание! В конце рабочего дня, обязательно выйдете из профиля.';
Добавим в init.php
:
/local/php_interface/init.php<?php
use Bitrix\Main\Page\Asset;
use \Bitrix\Main\Application;
// получаем url хита
$context = Application::getInstance()->getContext();
$request = $context->getRequest();
$rDir = $request->getRequestedPageDirectory();
// подключаем расширение
CJSCore::RegisterExt('custom_stuff',
array(
'js' => '/local/js/custom_stuff.js',
'lang' => '/local/lang/' . LANGUAGE_ID . '/custom_stuff.js.php',
'css' => '/local/css/custom_stuff.css',
)
);
// инициализируем расширение
CJSCore::Init('custom_stuff');
// проверяем на директорию и инициализируем функцию объекта createButton()
if($rDir == '/stream/') {
$asset = Asset::getInstance();
$asset->addString('<script>BX.ready(function () { BX.CustomStuff.overrideShare() });</script>');
}