2

I am trying to console.log a message once and only once in Drupal 10. I am cutting and pasting examples from https://www.drupal.org/node/3158256 into my file and I am still seeing the message appear multiple times in the console.

I have added the dependencies to the library:

my-library:
  version: VERSION
  js:
    js/my-library.js: { }
  dependencies:
    - core/drupal
    - core/once

And I have this simple test script in which I have cut & paste the example code from https://www.drupal.org/node/3158256

(function ($, Drupal, once) {
  'use strict';

  Drupal.behaviors.myBehavior = {
    'attach': function attach(context, settings) {

      once('init-once', context === document ? 'html' : context)
      .forEach(function initOnce(doc) {
        console.log('init-once', doc);
      });

    }

  };

})(jQuery, Drupal, once);

My goal is simply to console.log a message once and only once. How can I log a message only once?

3 Answers 3

4

core/once isn't for doing something just once, but for attaching a script just once. Then it's up to your script's logic to actually do something just once. You could for example toggle a simple CSS class on the <body> tag like oops-i-did-it and check in your JS if this class already exists or not before you trigger your magic.

And your libraries definition is missing the core/jquery dependency.

Bookmark this change record: https://www.drupal.org/node/3158256, it gives you a few really nice examples on how to use once() with and without jQuery. (You know that every times you use jQuery a small kitten dies? 😿)

myfeature:
  js: 
    js/myfeature.js: {}
  dependencies:
    - core/drupal
    - core/once
(function (Drupal, once) {
  Drupal.behaviors.myfeature = {
    attach(context) {
      // '[data-myfeature]' in the next line can also be 'div.my-class'
      // or '#my-id' etc.
      const elements = once('myfeature', '[data-myfeature]', context);
      // `elements` is always an Array.
      elements.forEach(processingCallback);
    }
  };

  // `value` in the next line is your actual DOM element. Do something with
  // `value` like `value.classList.add('hello-world')`.
  function processingCallback(value, index) {}
}(Drupal, once));
1
  • 1
    Haven’t heard the jQuery killing kittens thing for literally years, really made me chuckle 😀
    – Clive
    Commented Dec 26, 2023 at 19:33
1

I found something that works:

(function ($, Drupal, once) {
  'use strict';

  Drupal.behaviors.myBehavior = {
    'attach': function attach(context, settings) {

      if ($(once('my-test', 'body')).length) {
        console.log('my test');
      }

    }
  };

})(jQuery, Drupal, once);

Though I can't say I understand the syntax. It seems quite verbose and more difficult to grasp than Jquery .once().

2
  • This isn’t good code - jquery isn’t needed, and converting to a jquery object just to test the length, when once returns a testable result, is just over-engineering. Leymannx’s answer is v. good, that should be preferred
    – Clive
    Commented Dec 26, 2023 at 19:32
  • Jquery is needed, or at least preferable, for the actual feature I was building up to -- but not for the highly simplified console.log example in my answer above. I ended up not needing once for the actual feature, though the answer from leymannx will be a useful reference the next time I consider using once.
    – arnoldbird
    Commented Jan 8 at 14:10
-1

Just another example:

(function ($, Drupal, once) {
 "use strict";
 Drupal.behaviors.something = {
   attach (context, settings) {
   var resButton = once('#my-button-id', 'body', context);
   $(resButton).on('click', function() {
       alert('click');
   });
   // more stuff here
  
   }
 };
})(jQuery, Drupal, once);

and your library definition has:

dependencies:
  - core/drupal
  - core/jquery
  - core/once

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.