Skip to content
Menu
Open World News Open World News
  • Privacy Policy
Open World News Open World News
Specbee: Taming JavaScript in Drupal (Includes FAQs)

Specbee: Taming JavaScript in Drupal (Includes FAQs)

Posted on February 7, 2023 by Michael G

Author:
Source

Taming JavaScript in Drupal (Includes FAQs)
Sagar Chauhan
07 Feb, 2023





Interactive web experiences provide a more engaging and enjoyable experience for users. It leads to increased user satisfaction and a positive perception of a website. For example, a form that provides instant feedback and validation, rather than making the user wait for a page refresh, can significantly improve the user experience.

JavaScript plays an important role in Drupal by providing the means to create dynamic and interactive experiences for users on the frontend of a Drupal website. It enables developers to modify the behavior of certain elements on a page, such as forms, links, or any other DOM elements, without having to refresh the entire page. Drupal Behaviors are JavaScript functions that get executed when specific events occur on a page. Behaviors make it easy for developers to maintain and upgrade a site as they don’t need to change any underlying HTML code. Find out all you wanted to know about Drupal Behaviors in the article.

Specbee: Taming JavaScript in Drupal (Includes FAQs)

What are Drupal Behaviors?

Drupal.behaviors is an object inside the Javascript structure in Drupal, which allows us to attach functions to be executed at certain times during the execution of the application. It is called when the DOM is fully loaded, but these behaviors can be called again. Drupal’s official JavaScript documentation suggests that modules should implement JavaScript by attaching logic to Drupal.behaviors.

Why do we need Drupal behaviors?

The advantage of Behaviors is that they are automatically re-applied to any content that is loaded through AJAX. They can be called at any time with a context that represents new additions or changes to the DOM. This is better than $(document).ready() or document DOMContentLoaded where the code is just run once.

When are Drupal behaviors unwanted?

Drupal behaviors are not always the perfect solution for writing Javascript in Drupal. In some cases, as stated below Drupal behaviors are not needed at all!

  • When we need to execute some code that does not affect the DOM. Eg. Initializing an external script like Google Analytics
  • When some JS operation needs to be performed on the DOM just once knowing that the element will be available when the page loads (This scenario is different from using Once).

When are Drupal Behaviors called?

  • After an administration overlay has been loaded into the page.
  • After the AJAX Form API has submitted a form.
  • When an AJAX request returns a command that modifies the HTML, such as ajax_command_replace().

Other times when Drupal Behaviors are invoked

  • CTools calls it after a modal has been loaded.
  • Media calls it after the media browser has been loaded.
  • Panels calls it after in-place editing has been completed.
  • Views calls it after loading a new page that uses AJAX.
  • Views Load More calls it after loading the next chunk of items.
  • JavaScript from custom modules may call Drupal.attachBehaviors() when they add or change parts of the page.

Writing code without Drupal behaviors

In this code, we are adding a click event listener to the .views-row class which calculates the number of times we are clicking on this row. But it is added only once to the elements which come in the DOM during the initial page load. After clicking on Load More and loading more items, the click listener does not work on the newly loaded items.

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


   });


 });


})();

event listener

How do we use Drupal Behaviors?

Answer: Using the attach method

Things to remember:

  • The new object needs to have at least an attach method. 
  • Anytime Drupal.attachBehaviors is called, it will iterate through all behavior objects and call their respective attach methods.

Adding Drupal behavior to our code

After adding Drupal Behaviors, the code looks something like this.

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

 But something odd appears in the top when we click on Load More:

behavior

 

This is because Drupal behavior is called a lot of times and subsequently we get some unintended behavior.

What is Context in “Drupal context”?

  • When calling the attach method for all behaviors, Drupal passes along a context parameter. 
  • The context parameter that is passed can often give a better idea of what DOM element is being processed.
  • During the initial page load this will be the complete HTML Document; during subsequent calls, this will be just the elements that are being added to the page or get modified.

How to add Context?

The previous problem can be solved by using the context parameter that is provided by Drupal Behaviors. In this case, the first time the page loads, we get the whole HTML Document as context and that’s when we attach the header. For further operations, it will be the part of the code which is affected by Drupal Behaviors and hence that part of the code is safely controlled.

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

Again there is some odd behavior when we click on Load More. The food items which were initially loaded work fine. But After clicking on Load More, the new items get the click listener and work normally. But the initially loaded items get the listener attached again and clicking on them calls the click event more than once!

Load more

 

When do Drupal Behaviors start misbehaving?

  • Writing all the event listeners inside Drupal behaviors without using Once and Context.
  • Declaring unwanted functions inside Drupal behaviors which leads to the redeclaration of functions every time the attach method is called.

“Once” to the rescue

  • Once ensures that something is processed only once by adding a data-once attribute in a DOM element after the code has been executed.
  • If the behavior is called again, the element with the data-once attribute is skipped for further execution.
  • Once is a modern implementation of jQuery.once (which is an endeavor to move away from jQuery)
  • Once, in combination with context, controls the entire functionality perfectly as we need it.

Adding Once to fix the event listeners in our code

(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

 

Now everything works as intended. We get a data-once attribute to the elements where the event listeners are attached and newly loaded elements and previously loaded elements function properly.

The Need for Detach method

The Detach method acts like an anti-hero (not evil), removing whatever we did in the attach method. Any code in the detach method will be called whenever content is removed from the DOM. This helps us to clean up our application. For example, Detach method enables us to remove unwanted event listeners which consume resources like a continuous polling situation.

Examples of Detach

Assume that we have an ajax form to fill and we are using a timer to show the time elapsed. We use setTimeOut to manage the timer. We log this timer in the console for monitoring.

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

set timeout

 

On form submission, the timer on DOM gets removed but the console starts throwing an error. This is because the element on which the setTimeOut is acting has been removed from DOM:

detach

 

To avoid this we can use the detach method like this:

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

This removes the timer on unload and as seen from the logger, the error does not occur.

JS Demo

Immediately Invoked Function Expressions (IIFE) – The wrapper for JS

We have been using IIFE to write our Drupal code. The initial opening parentheses define an anonymous function which helps prevent the function’s scope from polluting the global scope of the entire application. You can pass arguments to your anonymous function by including them as arguments at the end of the function definition. 

This also helps us to namespace the parameters however we want them to be used.

Example:

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

Final Thoughts

Implementing Drupal behaviors allows for dynamic interactivity, streamlined user interaction, improved user feedback, efficient development and overall enhanced user experience of your website. Drupal.behaviors are flexible and modular, in that they can be executed multiple times on a page, can override and extend existing behavior, and can be automatically re-applied to any content loaded through Ajax. 

Looking for a Drupal development agency to help you build interactive web experiences, making the best out of Drupal? We’d love to talk!

Author: Sagar Chauhan

Meet Sagar Chauhan, Lead Engineer, Acquia-certified Frontend Developer, soccer player, and a huge Arsenal fan. Sagar loves long train journeys and checking out latest gadget reviews. If you want to invite him for a meal (and you don’t know how to cook), Maggi would be perfect! 🙂





Drupal
Drupal Development
Drupal Planet
CSS/JS

 

Recent Blogs

Image
JavaScript in Drupal teaser

Taming JavaScript in Drupal (Includes FAQs)

Image
Malabya Career Story

From Aspirations to Accomplishments – Malabya Tewari’s Career Story

Image
Custom module add css drupal 9 teaser

How to Create a Custom Module and add CSS Libraries in Drupal 9

Want to extract the maximum out of Drupal?
TALK TO US

Featured Success Stories

semi-casestudy

A Drupal powered multi-site, multi-lingual platform to enable a unified user experience at SEMI.

ux-magazine

Discover how our technology enabled UX Magazine to cater to their massive audience and launch outreach programs.

Flipkart-client

Discover how a Drupal powered internal portal encouraged the sellers at Flipkart to obtain the latest insights with respect to a particular domain.

VIEW ALL CASE STUDIES

Read more

Related Posts:

  • Dries Buytaert: A Composable Digital Experience Manifesto
    Dries Buytaert: A Composable Digital Experience Manifesto
  • Specbee: What You Need To Know About Continuous Integration and Testing in Drupal
    Specbee: What You Need To Know About Continuous Integration…
  • Specbee: Migrate to Drupal 9 (or 10) Without Losing Your Hard-Earned SEO Ranking
    Specbee: Migrate to Drupal 9 (or 10) Without Losing Your…
  • Envato Tuts+: Top 20 Most Usable Content Management Systems
    Envato Tuts+: Top 20 Most Usable Content Management Systems
  • Specbee: The Ultimate Guide to Jumpstart your Drupal Contribution Journey
    Specbee: The Ultimate Guide to Jumpstart your Drupal…
  • Andy Wingo: a world to win: webassembly for the rest of us
    Andy Wingo: a world to win: webassembly for the rest of us

Recent Posts

  • Domaćice sa Bosfora – 69 Epizoda
  • Família de jovem morto por Bruno Krupp protesta contra a soltura do modelo
  • Rekomendasi Body Scrub – Shinzui VS Scarlett Mana Yang Terbaik | Makeup Review
  • Minister Malla Reddy Praises Telangana State For Receiving National Panchayat Awards| V6 News
  • Noah’s Island Noah’s Island S02 E010 Bad Gnus on Noah’s Island

Categories

  • Android
  • Linux
  • News
  • Open Source
©2023 Open World News | Powered by WordPress & Superb Themes
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept All”, you consent to the use of ALL the cookies. However, you may visit "Cookie Settings" to provide a controlled consent.
Cookie SettingsAccept All
Manage consent

Privacy Overview

This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary
Always Enabled
Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
CookieDurationDescription
cookielawinfo-checkbox-analytics11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".
cookielawinfo-checkbox-functional11 monthsThe cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
cookielawinfo-checkbox-necessary11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".
cookielawinfo-checkbox-others11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.
cookielawinfo-checkbox-performance11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".
viewed_cookie_policy11 monthsThe cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.
Functional
Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
Performance
Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
Analytics
Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
Advertisement
Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.
Others
Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet.
SAVE & ACCEPT