Domar JavaScript en Drupal (incluye preguntas frecuentes)
Publicado: 2023-02-07Las experiencias web interactivas brindan una experiencia más atractiva y agradable para los usuarios. Conduce a una mayor satisfacción del usuario y una percepción positiva de un sitio web. Por ejemplo, un formulario que proporciona comentarios y validación instantáneos, en lugar de hacer que el usuario espere a que se actualice la página, puede mejorar significativamente la experiencia del usuario.
JavaScript juega un papel importante en Drupal al proporcionar los medios para crear experiencias dinámicas e interactivas para los usuarios en la interfaz de un sitio web de Drupal. Permite a los desarrolladores modificar el comportamiento de ciertos elementos en una página, como formularios, enlaces o cualquier otro elemento DOM, sin tener que actualizar toda la página. Los comportamientos de Drupal son funciones de JavaScript que se ejecutan cuando ocurren eventos específicos en una página. Los comportamientos facilitan a los desarrolladores el mantenimiento y la actualización de un sitio, ya que no necesitan cambiar ningún código HTML subyacente. Descubra todo lo que quería saber sobre los comportamientos de Drupal en el artículo.
¿Qué son los comportamientos de Drupal?
Drupal.behaviors es un objeto dentro de la estructura de Javascript en Drupal, que nos permite adjuntar funciones para ser ejecutadas en determinados momentos durante la ejecución de la aplicación. Se llama cuando el DOM está completamente cargado, pero estos comportamientos se pueden volver a llamar. La documentación oficial de JavaScript de Drupal sugiere que los módulos deberían implementar JavaScript adjuntando lógica a Drupal.behaviors .
¿Por qué necesitamos los comportamientos de Drupal?
La ventaja de los comportamientos es que se vuelven a aplicar automáticamente a cualquier contenido que se cargue a través de AJAX. Se pueden llamar en cualquier momento con un contexto que represente nuevas incorporaciones o cambios en el DOM. Esto es mejor que $(document).ready() o document DOMContentLoaded donde el código solo se ejecuta una vez.
¿Cuándo no son deseados los comportamientos de Drupal?
Los comportamientos de Drupal no siempre son la solución perfecta para escribir Javascript en Drupal. En algunos casos, como se indica a continuación, ¡los comportamientos de Drupal no son necesarios en absoluto!
- Cuando necesitamos ejecutar algún código que no afecte al DOM. P.ej. Inicializar un script externo como Google Analytics
- Cuando se necesita realizar alguna operación JS en el DOM solo una vez sabiendo que el elemento estará disponible cuando se cargue la página (este escenario es diferente de usar una vez).
¿Cuándo se llaman Drupal Behaviors?
- Después de que se haya cargado una superposición de administración en la página.
- Después de que la API de formularios de AJAX haya enviado un formulario.
- Cuando una solicitud AJAX devuelve un comando que modifica el HTML, como ajax_command_replace() .
Otras veces cuando se invocan los comportamientos de Drupal
- CTools lo llama después de que se haya cargado un modal.
- Media lo llama después de que se haya cargado el navegador de medios.
- Panels lo llama después de que se haya completado la edición en el lugar.
- Views lo llama después de cargar una nueva página que usa AJAX.
- Views Load More lo llama después de cargar la siguiente porción de elementos.
- El JavaScript de los módulos personalizados puede llamar a Drupal.attachBehaviors() cuando agregan o cambian partes de la página.
Escribir código sin comportamientos de Drupal
En este código, agregamos un detector de eventos de clic a la clase .views-row que calcula la cantidad de veces que hacemos clic en esta fila. Pero se agrega solo una vez a los elementos que vienen en el DOM durante la carga de la página inicial. Después de hacer clic en Cargar más y cargar más elementos, el detector de clics no funciona en los elementos recién cargados.
// 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; }); }); })();
¿Cómo usamos Drupal Behaviors?
Respuesta: Usar el método de adjuntar
Cosas para recordar:
- El nuevo objeto debe tener al menos un método de conexión.
- Cada vez que se llame a Drupal.attachBehaviors, iterará a través de todos los objetos de comportamiento y llamará a sus respectivos métodos de conexión.
Agregando el comportamiento de Drupal a nuestro código
Después de agregar Drupal Behaviors, el código se parece a esto.
(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);
Pero algo extraño aparece en la parte superior cuando hacemos clic en Cargar más:
Esto se debe a que el comportamiento de Drupal se llama muchas veces y, posteriormente, obtenemos un comportamiento no deseado.
¿Qué es Contexto en “Contexto Drupal”?
- Al llamar al método de conexión para todos los comportamientos, Drupal pasa un parámetro de contexto .
- El parámetro de contexto que se pasa a menudo puede dar una mejor idea de qué elemento DOM se está procesando.
- Durante la carga de la página inicial, este será el documento HTML completo; durante las llamadas posteriores, estos serán solo los elementos que se agregan a la página o se modifican.
¿Cómo agregar contexto?
El problema anterior se puede resolver utilizando el parámetro de contexto que proporciona Drupal Behaviors. En este caso, la primera vez que se carga la página, obtenemos todo el documento HTML como contexto y ahí es cuando adjuntamos el encabezado. Para operaciones posteriores, será la parte del código la que se verá afectada por los comportamientos de Drupal y, por lo tanto, esa parte del código se controlará de forma segura.
(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);
Nuevamente, hay un comportamiento extraño cuando hacemos clic en Cargar más . Los alimentos que se cargaron inicialmente funcionan bien. Pero después de hacer clic en Cargar más, los nuevos elementos obtienen el detector de clics y funcionan normalmente. ¡Pero los elementos cargados inicialmente vuelven a conectar al oyente y al hacer clic en ellos se llama al evento de clic más de una vez!
¿Cuándo comienzan a comportarse mal los comportamientos de Drupal?
- Escribir todos los detectores de eventos dentro de los comportamientos de Drupal sin usar Once y Context.
- Declarar funciones no deseadas dentro de los comportamientos de Drupal, lo que conduce a la redeclaración de funciones cada vez que se llama al método de conexión.
“Once” al rescate
- Once garantiza que algo se procese solo una vez al agregar un atributo data-once en un elemento DOM después de que se haya ejecutado el código.
- Si se vuelve a llamar al comportamiento, el elemento con el atributo data-once se omite para su posterior ejecución.
- Once es una implementación moderna de jQuery.once (que es un esfuerzo por alejarse de jQuery)
- Once, en combinación con context, controla perfectamente toda la funcionalidad tal y como la necesitamos.
Agregar una vez para corregir los detectores de eventos en nuestro código
(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);
Ahora todo funciona según lo previsto. Obtenemos un atributo data-once para los elementos donde se adjuntan los detectores de eventos y los elementos recién cargados y los elementos cargados anteriormente funcionan correctamente.
El método Need for Detach
El método Detach actúa como un antihéroe (no malvado), eliminando todo lo que hicimos en el método de conexión. Se llamará a cualquier código en el método de separación cada vez que se elimine el contenido del DOM. Esto nos ayuda a limpiar nuestra aplicación. Por ejemplo, el método Detach nos permite eliminar detectores de eventos no deseados que consumen recursos como una situación de sondeo continuo.
Ejemplos de separar
Supongamos que tenemos que llenar un formulario ajax y estamos usando un temporizador para mostrar el tiempo transcurrido. Usamos setTimeOut para administrar el temporizador. Registramos este temporizador en la consola para monitorear.
(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);
Al enviar el formulario, el temporizador en DOM se elimina, pero la consola comienza a generar un error. Esto se debe a que el elemento sobre el que actúa setTimeOut se ha eliminado del DOM:
Para evitar esto, podemos usar el método de separación así:
(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);
Esto elimina el temporizador de descarga y, como se ve desde el registrador, no se produce el error.
Expresiones de función invocadas inmediatamente (IIFE): el contenedor para JS
Hemos estado usando IIFE para escribir nuestro código Drupal. Los paréntesis de apertura iniciales definen una función anónima que ayuda a evitar que el alcance de la función contamine el alcance global de toda la aplicación. Puede pasar argumentos a su función anónima incluyéndolos como argumentos al final de la definición de la función.
Esto también nos ayuda a asignar espacios de nombres a los parámetros como queramos que se utilicen.
Ejemplo:
// 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);
Pensamientos finales
La implementación de los comportamientos de Drupal permite una interactividad dinámica, una interacción optimizada con el usuario, mejores comentarios de los usuarios, un desarrollo eficiente y una experiencia de usuario mejorada en general de su sitio web. Los comportamientos de Drupal son flexibles y modulares, ya que se pueden ejecutar varias veces en una página, se pueden anular y extender el comportamiento existente y se pueden volver a aplicar automáticamente a cualquier contenido cargado a través de Ajax.
¿Está buscando una agencia de desarrollo de Drupal que lo ayude a crear experiencias web interactivas, aprovechando al máximo Drupal? ¡Nos encantaría hablar!