4

I am defining a d3.js script for my module like this:

test:
  js:
    js/test.js: {}
  dependencies:
    - mymodule/d3
    - core/drupalSettings
d3:
  remote: https://d3js.org
  version: "4.x"
  license:
    name: BSD
    url: https://opensource.org/licenses/BSD-3-Clause
    gpl-compatible: true
  js:
    https://d3js.org/d3.v4.min.js: { type: external, minified: true }

In my controller, I am sending my data (in $data) to the script, like this:

$page[] = array(
  '#markup' => 'foo',
  '#attached' => array(
    'library' =>  array(
      'mymodule/test',
    ),
    'drupalSettings' => array(
      'mymodule' => array(
        'test' => array(
          'data' => $data,
        ),
      ),
    ),
  )
);

It's working fine, but I've noticed that most js files in core have a wrapper - there is some information here.

Should I be using a wrapper for my script? and if so, what should the wrapper be? and why?

There is no jQuery in my script:

(function (Drupal, drupalSettings, d3) {
  Drupal.behaviors.mymoduleTest = {
    attach: function (context, settings) {

      const data = JSON.parse(drupalSettings.mymodule.test.data);

      ...

      console.log(data);

    }
  };
})(Drupal, drupalSettings, d3);

I'm wrapping my code, with these three lines top an bottom, but I don't understand if I need any of it?

0

1 Answer 1

4

There are a couple of different things at play here.

This:

(function (Drupal, drupalSettings, d3) {

})(Drupal, drupalSettings, d3));

is an Immediately Invoked Function Expression (IIFE). Quoting gion_13 from the canonical SO post on the subject:

This pattern is often used when trying to avoid polluting the global namespace, because all the variables used inside the IIFE (like in any other normal function) are not visible outside its scope.

That's why you'll see it so often in Drupal's (and pretty much any other framework or library's) JS code; it's generally considered good practice.

Ben Alman's blog post is a must read if you're not familiar with this pattern. Yes, it's a good idea to use it in your own code.

This:

Drupal.behaviors.mymoduleTest = {
    attach: function() {

    }
};

is a Drupal "behavior". Quoting the docs:

Any object defined as a property of Drupal.behaviors will get its attach() method called when the DOM has loaded both initially and after any AJAX calls.

This allows your code to re-run on elements that may have been replaced as a result of an AJAX or other JS event, so you don't need to keep track of that.

Say you had a d3 chart bound to an element in the content area, and an AJAX operation on the page replaces the content area with new HTML; the d3 bindings will be lost, and the chart will no longer show. Assuming the AJAX op was invoked by Drupal's API in some way, your attach method will be invoked automatically, and the chart will be re-bound to the new element in the content area.

It's a good idea to conform to this pattern when writing JS code for a Drupal site, it has the potential to save time and headaches later on.

As always it'll pay to use common sense with stuff like this; if you find yourself using these patterns, then immediately trying to get around them for a couple of specific requirements, those situations may be exceptions where it's best not to use them.

2
  • Nice explanation :-) So, as a general rule, its always best to use the the first line of this pattern - the IIFE - because that namespaces my code that follows. If AJAX calls are being made on my page, I should include the other two lines - Drupal.behaviours - because it can save me pulling my hair out later on. If there are no AJAX calls, I can leave this out.
    – dbj44
    Commented May 8, 2017 at 19:54
  • Yes to the IIFE thing, I may have overstated the role of AJAX in behaviors a bit...they also allow your code to be run when the DOM is first ready, so you don't have to negotiate that event yourself (or include another library that does). If you don't need that, then a behavior is technically unnecessary I guess, yeah
    – Clive
    Commented May 8, 2017 at 23:03

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.