Хак с Form API в Drupal 7: создаём HTML5‑поля (number, email, tel) без лишних модулей

Drupal 7 предлагает мощный Form API для построения форм. Однако при работе с HTML5‑атрибутами можно столкнуться с неожиданным ограничением: если вы попытаетесь задать тип поля numberemail или tel стандартным способом, Drupal проигнорирует ваше значение и оставит тип text (или тот, который определён через #type).

Рассмотрим ситуацию: нам нужно поле для ввода числа с шагом 10 и ограничениями от 0 до 100. Логично использовать HTML5‑тип number:

Но на выходе мы получим <input type="text" ...>. Почему? Потому что Drupal при рендеринге элемента проставляет тип, основываясь на #type (в данном случае textfield), и атрибут type из #attributes перезаписывается внутренним значением.

Опытные разработчики Drupal 7 знают небольшой хак: если добавить пробел в начало ключа атрибута, Drupal не сможет его сопоставить со своим внутренним списком и просто склеит все атрибуты в итоговом теге. В результате в HTML появятся два атрибута type.

После рендеринга получится примерно такой HTML:

Когда мы добавляем в #attributes элемент с ключом ' type' (с пробелом), Drupal воспринимает его как обычный атрибут, но из-за пробела в имени ключа внутренний механизм Form API не распознаёт его как атрибут type, подлежащий перезаписи. Однако при финальном рендеринге функция drupal_attributes() обрабатывает массив атрибутов и обрезает пробелы в именах ключей. В результате в итоговой разметке атрибут появляется уже без пробела, как обычный type="number".

Как это работает?

Можно заметить интересную деталь: в реальном HTML-коде, который генерирует Drupal, наш атрибут type="number" появляется до стандартного type="text", а не после, как можно было бы ожидать. При этом поле всё равно работает как числовое. Давайте разберёмся почему так происходит, и почему хак не перестаёт работать из-за повторяющихся атрибутов.

Формально это невалидный HTML — атрибут не должен повторяться. Однако браузеры при разборе HTML достаточно либеральны и, как правило, применяют первое встретившееся значение атрибута, игнорируя последующие. В нашем случае браузер видит сначала type="number" и создаёт числовое поле, а второй type="text" просто не оказывает влияния. Именно поэтому хак работает, даже если стандартный type идёт после.

Конечно, поведение браузеров в такой ситуации чётко не специфицировано, и разные версии или браузеры могут теоретически обрабатывать это иначе. Но многолетняя практика использования этого приёма в Drupal 7 показывает, что во всех современных браузерах (Chrome, Firefox, Safari, Edge) поле становится именно того типа, который указан первым.

Мало букафф? Читайте есчо !

Программно устанавливаем значения настроек на страницах, созданных Config Pages

Ноябрь 22, 2018 г.

Модуль Config Pages позволяет создавать страницы конфигурации для вашего модуля, всего сайта, или может быть просто одного из разделов сайта. Потом эти значения можно читать из программы и использовать так, как вам требуется. Но API Config Pages не содержит ...

Читать

Подключаем suggestions list в диалог CKeditor - Link

Август 10, 2021 г.

Link - это штатный диалог, который используется в CKeditor для редактирования ссылок. И выглядит он как показано на картинке сверху. Он хорошо выполняет ...

Читать

Добавление пунктов меню в Administration menu (admin_menu)

Апрель 8, 2020 г.

Популярный административный модуль admin_menu, собирает всё самое нужное и важное для управления сайтом. И хотя в 7 версии в ядре вы найдете модуль tool, обладающий похожим функционалом, ему не удалось заменить admin_menu. Далее, разберем код, добавляющий ...

Читать

Получить имя текущей темы по дефолту в Drupal

Январь 21, 2019 г.

Theme API к сожалению не даёт прямого метода, чтобы прочитать это значение. Как его получить? Иногда программно нужно установить, что именно видит пользователь на фронт-енд (какую тему использует движок). Необходимость возникает из-за того, ...

Читать
 

Комментарии к «Хак с Form API в Drupal 7: создаём HTML5‑поля (number, email, tel) без лишних модулей»

Понравилась статья? Есть вопросы? - пишите в комментариях.



Комментарий: