Несколько слов о Drupal.Behaviors и $(document).ready()

Сегодня неизвестный, но бдительный пользователь вновь напомнил мне о разнице между Drupal.behaviors и $(document).ready(). Причем так напомнил, что я уже обновил по горячим следам jSlider Form API. Для закрепления, напишу об этой разнице несколько слов.

Дело в том, что некоторые (и я был в их числе) принимают Drupal.behaviors за полную замену $(document).ready(), дескать, в Друпале надо вот так — и все. Виной тому, с одной стороны, невнимательность, с другой — тот факт, что в 2009-м году описание Drupal JavaScript API было не таким подробным, как теперь. Так или иначе, основная разница между ними заключается в том, что, тогда как $(document).ready() вызывается однократно, после готовности DOM, Drupal.behaviors может вызываться несколько раз: если скрипты подгружают по AJAX на страницу новые элементы и вызывают для них функцию Drupal.attachBehaviors().

При отладке с этим сталкиваешься не всегда, поэтому о разнице нетрудно забыть. Но в боевом режиме вляпаться легко. Достаточно поставить какой-нибудь Vote Up/Down — и вот уже кнопки, генерируемые нашими скриптами, при каждом голосовании генерируются вторично, а формы начинают сохраняться по несколько раз подряд.

Чтобы избежать таких глюков, необходимо в Drupal.behaviors, во-первых, использовать переменную context, и во-вторых — присваивать обработанным однажды элементам соответствующий класс и проверять его наличие при обработке. Как в данном примере, добавленном к упомянутому описанию JS в Друпале:

Drupal.behaviors.myModuleBehavior = function(context) {
  $('.module-class-object:not(.module-class-processed)', context).each(function () {
    $(this).addClass('module-class-processed');
    // Do things
  });
};

Кроме того, для глобальных задач (например, однократно добавить на страницу кнопку) можно по-прежнему пользоваться $(document).ready().

Комментарии