A guide to writing JS scripts for front-end developers for Drupal 7

There are different ways to create a layout for Drupal. Someone typeset the already darkened pages, someone tries to get by with standard themes, but as a rule, first the layout designer typeset the pages by design, and the output is a set of html files - slices. Then developers integrate these files piece by piece when teminging.
And in the process of integration there are errors, some modifications, so the layout and scripts related to it should be available for editing and testing.
It is the latter method that will be mainly discussed, I will describe typical errors and best practices for solving them when writing JS scripts for D7. I think it will be interesting for both Drupal coders and developers of modules. In the case of layout designers, the main principle that should be guided by the fact is that your script will work in the Drupal environment, and this imposes a number of limitations, ideally, the script should connect to Drupal and work without any additional modifications, while working on slices outside Drupal .


JQuery variable name


In Drupal 7, unlike version 6, you cannot just take it and access jQuery methods via $, because this variable is simply not declared.
Often you have to deal with a situation where the typesetter wrote scripts that work on the layout, or you just took a ready-made script, and integration problems begin. Most likely the problem is the use of the variable $. No need to be scared - you don’t need to go around and replace $ with jQuery everywhere.
Let's say there is a script:
$(function () {
  $('div.menu-expanded').hide();
  $(....);
});

To make it work when connecting to Drupal, you need to re-arrange it like this:
(function ($) {
  $(function () {
    $('div.menu-expanded').hide();
    $(...);
  });
}) (jQuery);

Thus, simply wrapping all the code unchanged, using the standard scope allocation mechanism, the jQuery object became available by the name of $.
It’s better to immediately explain to your typesetter such a technique in order to avoid problems in the future and not to climb his scripts with corrections.

Drupal.behaviors


Drupal has a special way to handle the document ready event, Drupal.behaviors, which provides a number of benefits. It was already in D6, just the way of writing has changed a bit.
For example, there is such a script:
$(function (){
  $('a.tooltip').someTooltipPlugin();
});

Everything is simple and clear, but what happens if the elements a, with the class of interest to us, appear on the page asynchronously? It turns out that you need to call the function with your hands, which all the handlers will re-hang, or duplicate this code in the place where the asynchronous content returns.
The behavioral mechanism offers the following concept.
All the code that should be called when the page is ready needs to be enclosed in the following construction:
(function ($) {
  Drupal.behaviors.yourName = {
    attach : function(context, settings) {
      // Your code here.
    }
  };
})(jQuery);

In this way we designated our behavior - just added a new property to the Drupal.behaviors object. Its advantage is that in addition to the call when loading the entire page, Drupal will call all behaviors, for example, when an ajax request is received (if it is a standard request, for example, updating a form or view).
All behaviors can be called manually like this:
Drupal.attachBehaviors(document, {});

Drupal will go through all the properties of Drupal.behaviors and call the attach method.
Pay attention to the first argument (I decided not to talk about the second argument since in my understanding it will not be useful to developers purely front-end). Here you need to transfer the contents (DOM element or selector) to which you need to apply ("attach") the behavior. This value will be available as the context argument in every behavior. This is necessary in order to limit the scope of the code inside the behavior.
If we take the code from the previous example, then the handler will be added to all links on the page with every call to the behavioror. But if we rewrite the code like this:
$('a...', context).someTooltipPlugin();

First, when loading the page, all links with the class on the entire page will be processed, because the first call to behaviorors happens with the document object as context.
Then, with every call to behaviorists, but only inside the newly received content.
For example, if we have a list with ajax pagination, then when we go to the second page, we are only interested in reprocessing the contents of the second page, and not the entire document.
If you write your module, you simply have to wrap your functional in behavior to enable third-party developers to “attach” your logic-behavior to their content. And at any time, and not just when loading the page.
Nuance for layout designers. In the case when the layout is prepared first, and then they plan to screw it to Drupal - the code needs to be drawn up immediately using behaviorists.
Naturally, behaviors on the layout will not work without native Drupal scripts, errors will be poured about accessing non-existent variables, etc.
To avoid this, I suggest connecting the drupal.js script, which lies in the layout of Drupal core.
This way we can write basic scripts in a form suitable for Drupal, and when applying the layout just copy them.
All the coders need to know is to write behaviorists instead of the usual document ready. And inside always use context when building selectors.

jQuery.once


If behaviors were needed in order to additionally add handlers to new content, then now consider the opposite problem. Some handlers can be assigned to the same elements twice, but this is not always necessary.
The easiest way to add a class during the first processing, and include the condition for the absence of this class in the selector.
$('a.tooltip:not(.processed)', context).addClass('processed').someTooltipPlugin();

This design inside the behavior ensures that when the code is executed again, the handler is added once.
The same mechanism is embedded in the jQuery.once plugin, which is included in the D7 core and is available by default on any page.
Using the plugin, the construction above can be replaced like this:
$(context).once('myIdentifier', function () {
  // Your code here.
});

Where myIdentifier is any unique value that will be used as that processed class.
Can be replaced with a shorter design:
$(context).once(function () {
  // Your code here.
});

In this case, the plugin itself will form a unique class.

Base url


A fairly common mistake - they forget to take into account the base url. I met her more than once in proven modules.
It is easy to recognize it - a slash at the beginning of the path:
var url = '/ ajax / my-module / some-action';
Usually they develop on the same environment, and they simply hardcode the slash, and when the base url changes, part of the scripts stops working ... Most often, an error occurs when forming the path for ajax requests and when setting the path for the attributes of the a and img tags.
It will be correct to use a special variable, which is declared by the kernel:
var url = Drupal.settings.basePath + 'ajax/my-module/some-action';


Ajax request paths


A small recommendation (in our company it’s just a standard) - when forming the path of ajax requests, start them with the word ajax - this will make it possible, if necessary, to easily separate all ajax requests from ordinary ones. For example, exclude them from the cache at once, look in the logs, etc. Then the module name through a hyphen - it will immediately tell where to look for the processor of this request. Do not place hyphens for readability of the url, even if no one sees it, it is better to adhere to the same format, because you will not build aliases of nodes with underlining?
An example of the “correct” path for this logic would be ajax / my-module / some-action.

Line output


There are a number of functions that should be used when displaying text in JS.
I will simply list the main ones, without deepening - they are all analogues of the PHP kernel functions:
// Вывод переведенной строки.
Drupal.t('text');
// Вывод "безопасного" значения строки.
Drupal.checkPlain(name);
// Склонение окончаний фразы, в зависимости от множественного/единственного числа переменной.
Drupal.formatPlural(count, singular, plural, args, options);

They are all announced in drupal.js, respectively, they will not work without it.

Also popular now: