Drupal behaviors are added twice

Created on 28 July 2023, about 1 year ago
Updated 30 October 2023, 12 months ago

Problem/Motivation

In Drupal 10.1 (not 10.0), Drupal behaviors are added twice on page load, if the library is attached to a document node element.

  • Added when the JavaScript file is added to the DOM (unintended).
  • Added when drupal.init.js attaches the behaviors to the DOM (intended).

On drupal < 10.1:

On drupal >= 10.1:

Steps to reproduce

Add a js file to page for example:

function hook_preprocess_page(&$variables) {
$variables['#attached]['library'][] = my_library;
}

A regular js file:

(function ($, Drupal) {
  console.log('File added');
  Drupal.behaviors.custom_behavior = {
    attach: function (context, settings) {
      // Make sure behavior is added only once.
      if (context !== document) {
        return;
      }
      console.log(context);
    }
  }
})(jQuery, Drupal);

This will return something similar to the second screenshot.

Proposed resolution

To be investigated.

💬 Support request
Status

Closed: works as designed

Version

10.1

Component
Javascript 

Last updated 5 days ago

Created by

🇭🇷Croatia Aporie

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @Aporie
  • Which commit in 10.1.0 changed the behavior?

  • 🇭🇷Croatia Aporie

    If I knew ...

  • Do you have the capability of conducting a git bisect operation? That will identify the commit.

    Also the release notes and change records may prove useful.

  • 🇭🇷Croatia Aporie

    I'll see if I have some time next week. But there are 1588 commits to go ...

  • If 10.0.9 differs from 10.1.1 by 1588 commits (I did not check), it should take git bisect only about 10 attempts to find the commit.

  • 🇫🇷France nod_ Lille

    Is bigpipe enabled? if yes issue is probably related to 🐛 Use document in bigpipe first attachBehaviors call Fixed and/or 📌 Use Mutation observer for BigPipe replacements Fixed

    The supported way to make sure behaviors are run only once is to use once: https://www.drupal.org/node/3158256

  • 🇫🇷France nod_ Lille

    Based on the title, this is an expected behavior (pin intended)

  • Status changed to Needs review about 1 year ago
  • Status changed to Closed: works as designed about 1 year ago
  • 🇭🇷Croatia Aporie

    Hi nod_,

    Thanks for helping, and it's indeed linked to big_pipe.

    Well, I took a look and, too much work (moreover core related) has been made for me to grasp what is the exact reason of this "issue".

    I think the closest would be 🐛 The attachBehaviors() for document is only called after Big Pipe chunks are processed Fixed

    Though, these commits have been removed since then.

    I just want to add myself to the list of persons in the mentioned ticket relating how big this impact could have on contrib modules and current websites. I'm not sure if this change is due to switching on mutation observer and what is the exact reason to attach all Behaviors before drupal.init.js does it again.

    What I can tell, though, is that big_pipe was working before these changes were introduced, and it works also now (on 10.1), but all behaviors are added twice on page load.

    Indeed, relying on once is an option and will probably be the way to go, but it used not to be the case. For any JavaScript code that wouldn't need to be executed on ajax responses, we could just target the dom document on load and that was it. Plus, I do think the new changes have a browser performance impact (as now all attached dom functions will have to be checked for once requirement).

    Even though, again, I don't have a clear vision of the changes. If that's the way to go, then let's go for once everywhere. But if there is a way to get back to having all behaviors only added once on page load, then I'd vote for this solution.

    I'm closing this ticket for now.

  • 🇨🇦Canada ambient.impact Toronto

    @Aporie This a bit off topic but (emphasis mine):

    Indeed, relying on once is an option and will probably be the way to go, but it used not to be the case. For any JavaScript code that wouldn't need to be executed on ajax responses, we could just target the dom document on load and that was it.

    You shouldn't assume that behaviours will only be attached on a full page load, even if you wrote the behaviour yourself. A great example is what happens when you try to integrate Hotwire Turbo into Drupal, which I'm in the process of doing for the RefreshLess module (see 📌 Hotwire Turbo minimum viable implementation Active ) - all the JavaScript that people assumed would only ever get attached on load now turns into a nightmare to deal with when swapping out page content dynamically because they either didn't include a detach, they didn't use the context parameter to properly scope their attach, etc.

Production build 0.71.5 2024