Domar JavaScript no Drupal (inclui perguntas frequentes)

Publicados: 2023-02-07

Experiências interativas na Web fornecem uma experiência mais envolvente e agradável para os usuários. Isso leva ao aumento da satisfação do usuário e a uma percepção positiva de um site. Por exemplo, um formulário que fornece feedback e validação instantâneos, em vez de fazer o usuário esperar por uma atualização de página, pode melhorar significativamente a experiência do usuário.

O JavaScript desempenha um papel importante no Drupal, fornecendo os meios para criar experiências dinâmicas e interativas para usuários no front-end de um site Drupal. Ele permite que os desenvolvedores modifiquem o comportamento de determinados elementos em uma página, como formulários, links ou qualquer outro elemento DOM, sem precisar atualizar a página inteira. Drupal Behaviors são funções JavaScript que são executadas quando eventos específicos ocorrem em uma página. Os comportamentos tornam mais fácil para os desenvolvedores manter e atualizar um site, pois não precisam alterar nenhum código HTML subjacente. Descubra tudo o que você queria saber sobre Drupal Behaviors no artigo.

Domando JavaScript no Drupal

O que são comportamentos Drupal?

Drupal.behaviors é um objeto dentro da estrutura Javascript do Drupal, que permite anexar funções a serem executadas em determinados momentos durante a execução da aplicação. Ele é chamado quando o DOM está totalmente carregado, mas esses comportamentos podem ser chamados novamente. A documentação oficial do JavaScript do Drupal sugere que os módulos devem implementar o JavaScript anexando a lógica ao Drupal.behaviors .

Por que precisamos de comportamentos Drupal?

A vantagem dos Behaviors é que eles são reaplicados automaticamente a qualquer conteúdo carregado por meio do AJAX. Eles podem ser chamados a qualquer momento com um contexto que representa novas adições ou mudanças no DOM. Isso é melhor do que $(document).ready() ou document DOMContentLoaded onde o código é executado apenas uma vez.

Quando os comportamentos do Drupal são indesejados?

Os comportamentos do Drupal nem sempre são a solução perfeita para escrever Javascript no Drupal. Em alguns casos, conforme indicado abaixo, os comportamentos do Drupal não são necessários!

  • Quando precisamos executar algum código que não afeta o DOM. Por exemplo. Inicializando um script externo como o Google Analytics
  • Quando alguma operação JS precisa ser executada no DOM apenas uma vez, sabendo que o elemento estará disponível quando a página carregar (este cenário é diferente de usar uma vez).

Quando são chamados os Comportamentos Drupal?

  • Depois que uma sobreposição de administração foi carregada na página.
  • Depois que a API de formulário AJAX tiver enviado um formulário.
  • Quando uma solicitação AJAX retorna um comando que modifica o HTML, como ajax_command_replace() .

Outras vezes, quando Drupal Behaviors são invocados

  • CTools o chama depois que um modal foi carregado.
  • A mídia o chama depois que o navegador de mídia é carregado.
  • O Panels o chama após a conclusão da edição no local.
  • Views o chama depois de carregar uma nova página que usa AJAX.
  • Views Load More o chama depois de carregar o próximo bloco de itens.
  • O JavaScript de módulos personalizados pode chamar Drupal.attachBehaviors() ao adicionar ou alterar partes da página.

Escrevendo código sem comportamentos do Drupal

Neste código, estamos adicionando um ouvinte de evento click à classe .views-row que calcula o número de vezes que clicamos nesta linha. Mas é adicionado apenas uma vez aos elementos que vêm no DOM durante o carregamento inicial da página. Depois de clicar em Carregar mais e carregar mais itens, o ouvinte de clique não funciona nos itens recém-carregados.

 // 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; }); }); })(); 
ouvinte de eventos

Como usamos os Comportamentos do Drupal?

Resposta: Usando o método attach

Coisas para lembrar:

  • O novo objeto precisa ter pelo menos um método attach.
  • Sempre que Drupal.attachBehaviors for chamado, ele irá percorrer todos os objetos de comportamento e chamar seus respectivos métodos de anexação.

Adicionando o comportamento do Drupal ao nosso código

Depois de adicionar Drupal Behaviors, o código fica mais ou menos assim.

 (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);

Mas algo estranho aparece no topo quando clicamos em Load More:

comportamento

Isso ocorre porque o comportamento do Drupal é chamado várias vezes e, subsequentemente, obtemos algum comportamento não intencional.

O que é Contexto no “contexto Drupal”?

  • Ao chamar o método attach para todos os comportamentos, o Drupal passa um parâmetro de contexto .
  • O parâmetro de contexto que é passado geralmente pode dar uma ideia melhor de qual elemento DOM está sendo processado.
  • Durante o carregamento inicial da página, este será o documento HTML completo; durante as chamadas subseqüentes, serão apenas os elementos que estão sendo adicionados à página ou modificados.

Como adicionar Contexto?

O problema anterior pode ser resolvido usando o parâmetro context fornecido pelo Drupal Behaviors. Nesse caso, na primeira vez que a página é carregada, obtemos todo o documento HTML como contexto e é quando anexamos o cabeçalho. Para outras operações, será a parte do código que é afetada pelos Comportamentos do Drupal e, portanto, essa parte do código é controlada com segurança.

 (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);

Novamente, há um comportamento estranho quando clicamos em Carregar mais . Os alimentos que foram carregados inicialmente funcionam bem. Mas depois de clicar em Load More, os novos itens pegam o click listener e funcionam normalmente. Mas os itens carregados inicialmente atraem o ouvinte novamente e clicar neles chama o evento click mais de uma vez!

Carregue mais

Quando os Comportamentos do Drupal começam a se comportar mal?

  • Gravar todos os ouvintes de evento dentro dos comportamentos do Drupal sem usar Once e Context.
  • Declarar funções indesejadas dentro de comportamentos do Drupal que levam à redeclaração de funções toda vez que o método attach é chamado.

“Uma vez” para o resgate

  • Uma vez garante que algo seja processado apenas uma vez adicionando um atributo data-once em um elemento DOM após a execução do código.
  • Se o comportamento for chamado novamente, o elemento com o atributo data-once será ignorado para posterior execução.
  • Once é uma implementação moderna do jQuery.once (que é um esforço para se afastar do jQuery)
  • Uma vez, em combinação com o contexto, controla toda a funcionalidade perfeitamente conforme precisamos.

Adicionando uma vez para corrigir os ouvintes de eventos em nosso 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);
dados uma vez

Agora tudo funciona como pretendido. Obtemos um atributo data-once para os elementos onde os ouvintes de evento estão anexados e os elementos recém-carregados e os elementos carregados anteriormente funcionam corretamente.

O método Need for Detach

O método Detach age como um anti-herói (não mal), removendo tudo o que fizemos no método attach. Qualquer código no método de desconexão será chamado sempre que o conteúdo for removido do DOM. Isso nos ajuda a limpar nosso aplicativo. Por exemplo, o método Detach nos permite remover ouvintes de eventos indesejados que consomem recursos como uma situação de pesquisa contínua.

Exemplos de desanexar

Suponha que temos um formulário ajax para preencher e estamos usando um cronômetro para mostrar o tempo decorrido. Usamos setTimeOut para gerenciar o cronômetro. Registramos esse cronômetro no console para monitoramento.

 (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);
definir tempo limite

No envio do formulário, o cronômetro no DOM é removido, mas o console começa a gerar um erro. Isso ocorre porque o elemento no qual o setTimeOut está atuando foi removido do DOM:

separar

Para evitar isso, podemos usar o método detach assim:

 (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);

Isso remove o cronômetro no descarregamento e, como visto no registrador, o erro não ocorre.

Demonstração JS

Expressões de função invocadas imediatamente (IIFE) - O wrapper para JS

Temos usado o IIFE para escrever nosso código Drupal. Os parênteses iniciais de abertura definem uma função anônima que ajuda a evitar que o escopo da função polua o escopo global de todo o aplicativo. Você pode passar argumentos para sua função anônima incluindo-os como argumentos no final da definição da função.

Isso também nos ajuda a nomear os parâmetros da maneira que queremos que sejam usados.

Exemplo:

 // 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);

Pensamentos finais

A implementação de comportamentos do Drupal permite interatividade dinâmica, interação simplificada do usuário, feedback aprimorado do usuário, desenvolvimento eficiente e experiência geral aprimorada do usuário em seu site. Drupal.behaviors são flexíveis e modulares, pois podem ser executados várias vezes em uma página, podem substituir e estender o comportamento existente e podem ser reaplicados automaticamente a qualquer conteúdo carregado por meio de Ajax.

Procurando uma agência de desenvolvimento Drupal para ajudá-lo a criar experiências interativas na web, tirando o melhor proveito do Drupal? Adoraríamos conversar!