Руководство по написанию JS скриптов для front-end разработчиков под Drupal 7

Существуют разные способы создания верстки под Drupal. Кто-то верстает уже затемленные страницы, кто-то пытается обойтись стандартными темами, но как правило, сначала верстальщик верстает страницы по дизайну, и на выходе получается набор html файлов — слайсов. Затем девелоперы интегрируют эти файлы по кусочкам при теминге.
Причем в процессе интеграции находятся ошибки, какие-то модификации, поэтому верстка и скрипты к ней относящиеся, должны быть доступными для правок и тестирования.
Именно о последнем способе в основном будет идти речь, я опишу типичные ошибки и бест-практики по их решению при написании JS-скриптов для D7. Думаю это будет интересно как верстальщикам под Drupal, так и разработчикам модулей. В случае верстальщиков основным принципом, которым нужно руководствоваться будет факт, что ваш скрипт будет работать в окружении Drupal, и это накладывает ряд ограничений, в идеале скрипт должен подключаться к Drupal и работать без каких-либо дополнительных модификаций, при этом работать на слайсах вне Drupal.


Имя переменной jQuery


В Drupal 7, в отличие от 6 версии нельзя просто так взять, и обратиться к методам jQuery через $, потому что эта переменная просто не объявлена.
Часто приходится сталкиваться с ситуацией, когда верстальщик написал скрипты, которые на верстке работают, либо вы просто взяли готовый скрипт, а при интеграции начинаются проблемы. Скорее всего проблема в использовании переменной $. Пугаться не надо — ходить и везде заменять $ на jQuery не нужно.
Допустим есть скрипт:
$(function () {
  $('div.menu-expanded').hide();
  $(....);
});

Чтобы он заработал при подключении в Drupal его нужно переоформить так:
(function ($) {
  $(function () {
    $('div.menu-expanded').hide();
    $(...);
  });
}) (jQuery);

Таким образом, просто обернув весь код без изменений, с помощью стандартного механизма выделения scope, объект jQuery стал доступен по имени $.
Лучше сразу объяснить вашему верстальщику такой прием, чтобы в будущем избежать проблем и не лазить по его скриптам с исправлениями.

Drupal.behaviors


В Drupal есть специальный способ обработки события document ready, Drupal.behaviors, который предоставляет ряд преимуществ. В D6 он уже был, просто немного поменялся способ написания.
Например, есть такой скрипт:
$(function (){
  $('a.tooltip').someTooltipPlugin();
});

Все просто и понятно, но что будет если элементы а, с интересующим нас классом появляются на странице асинхронно? Выходит, что нужно руками вызвать функцию, которая заново навесит все обработчики, либо дублировать этот код в месте возврата асинхронного контента.
Механизм бихевиоров предлагает следующую концепцию.
Весь код, который должен вызваться при готовности страницы нужно заключить в следующую конструкцию:
(function ($) {
  Drupal.behaviors.yourName = {
    attach : function(context, settings) {
      // Your code here.
    }
  };
})(jQuery);

Таким способом мы обозначили свой бихевиор — просто добавили новое свойство в объект Drupal.behaviors. Его преимущество в том, что помимо вызова при загрузке всей страницы, Drupal вызовет все бихевиоры например, при получении ответа ajax запроса (в случае если это стандартный запрос, например обновление формы или вью).
Все бихевиоры можно вызвать вручную так:
Drupal.attachBehaviors(document, {});

Drupal пройдет по всем свойствам Drupal.behaviors и вызовет метод attach.
Обратите внимание на первый аргумент (про второй аргумент я решил не рассказывать так как он в моем понимании чисто front-end разработчикам не будет полезен). Сюда нужно передавать содержимое (DOM-элемент или селектор), к которому нужно применить(«приаттачить») бихевиор. Это значение будет доступно в качестве аргумента context в каждом бихевиоре. Нужно это для того, что бы ограничить область действия кода внутри бихевиора.
Если взять код из предыдущего примера, то обработчик будет добавляться ко всем ссылкам на странице при каждом вызове бихевиора. Но если мы перепишем код так:
$('a...', context).someTooltipPlugin();

То сначала при загрузке страницы будут обработаны все ссылки с классом на всей странице, потому что первый вызов бихевиоров происходит с объектом document в качестве контекста.
Затем при каждом вызове бихевиоров, но только внутри вновь полученного контента.
Например, если у нас есть список с ajax пагинацией, то при переходе на вторую страницу нас интересует для повторной обработки только содержимое второй страницы, а не всего документа.
Если вы пишите свой модуль вы просто обязаны ваш функционал завернуть в бихевиор, что бы дать возможность сторонним разработчикам «приаттачить» вашу логику-поведение к их контенту. Причем в любой момент, а не только при загрузке страницы.
Нюанс для верстальщиков. В случае, когда сначала готовится верстка, а потом ее планируют прикручивать к Drupal — код нужно оформлять сразу с использованием бихевиоров.
Естественно на верстке бихевиоры работать без родных скриптов Drupal не будут, будут сыпаться ошибки об обращении к несуществующим переменным и т.д.
Чтобы этого избежать, я предлагаю подключать скрипт drupal.js, который лежит в ядре Drupal на верстке.
Таким образом мы сможем писать основные скрипты в пригодном для Drupal виде, и при применении верстки просто копировать их.
Все что нужно знать верстальщику — вместо обычного document ready писать бихевиоры. И внутри всегда использовать context при построении селекторов.

jQuery.once


Если бихевиоры нужны были для того, чтобы дополнительно добавлять обработчики к новому контенту, то теперь рассмотрим обратную проблему. Какие-то обработчики могут назначаться на одни и те же элементы дважды, а нужно это не всегда.
Самый простой способ при первой обработке добавлять класс, а в селектор включить условие отсутствия этого класса.
$('a.tooltip:not(.processed)', context).addClass('processed').someTooltipPlugin();

Такая конструкция внутри бихевиора гарантирует, что при повторном выполнении кода, обработчик добавился единожды.
Такой же механизм заложен в плагин jQuery.once, который включен в ядро D7 и по умолчанию доступен на любой странице.
С использованием плагина конструкцию выше можно заменить так:
$(context).once('myIdentifier', function () {
  // Your code here.
});

Где myIdentifier любое уникальное значение, которое будет использовано в качестве того самого processed класса.
Можно заменить более короткой конструкцией:
$(context).once(function () {
  // Your code here.
});

В данном случае плагин сам сформирует уникальный класс.

Base url


Довольно распространенная ошибка — забывают учесть base url. Не раз встречал ее в проверенных контриб модулях.
Распознать ее легко — слеш в начале пути:
var url = '/ajax/my-module/some-action';
Обычно разрабатывают на одном окружении, и просто хардкодят слеш, а когда бейс урл меняется — часть скриптов перестает работать… Чаще всего ошибка встречается при формировании пути для ajax запросов и при задании пути для атрибутов тегов a и img.
Правильным будет использовать специальную переменную, которая объявляется ядром:
var url = Drupal.settings.basePath + 'ajax/my-module/some-action';


Пути ajax запросов


Небольшая рекомендация (у нас в компании это просто стандарт) — при формировании пути ajax запросов, начинать их со слова ajax — это позволит при необходимости легко отделить все ajax запросы от обычных. Например исключить их разом из кеша, искать в логах и т.д. Затем имя модуля через дефис — это сразу скажет где искать обработчик этого запроса. Дефисы нужны для читаемости урла, даже если никто его не увидит, лучше придерживаться одного формата, ведь вы не станете строить алиасы нод с подчеркиванием?
Пример «правильного» пути по данной логике будет — ajax/my-module/some-action.

Вывод строк


Есть ряд функций, которые следует использовать при выводе текста на JS.
Я просто перечислю основные, без углубления — все они являются аналогами PHP функций ядра:
// Вывод переведенной строки.
Drupal.t('text');
// Вывод "безопасного" значения строки.
Drupal.checkPlain(name);
// Склонение окончаний фразы, в зависимости от множественного/единственного числа переменной.
Drupal.formatPlural(count, singular, plural, args, options);

Объявлены они все в drupal.js, соответственно работать без него не будут.
Поделиться публикацией
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 19
  • 0
    Все просто и понятно, но что будет если элементы а, с интересующим нас классом появляются на странице асинхронно? Выходит, что нужно руками вызвать функцию, которая заново навесит все обработчики, либо дублировать этот код в месте возврата асинхронного контента.

    Недавно столкнулся с подобной проблемой, но разве нельзя добавить данный код в callback функцию, после добавления элементов, или почему это не правильно делать?
    (function ($) {
    //
    }) (jQuery);
    

    Вроде везде такая обертка приветствуется, чтобы не конфликтовать с другими библиотеками
    • +2
      Вроде везде такая обертка приветствуется, чтобы не конфликтовать с другими библиотеками

      Да, это правильный вариант, я так и написал.
    • +1
      Теминг, бихевиор, бейс урл? Что? Странно звучит, как минимум. Теминг — темизация, бихевиор — ну, поведением хотя бы назвать, бейс урл — базовый URL.
      • +1
        > Существуют разные способы создания верстки под Drupal. Кто-то верстает уже затемленные страницы, кто-то пытается обойтись стандартными темами, но как правило, сначала верстальщик верстает страницы по дизайну, и на выходе получается набор html файлов — слайсов. Затем девелоперы интегрируют эти файлы по кусочкам при теминге.

        А потом проклятия сыпятся на тех, кто вывод перемешивает с оформлением, зашивает меню и информационные блоки в шаблоны, а файлы подключает не через drupal_add_.

        > Лучше сразу объяснить вашему верстальщику такой прием, чтобы в будущем избежать проблем и не лазить по его скриптам с исправлениями.

        Лучше его пристрелить и найти того, кто знает, что такое и зачем нужен jQuery.noConflict(). Drupal тут не при чём, это общая практика.

        Что касается basePath, то Drupal.settings.basePath + Drupal.settings.pathPrefix. Не стоит забывать про мультиязычные сайты.
        • –1
          А потом проклятия сыпятся на тех, кто вывод перемешивает с оформлением, зашивает меню и информационные блоки в шаблоны, а файлы подключает не через drupal_add_.

          Не совсем понял, это как-то связано со способом подготовки верстки?

          Лучше его пристрелить и найти того, кто знает, что такое и зачем нужен jQuery.noConflict(). Drupal тут не при чём, это общая практика.

          Конечно такой верстальщик лучше, очень хочу найти хотя бы такого, да еще чтоб и под друпал верстал хорошо. Есть кого посоветовать?
        • 0
          товарищ спрашивает: «Отличается ли верстка под друпал по цене, от обычной верстки такой же сложности?»
          • +1
            Отличается тем, что не стоит давать вёртску и программирование разным людям.
            Друпал — это в 80% случаев «натыкать в админке». Верстать нечего, только писать css. Можете представить сколько ненависти вызовет вёрстка, у которой не те классы и не та структура.
            Строя дом, не следует сначала вешать картины на обои, а потом возводить стены. Делая сайт на Друпале, следует подготовить структуру, установить модули, настроить выводы и только потом красить кнопки в оранжевый.
            • +1
              Когда вы делаете проект на 10-15 тысяч человеко-часов
              вы вынуждены распараллеливать работу
              и начинать верстку не дожидаясь того когда «весь» функционал будет сделан
              особенно если учесть что состояние «весь» не бывает вообще никогда достигнуто как минимум в половине проектов
              хотя бы потому что в процессе работы даже если был подписан договор с макетами
              все равно выясняется что что-то надо убрать, что-то переделать, а чегото срочно добавить причем раньше чем сдать основной функционал в договоре.

              ну и опять же не уверен что 100% объективно «писать css» отделять от «верстать»
              мы не имеем отдельной должность CSSщик
              для нас это верстальщик
              и это другой человек чем программер
              с другими навыками и умениями.
              • 0
                Нет ничего особого в том, что тот, кто пишет css, будет тыкать мышкой в админке. Или тот, кто тыкает мышкой в админке, будет писать css. А это и есть 80% сайтов.

                Что касается результата работы программиста, то render массивы — это совсем не html. Что остаётся для вёрстки в базовом её понимании — сложно сказать.

                Макеты — это прекрасно, но вот третьего дня я безумно веселился, когда часть работы у нас забрали, когда мы выставили «безумную» цену за переписывание страницы оформления заказа в уберкарте и передали её тем, кто довёл бы её до соответствия с макетом за еду. Сайт 3 раза упал, оформление заказа не работает.
                • +1
                  Нет ничего особого в том, что тот, кто пишет css, будет тыкать мышкой в админке. Или тот, кто тыкает мышкой в админке, будет писать css.

                  Особое тут тот факт, что найти хорошо квалифицированого человека сразу в обоих этих вещах сложнее, ну и в любом случае дороже, чем двух отдельных.
              • +1
                Далеко не всегда желания дизайнера можно реализовать только изменением CSS готовой структуры.
              • +1
                Да, может отличаться, но не сильно.
                Ведь технологии все те же, просто надо знать, что такой элемент нужно верстать такими-то блоками, с такими-то классами.
                • 0
                  за статью спасибо.
                  так же было бы интересно посмотреть на стандарты написания кода, о которых вы пишете выше… может быть они как-то оформлены и есть ссылка?
                  • +1
                    Все оформить пока руки не дошли, стандарты у нас есть на все, но пока они передаются в основном из уст в уста:)
                    Как будет пригодном виде, поделюсь.
              • 0
                Давненько я уже пришел к выводу, что при разработке на друпале эффективно работать с верстающими программистами (пусть и разной квалификации).

                Проходили вариант программист и верстальщик — это был ад. Слишком мого проблем и ругани.
                • –1
                  т.с.: забыли добавить, что стиль комментирования // Your code here — не очень хорош. После включения сжатия бывает, что скрипты из-за этого отваливаются.

                  2 Punk_UnDeaD > «А потом проклятия сыпятся на тех, кто вывод перемешивает с оформлением, зашивает меню и информационные блоки в шаблоны ...»
                  — нас ждет твиг! :)
                  2 Punk_UnDeaD > «Можете представить сколько ненависти вызовет вёрстка, у которой не те классы и не та структура.»
                  При грамотном подходе и нормальной первичной вёрстке — практически ничего не вызовет. 2 из последних последних проектов, которые уже были свёрстаны практически не потребовали доработки.

                  2 Abyasov «Давненько я уже пришел к выводу, что при разработке на друпале эффективно работать с верстающими программистами»
                  Ну, да что-то есть в этом :). Но imho полезнее с программирующими верстальщиками лучше. Обычно на уровне перекрытия стандартного вывода — вполне достаточно.
                  Опять же если кто-то из 2-х «генеальных» личностей глуп(да ещё с кучей понтов и амбиций) — то ни что уже не поможет.
                  • –1
                    Скрипты только склеиваются. Сжимается css.
                    • +1
                      т.с.: забыли добавить, что стиль комментирования // Your code here — не очень хорош. После включения сжатия бывает, что скрипты из-за этого отваливаются.

                      Да, когда-то на D6, я тоже встречал такую проблему. Но вживую я ее давно уже не видел, поэтому думаю что она неактуальна.
                      Особенно если учесть, что в скриптах ядра и контриб модулях такой стиль сплошь и рядом, поэтому отступать некуда.
                    • 0
                      Спасибо! Лаконично и по теме все.

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое