window.once name conflict with other third party script

Created on 16 December 2021, almost 3 years ago
Updated 15 August 2024, 3 months ago

Problem/Motivation

Drupal uses a new standalone library to provide the once functionality. This library is published on npmjs as the @drupal/once package. The package exposes a js module and a version compatible with browsers that don't support js modules (the "iife" version). This iife version exposes a global variable named "once", making window.once the entrypoint of the functionality, this is the version drupal core uses since IE11 support is necessary.

Some third party scripts/libraries can already declare a window.once that Drupal core ends up using instead of the intended @drupal/once script, causing errors in almost all behaviors since most of them use once.

Steps to reproduce

Add one of the following in some library definition that gets loaded on the page:

https://unpkg.com/alpinejs@3.x.x/dist/module.cjs.js: { attributes: { defer: true }, type: external, process: false }
https://cdn.flowplayer.com/releases/native/stable/plugins/chromecast.min.js: { attributes: { defer: true }, type: external, process: false }

Here the defer means those files will be loaded right before the behaviors are executed and after the once library has been loaded (so window.once will never point to the @drupal/once script).

Proposed resolution

None yet.

Remaining tasks

Find a good solution
Write tests

Original report by mrweiner

Problem/Motivation

Upon upgrading to 9.3, we are seeing a number of console errors for Uncaught TypeError: once(...).forEach is not a function from various modules including tour, toolbar, big_pipe, views, and contextual. One such example is ajax_view.js.

9.3.x, line 70: https://git.drupalcode.org/project/drupal/-/blob/9.3.x/core/modules/view...

once('exposed-form', this.$exposed_form).forEach($.proxy(this.attachExposedFormAjax, this));

Doing some logging, it looks like the calls are not always broken. When they work, once(...) appears to be an array. When they are broken, however, once(...) looks to be the Window, which is not iterable. I verified this in both ajax.js and big_pipe.js.

It seems that the culprit is that jquery.once.bc.js sets once() as a global via window.once, and if another script is loaded on the page that also sets this global then subsequent invocations of once() by core fire off this replaced version of the function, causing things to break.

Two scripts identified so far to cause the issue are https://unpkg.com/alpinejs@3.x.x/dist/module.cjs.js and https://cdn.flowplayer.com/releases/native/stable/plugins/chromecast.min.js, although there are likely others as well.

To reproduce, create a new project using the "standard" profile and add a script which modifies window.once to core/once in core.libraries.yml (or, realistically, probably to any library) as in:

once:
  remote: https://git.drupalcode.org/project/once
  version: "1.0.1"
  license:
    name: GNU-GPL-2.0-or-later
    url: https://git.drupalcode.org/project/once/-/raw/v1.0.1/LICENSE.md
    gpl-compatible: true
  js:
    assets/vendor/once/once.min.js: { weight: -19, minified: true }
    https://cdn.flowplayer.com/releases/native/stable/plugins/chromecast.min.js: { attributes: { defer: true }, type: external, process: false }
    #or
    https://unpkg.com/alpinejs@3.x.x/dist/module.cjs.js: { attributes: { defer: true }, type: external, process: false }
  dependencies:
    - core/drupal.element.matches

and then navigate to the homepage. Errors will be thrown by ajax.js and others.

Proposed resolution

Instead of setting window.once and calling to as a global, attach it to the Drupal global as Drupal.once or remove it from the global namespace by other means so as to avoid conflicts with third-party libraries.

🐛 Bug report
Status

Needs work

Version

11.0 🔥

Component
Javascript 

Last updated 2 days ago

Created by

🇺🇸United States mrweiner

Live updates comments and jobs are added and updated live.
  • Needs issue summary update

    Issue summaries save everyone time if they are kept up-to-date. See Update issue summary task instructions.

Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

Production build 0.71.5 2024