Class naming for Javascript development in Drupal
Steven Jones
We've settled on what we think is a best practice for class naming for Javascript in Drupal – let me explain what I mean and then talk you through our reasoning.
Say we have a Drupal Javascript behavior along the lines of:
(function($, Drupal) {
Drupal.behaviors.computermindsBehavior = {
attach: function(context) {
$(context).find('.js-computerminds-behavior').once('computerminds-behavior').each(function() {
// @TODO: do something really useful with $(this).
});
}
};
})(jQuery, Drupal);
The bit that I want to discuss today is the selector in the .find method, i.e. this bit: '.js-computerminds-behavior'
Our best practice is:
- The class name is something I've decided upon myself, and I'm not using a class that Drupal drops onto an element for me.
- I've used a
js-
prefix on my selector/class name.
A custom class
I've used a custom class that I'll need to add manually to my elements in HTML so that I have a much looser coupling between the Drupal PHP output and the frontend functionality.
If you're using Drupal 8 with the classy theme (or in Drupal 7 by default) you get a lot of classes added automatically – this is so that you don't need to go around overriding template files all the time. This arguably makes writing CSS and JavaScript easier, however you'll end up with CSS and JavaScript that's not very reusable because it'll be tightly coupled to the Drupal systems generating the output.
As an example, recently my colleague - let's call him 'ComputerMinds Developer A' - added a block to a custom module and wrote some javascript similar to the behavior outline above, but with a Drupal generated selector:
(function($, Drupal) {
Drupal.behaviors.tightlyCoupledBehavior = {
attach: function(context) {
$(context).find('.block--custom-module--block-1').once('computerminds-behavior').each(function() {
// @TODO: do something really useful with $(this).
});
}
};
})(jQuery, Drupal);
The block worked wonderfully, the client was very happy, the client wanted a second, similar block, but styled a little differently.
Enter 'Computerminds Developer B' who essentially did a marvellous copy/paste of the PHP and tweaked a few strings here and there. The second block was lovely. It needed to look a tiny bit different, so some tweaked CSS was applied, but functionally it should have been identical to the first block.
But it wasn't.
Because of the class the JS was attached to, Developer B would have had to go and modify the JavaScript behavior:
(function($, Drupal) {
Drupal.behaviors.tightlyCoupledBehavior = {
attach: function(context) {
$(context).find('.block--custom-module--block-1, .block--custom-module--block-2').once('computerminds-behavior').each(function() {
// @TODO: do something really useful with $(this).
});
}
};
})(jQuery, Drupal);
Which isn't too bad, until the client wants an additional 6 extra blocks… eek.
Sadly this was missed, and 'Computerminds Developer B' didn't add the additional selectors to the JS. Oh and 'ComputerMinds Developer B' is actually 'ComputerMinds Developer A', but a few months older. Whoops.
The prefix
Class names on the web are used for two main things: styling via CSS and hooks for JavaScript. I'd argue that keeping those domains separate is a good thing. All of your visual styling comes from CSS, and your dynamic functionality comes from Javascript. Using a js-
prefix in our selector here just reinforces that separation.
If I see a js-
prefix in a template or in some HTML, I instantly know that this is do with some special functionality that only JavaScript can provide.
Keeping the js-
prefixed classes out of our styling/CSS also means that we can freely add that class to other elements on the page that need the behaviour provided by JavaScript and know that the styling of the element won't change at all.
Explicit wins for us
By being explicit with the class names we're using in our JavaScript we can hopefully make it more obvious and explicit that something interesting is happening to a set of elements on the page.
It creates a separation between the thing generating the markup and the JavaScript system applying functionality to that markup.
With this looser coupling between the data structures in Drupal and the frontend markup it can be significantly easier to refactor either side without huge unknown breakage.
And by going further and adding a js-
prefix we're declaring to the world that this is a class only for JavaScript and not CSS: hands off!