Drupal 7 предлагает мощный Form API для построения форм. Однако при работе с HTML5‑атрибутами можно столкнуться с неожиданным ограничением: если вы попытаетесь задать тип поля number, email или tel стандартным способом, Drupal проигнорирует ваше значение и оставит тип text (или тот, который определён через #type).
Рассмотрим ситуацию: нам нужно поле для ввода числа с шагом 10 и ограничениями от 0 до 100. Логично использовать HTML5‑тип number:
|
1 2 3 4 5 6 7 8 9 10 |
$form['y'] = [ '#type' => 'textfield', '#title' => 'Y', '#attributes' => [ 'type' => 'number', 'min' => 0, 'max' => 100, 'step' => 10, ], ]; |
Но на выходе мы получим <input type="text" ...>. Почему? Потому что Drupal при рендеринге элемента проставляет тип, основываясь на #type (в данном случае textfield), и атрибут type из #attributes перезаписывается внутренним значением.
Опытные разработчики Drupal 7 знают небольшой хак: если добавить пробел в начало ключа атрибута, Drupal не сможет его сопоставить со своим внутренним списком и просто склеит все атрибуты в итоговом теге. В результате в HTML появятся два атрибута type.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
$form['coord_fields']['y'] = [ '#type' => 'textfield', '#title' => 'Y', '#size' => 12, '#default_value' => 0, '#attributes' => [ ' type' => 'number', // обратите внимание на пробел перед type 'min' => 0, 'max' => 100, 'step' => 10, ], ]; |
После рендеринга получится примерно такой HTML:
|
1 |
<input type="number" min="0" max="100" step="10" type="text" name="y" value="0" size="12" maxlength="128" /> |
Когда мы добавляем в #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) поле становится именно того типа, который указан первым.
