Укрощение JavaScript в Drupal (включая часто задаваемые вопросы)
Опубликовано: 2023-02-07Интерактивный веб-интерфейс обеспечивает более привлекательный и приятный опыт для пользователей. Это приводит к повышению удовлетворенности пользователей и положительному восприятию веб-сайта. Например, форма, которая обеспечивает мгновенную обратную связь и проверку, а не заставляет пользователя ждать обновления страницы, может значительно улучшить взаимодействие с пользователем.
JavaScript играет важную роль в Drupal, предоставляя средства для создания динамических и интерактивных возможностей для пользователей на внешнем интерфейсе веб-сайта Drupal. Он позволяет разработчикам изменять поведение определенных элементов на странице, таких как формы, ссылки или любые другие элементы DOM, без необходимости обновления всей страницы. Drupal Behaviors — это функции JavaScript, которые выполняются, когда на странице происходят определенные события. Поведения упрощают разработчикам поддержку и обновление сайта, поскольку им не нужно изменять базовый HTML-код. Узнайте все, что вы хотели знать о Drupal Behaviors в этой статье.
Что такое поведение Drupal?
Drupal.behaviors — это объект внутри структуры Javascript в Drupal, который позволяет нам прикреплять функции, которые будут выполняться в определенное время во время выполнения приложения. Он вызывается, когда DOM полностью загружен, но это поведение можно вызвать снова. Официальная документация Drupal по JavaScript предполагает, что модули должны реализовывать JavaScript, присоединяя логику к Drupal.behaviors .
Зачем нам нужно поведение Drupal?
Преимущество Behaviors заключается в том, что они автоматически повторно применяются к любому содержимому, загружаемому через AJAX. Их можно вызывать в любое время с контекстом, представляющим новые дополнения или изменения в DOM. Это лучше, чем $(document).ready() или document DOMContentLoaded, где код запускается только один раз.
Когда поведение Drupal нежелательно?
Поведение Drupal не всегда идеальное решение для написания Javascript в Drupal. В некоторых случаях, как указано ниже, поведение Drupal вообще не требуется!
- Когда нам нужно выполнить какой-то код, не влияющий на DOM. Например. Инициализация внешнего скрипта, такого как Google Analytics
- Когда какую-то операцию JS необходимо выполнить в DOM только один раз, зная, что элемент будет доступен при загрузке страницы (этот сценарий отличается от использования Once).
Когда вызывается Drupal Behaviors?
- После загрузки административного наложения на страницу.
- После того, как AJAX Form API отправил форму.
- Когда запрос AJAX возвращает команду, которая изменяет HTML, например ajax_command_replace() .
Другие случаи, когда вызывается Drupal Behaviors
- CTools вызывает его после загрузки модального окна.
- Медиа вызывает его после загрузки медиабраузера.
- Panels вызывает его после завершения редактирования на месте.
- Views вызывает его после загрузки новой страницы, использующей AJAX.
- Views Load More вызывает его после загрузки следующего фрагмента элементов.
- JavaScript из пользовательских модулей может вызывать Drupal.attachBehaviors() при добавлении или изменении частей страницы.
Написание кода без поведения Drupal
В этом коде мы добавляем прослушиватель событий щелчка в класс .views-row , который вычисляет, сколько раз мы нажимаем на эту строку. Но он добавляется только один раз к элементам, которые попадают в DOM во время начальной загрузки страницы. После нажатия «Загрузить еще» и загрузки дополнительных элементов прослушиватель кликов не работает с вновь загруженными элементами.
// No Drupal Behaviors (function () { let header = document.querySelector(".food-list-header"); if (header) { let greatFoodSpan = document.createElement("span"); greatFoodSpan.textContent = "Get ready for great food!!!!!!"; header.append(greatFoodSpan); } // Add the event listener for each click on the food let foods = document.querySelectorAll(".views-row"); foods.forEach((food) => { food.addEventListener("click", () => { let foodCounter = food.querySelector(".food-click-counter"); let timesClicked = parseInt(foodCounter.textContent.trim()); foodCounter.textContent = ++timesClicked; }); }); })();
Как мы используем поведение Drupal?
Ответ: с помощью метода присоединения
То, что нужно запомнить:
- Новый объект должен иметь как минимум метод присоединения.
- Каждый раз, когда вызывается Drupal.attachBehaviors, он будет перебирать все объекты поведения и вызывать соответствующие методы подключения.
Добавление поведения Drupal в наш код
После добавления Drupal Behaviors код выглядит примерно так.
(function (Drupal) { Drupal.behaviors.exampleBehaviour1 = { attach: (context, settings) => { // Add a delicious text to the top of the document let header = document.querySelector(".food-list-header"); // jQuery Equivalent // $(".food-list-header"); if (header) { let greatFoodSpan = document.createElement("span"); greatFoodSpan.textContent = "Get ready for great food!!!!!!"; header.append(greatFoodSpan); } // Add the event listener for each click on the food let foods = document.querySelectorAll(".views-row"); foods.forEach((food) => { food.addEventListener("click", () => { let foodCounter = food.querySelector(".food-click-counter"); let timesClicked = parseInt(foodCounter.textContent.trim()); foodCounter.textContent = ++timesClicked; }); }); }, }; })(Drupal);
Но что-то странное появляется вверху, когда мы нажимаем « Загрузить еще»:
Это связано с тем, что поведение Drupal вызывается много раз, и впоследствии мы получаем непреднамеренное поведение.
Что такое контекст в «контексте Drupal»?
- При вызове метода attach для всех вариантов поведения Drupal передает параметр контекста .
- Передаваемый параметр контекста часто может дать лучшее представление о том, какой элемент DOM обрабатывается.
- Во время начальной загрузки страницы это будет полный HTML-документ; во время последующих вызовов это будут только элементы, которые добавляются на страницу или изменяются.
Как добавить контекст?
Предыдущую проблему можно решить, используя параметр контекста , предоставляемый Drupal Behaviors. В этом случае при первой загрузке страницы мы получаем весь HTML-документ в качестве контекста, и именно тогда мы присоединяем заголовок. Для дальнейших операций это будет часть кода, на которую влияет Drupal Behaviors, и, следовательно, эта часть кода находится под безопасным контролем.
(function (Drupal) { Drupal.behaviors.exampleBehaviour2 = { attach: (context, settings) => { // Add a delicious text to the top of the document. // The context parameter now can be used for adding // certain functionality which removes unwanted repeatability let header = context.querySelector(".food-list-header"); // jQuery Equivalent // $(".food-list-header", context); if (header) { let greatFoodSpan = document.createElement("span"); greatFoodSpan.textContent = "Get ready for great food!!!!!!"; header.append(greatFoodSpan); } // Add the event listener for each click on the food let foods = context.querySelectorAll(".views-row"); foods.forEach((food) => { food.addEventListener("click", () => { let foodCounter = food.querySelector(".food-click-counter"); let timesClicked = parseInt(foodCounter.textContent.trim()); foodCounter.textContent = ++timesClicked; }); }); }, }; })(Drupal);
Опять же, есть какое-то странное поведение, когда мы нажимаем «Загрузить еще ». Продукты питания, которые были изначально загружены, работают нормально. Но после нажатия «Загрузить еще» новые элементы получают прослушиватель кликов и работают нормально. Но к первоначально загруженным элементам снова подключается прослушиватель, и нажатие на них вызывает событие клика более одного раза!
Когда Drupal Behaviors начинает вести себя неправильно?
- Написание всех прослушивателей событий внутри поведения Drupal без использования Once и Context.
- Объявление нежелательных функций внутри поведения Drupal, что приводит к повторному объявлению функций каждый раз, когда вызывается метод attach.
«Однажды» на помощь
- Once гарантирует, что что-то обрабатывается только один раз, добавляя атрибут data-once в элемент DOM после выполнения кода.
- Если поведение вызывается снова, элемент с атрибутом data-once пропускается для дальнейшего выполнения.
- Once — это современная реализация jQuery.once (попытка уйти от jQuery).
- Once, в сочетании с контекстом, прекрасно управляет всем функционалом так, как нам нужно.
Добавление Once для исправления прослушивателей событий в нашем коде
(function (Drupal, once) { Drupal.behaviors.exampleBehaviour3 = { attach: (context, settings) => { once("food-header-initialized", ".food-list-header", context).forEach( (header) => { let greatFoodSpan = document.createElement("span"); greatFoodSpan.textContent = "Get ready for great food!!!!!!"; header.append(greatFoodSpan); } ); // jQuery Equivalent // $(".food-list-header", context).once("food-header-initialized", function (header) { // // }); // Add the event listener for each click on the food once("food-initialized", ".views-row", context).forEach((food) => { food.addEventListener("click", () => { let foodCounter = food.querySelector(".food-click-counter"); let timesClicked = parseInt(foodCounter.textContent.trim()); foodCounter.textContent = ++timesClicked; }); }); }, }; })(Drupal, once);
Теперь все работает как задумано. Мы получаем атрибут data-once для элементов, к которым подключены прослушиватели событий, и вновь загруженные элементы и ранее загруженные элементы функционируют правильно.
Метод необходимости отсоединения
Метод Detach действует как антигерой (не зло), удаляя все, что мы сделали в методе attach. Любой код в методе detach будет вызываться всякий раз, когда контент удаляется из DOM. Это поможет нам очистить наше приложение. Например, метод Detach позволяет нам удалять нежелательные прослушиватели событий, которые потребляют ресурсы, такие как ситуация непрерывного опроса.
Примеры отсоединения
Предположим, что у нас есть форма ajax для заполнения, и мы используем таймер, чтобы показать прошедшее время. Мы используем setTimeOut для управления таймером. Логируем этот таймер в консоли для мониторинга.
(function (Drupal, once) { let counter = 0; Drupal.behaviors.exampleBehaviour4 = { attach: (context, settings) => { once("timer-initalized", ".contact-timer", context).forEach((ele) => { const timer = context.querySelector(".contact-timer-sec"); timer.textContent = counter; // Set the timer for user to see the time elapsed setInterval(() => { console.log("This is logging"); const timer = document.querySelector(".contact-timer-sec"); timer.textContent = ++counter; }, 1000); }); }, }; })(Drupal, once);
При отправке формы таймер в DOM удаляется, но консоль начинает выдавать ошибку. Это связано с тем, что элемент, на который действует setTimeOut, был удален из DOM:
Чтобы избежать этого, мы можем использовать метод detach следующим образом:
(function (Drupal, once) { let counter = 0; let intervalStopper; Drupal.behaviors.exampleBehaviour4 = { attach: (context, settings) => { // Set the timer for user to see the time elapsed once("timer-initialized", ".contact-timer", context).forEach((ele) => { const timer = context.querySelector(".contact-timer-sec"); timer.textContent = counter; intervalStopper = setInterval(() => { const timer = document.querySelector(".contact-timer-sec"); timer.textContent = ++counter; console.log("This is logging"); }, 1000); }); }, // Clear the timer on confirmation detach: (context, settings, trigger) => { const timer = context.querySelector(".contact-timer-sec"); if (trigger == "unload" && timer) { clearInterval(intervalStopper); } }, }; })(Drupal, once);
Это удаляет таймер при выгрузке, и, как видно из регистратора, ошибка не возникает.
Немедленно вызываемые функциональные выражения (IIFE) — оболочка для JS
Мы использовали IIFE для написания нашего кода Drupal. Начальные открывающие круглые скобки определяют анонимную функцию, которая помогает предотвратить загрязнение области действия функции глобальной областью действия всего приложения. Вы можете передать аргументы своей анонимной функции, включив их в качестве аргументов в конце определения функции.
Это также помогает нам распределять имена параметров так, как мы хотим, чтобы они использовались.
Пример:
// Function name crisis!!!! // The function is vulnearble to // be replaced by some other function function someFunction() { // Some code for this function } (function (Drupal) { // Function name crisis averted! function someFunction() { // Some code for this other function } Drupal.behaviors.exampleBehaviour6 = { attach: (context, settings) => { someFunction(); }, }; })(Drupal);
Последние мысли
Реализация поведения Drupal обеспечивает динамическую интерактивность, упрощенное взаимодействие с пользователем, улучшенную обратную связь с пользователем, эффективную разработку и общее улучшение пользовательского опыта вашего веб-сайта. Drupal.behaviors являются гибкими и модульными, поскольку они могут выполняться несколько раз на странице, могут переопределять и расширять существующее поведение и могут автоматически повторно применяться к любому содержимому, загруженному через Ajax.
Ищете агентство по разработке Drupal, которое поможет вам создать интерактивный веб-интерфейс, максимально используя возможности Drupal? Мы бы хотели поговорить!