Полный цикл в digital

Модификации на стороне клиента через JavaScript

С помощью JavaScript можно произвольным образом менять внешний вид КП на стороне клиента, в том числе добавлять свои элементы элементы управления с нужным вам поведением.

Этот способ обладает рядом ограничений, которые вы должны принимать во внимание:

  1. При включенном объединении JS в настройках главного модуля появление вашего скрипта на клиенте не полностью под вашим контролем. Требуется организовывать свой код определенным образом
  2. При очередном обновлении Битрикс24, верстка может измениться. Вам необходимо следить за тем, чтобы обновление не привело к ошибкам в вашем коде и своевременно его актуализировать
  3. Код исполняется на стороне клиента, вы точно не знаете какой именно на той стороне браузер, по этой причине код должен быть кроссбраузерным
  4. С различными фрагментами DOM, может быть связана штатная клиентская логика, это может приводит к динамическому изменению DOM-дерева, ваш код должен корректно реагировать на эти изменения
  5. Некоторыми штатными функциями Битрикс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>');
}

Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!
Эйч Маркетинг