Блог разработчика 1С-Битрикс

Обработка событий в 1С-Битрикс: BX.proxy и BX.delegate

Кратко: BX.proxy и BX.delegate — это «фабрики» функций-делегатов, которые помогают корректно передавать контекст (this) в обработчики событий, колбэки AJAX и другие асинхронные вызовы во фронтенде 1С-Битрикс.

Обработка событий. BX.proxy/BX.delegate

Зачем нужны делегаты?

JavaScript теряет исходный this, когда метод объекта передаётся как колбэк:

someBtn.onclick = obj.method; // внутри method «this» укажет на кнопку, а не на obj

Чтобы вернуть контексту правильное значение, Bitrix-ядро предоставляет две удобные оболочки:

BX.proxy(func, context);    // кэширует делегат
BX.delegate(func, context); // создаёт новый делегат каждый раз

В чём разница между BX.proxy и BX.delegate?

Критерий BX.proxy BX.delegate
Кэширование результата Да — при одинаковых аргументах возвращает ту же ссылку на функцию-делегат. Нет — создаёт новый объект функции каждый вызов.
Удобно снимать обработчики? Да — можно «отвязать» именно тот обработчик, что был назначен. Труднее — нужен доступ к той же самой ссылке, а делегат каждый раз новый.
Аналог в jQuery jQuery.proxy

Минимальный пример

Разметка

<button id="show-title">Показать содержимое</button>

Скрипт

<script>
/**
 * Класс-демонстратор.
 */
function Card(title) {
  this.title = title;
  BX.bind(
    BX('show-title'),
    'click',
    // сохраняем контекст класса для обработчика
    BX.proxy(this.handleClick, this)
  );
}
Card.prototype.handleClick = function (e) {
  alert(this.title); // корректно выводит заголовок
  // снимаем обработчик, используя ту же ссылку-делегат
  BX.unbind(
    BX('show-title'),
    'click',
    BX.proxy(this.handleClick, this)
  );
};
BX.ready(function () {
  new Card('Моя первая карточка');
});
</script>

Если заменить BX.proxy на BX.delegate, то второй вызов создаст другую функцию, и BX.unbind не найдёт нужный обработчик.


Проверка кэширования

function foo() {}
var ctx = { a: 1 };
console.log(
  BX.proxy(foo, ctx) === BX.proxy(foo, ctx) // true
);
console.log(
  BX.delegate(foo, ctx) === BX.delegate(foo, ctx) // false
);

Практический кейс: AJAX-запрос

/**
 * Компонент, отправляющий AJAX и обрабатывающий ответ в своём контексте.
 */
class CommentsWidget {
  constructor(url) {
    this.endpoint = url;
  }
  refresh() {
    BX.ajax.post(
      this.endpoint,
      { sessid: BX.bitrix_sessid() },
      // сохраняем «this» через делегат
      BX.proxy(function (data) {
        this.render(JSON.parse(data));
      }, this)
    );
  }
  render(comments) {
    console.log('Получены комментарии', comments);
  }
}
BX.ready(() => {
  const widget = new CommentsWidget('/api/comments.php');
  widget.refresh();
});

Передаём дополнительные аргументы

Иногда нужно «подмешать» параметры в делегат. Самый гибкий способ — анонимная обёртка:

BX.bind(
  targetNode,
  'click',
  BX.proxy(function (event) {
    this.process(event, 42, 'extra-data');
  }, this)
);

Что делать не нужно

// Неправильно: handler(arg) вызывается немедленно!
BX.bind(targetNode, 'click', BX.proxy(this.handler(arg), this));

Альтернатива без Bitrix-функций — нативный Function.prototype.bind, но он не кэширует результат, поэтому для последующего BX.unbind придётся хранить ссылку на делегат вручную:

this._clickDelegate = this.handler.bind(this, arg);
BX.bind(targetNode, 'click', this._clickDelegate);
// ...
BX.unbind(targetNode, 'click', this._clickDelegate);

Под капотом: как это работает?

  1. Создание делегата
    Оба метода возвращают новую функцию (wrapper), внутри которой вызывается func.apply(context, arguments).
  2. Кэширование (только BX.proxy)
    Bitrix хранит ассоциативный массив __bx_proxy_cache (или похожий), где ключом служит строчка func + '_' + context. Если такой ключ уже есть, возвращается сохранённый wrapper.
  3. Удаление обработчика
    Благодаря одинаковой ссылке, BX.unbind легко находит именно тот wrapper, который был повешен ранее.

Советы по использованию

  1. Для большинства сценариев выбирайте BX.proxy — он помогает и добавить, и потом убрать обработчик.
  2. BX.delegate подходит, если гарантированно не придётся откреплять обработчик или нужно всегда получать свежий делегат (редкий случай).
  3. Храните делегаты, если снимаете события часто. При большом числе динамически создаваемых элементов можно сохранить ссылку в свойстве объекта или data-* атрибуте узла.
  4. Не злоупотребляйте передачей анонимных функций внутрь BX.proxy. Если обёртка логически отделима, вынесите её в метод класса — это облегчит отладку и переиспользование.

Итог

BX.proxy и BX.delegate — незаменимые инструменты при работе с событиями и асинхронными вызовами в 1С-Битрикс. Зная их различия и правильно управляя контекстом, вы избежите типичной ошибки «потерянного this» и сделаете код обработчиков чище, надёжнее и легче для поддержки.

Теги:  BX.proxy, BX.delegate, обработка событий, JavaScript, контекст, асинхронные вызовы


Стоимость услуг по разработке и сопровождению сайтов на 1C-Битрикс

Лендинг

от 3 дней

от 25 000 рублей

Разработка одностраничного сайта на платформе Битрикс

* стоимость зависит от наличия верстки, использования готового решения и т.д.

Интернет-магазин на готовом решении

от 7 дней

от 40 000 рублей
запуск сайта в максимально короткие сроки

* указана минимальная стоимость. Стоимость выбранной лицензии «1С-Битрикс» оплачивается отдельно.

Аутсорсинг

готов помочь, если нет времени

договорная

Могу взять на себя работы по full-stack на основе готовой верстки

* если нет верстки, то возможность верстать с Figma в режиме редактора