En Drupal la forma correcta de agregar código de JavaScript es por medio de los Drupal.behaviors, estos se ejecutan cada vez que el DOM es totalmente cargado, es decir que todos los elementos de la página ya están presentes.
Otro momento en el que estos se ejecutan es después de una respuesta AJAX, porque estas suelen alterar el HTML existente en la página.
Así se ve un Behavior y como puedes ver 2 argumentos son enviados:
Drupal.behaviors.myBehavior = {
attach: function (context, settings) {
//Using once() to apply the myCustomBehaviour effect when you want to run just one function.
once('myCustomBehavior', 'input.myCustomBehavior', context).forEach(function (element) {
element.classList.add('processed');
});
}
};
Context: Aquí está DOM, la primera vez que carga la página será todo el documento, pero cuando es por un AJAX solo contiene el HTML insertado.
Settings: Todas las "Settings" enviadas desde el código php.
Usando la variable context adecuadamente
Como te explique anteriormente la variable context contiene el DOM y es necesario usarla sabiamente para evitar situaciones extrañas, por ejemplo cuando estás agregando un EventListener a un botón de la siguiente manera:
document.querySelector('#mi-boton').addEventListener("click", function (e) {
// codigo a ejecutar
});
Si este código se encuentra dentro de un Behavior es posible que se ejecute más de una vez, entonces estarías agregando este evento varias veces al botón y al hacerle clic la función también se ejecutaría múltiples veces causando efectos no deseados.
Lo correcto en este caso es usar la variable context para que tu selector no busque el elemento en todo el documento siempre, sino que lo busque únicamente en el DOM insertado, en ese caso cada vez que este Behavior se vuelva a ejecutar tus eventos no se agregaran de nuevo a los elementos ya existentes.
Solo con reemplazar document por context tu código ya quedará bien
context.querySelector('#mi-boton').addEventListener("click", function (e) {
// codigo a ejecutar
});
¿Para qué se ejecutan con cada respuesta de AJAX?
Un ejemplo sencillo seria cuando tienes un formulario que te muestra un selector de lista de países y otro para ciudades que contiene solamente las del país seleccionado, en Drupal esto se logra utilizando AJAX en los elementos de formulario para que al cambiar el país se obtenga la lista de ciudades correspondiente y se reemplace la lista existente de ciudades.
Ahora pensemos que quieres emplear alguna librería de JS que hace que las listas se vean de una forma especial, algo que es muy común para unificar los estilos de los elementos de formulario.
Normalmente dichas librerías requieren que corras alguna función sobre los elementos y si esto pasara solo al momento de cargar la página, entonces cuando el usuario cambie el país y se regenere el selector de ciudades, este perderá su apariencia mejorada.
Por esto es importante que el código se ejecute de nuevo, pero únicamente sobre los elementos que fueron agregados o reemplazados en la página, para que el código de JS que los mejora vuelva a aplicarse sobre ellos.
El siguiente sería el código y lo explicaré paso a paso:
Drupal.behaviors.selectLists = {
attach: function (context, settings) {
context.querySelectorAll('select').forEach( function (select) {
styleSelect(select);
}):
}
};
- El Behavior debe tener un nombre en formato cameCase en este caso es selectLists.
- Se pueden usar un selector que apunta a todos los elementos tipo select en tu página y que itera sobre cada uno de ellos.
- Utilizando la variable context al cargar la página encontrará todos los elementos tipo select, cuando cambia el país y el listado de ciudades sea reemplazado este vendrá dentro de context, lo que hará que también sea encontrado sin afectar a los elementos cargados inicialmente.
- Se ejecuta la función styleSelect en cada elemento encontrado.
Relacionado con los Behaviors existe otra forma de forzar el código a correr solo una vez, esto lo expliqué en otro artículo que te dejo por aquí acerca de Drupal Once.
Conclusión
Ejecutar código JS en Drupal es algo que se necesita con mucha frecuencia y usando los Drupal.Behaviors correctamente, ahorraras muchos dolores de cabeza y facilitaras el trabajo.