Привет, представляю твоему вниманию перевод статьи Моники Динкулеску '<input> I ? you, but you're bringing me down'. Художественный перевод с английского не является моей основной специализацией, поэтому в тексте возможны неточности. Правки призываю отправлять личным сообщением, а если есть что сказать, велкам в комментарии. Отдельное спасибо @Kt за редакторские правки. Приятного чтения.
Некоторые люди делают мебель. Некоторые люди вяжут. Некоторые люди имеют хобби, которые никак не пересекаются с HTML спецификациям из 90-х. Но я не из таких. И, вот история о том, как <input> стал той хренью, которой он является, и почему его надо сжечь
Ранние годы
1995 был клевым годом. Друзья, Скорая Помощь, Зена по телеку. TLC занимали верхушки чартов с хитом "Waterfalls". С браузерами было нормуль, потому что HTML было все очень нормуль. У нас были Mosaic, Netscape и IE1, а при утверждении спеков HTML 2, наконец, выкроили время для стандартизации форм. Девяносто пятый был годом рождения <input>, и теперь, когда он достаточно взрослый, чтобы покупать в магазине алкоголь, нам нужно поговорить.
Изначально <input> пришел к нам в восьми видах: text, password, checkbox, radio, image, hidden, submit и reset, а так же с отдельном последующим RFC, file.
Подожди, ты сказал image? Да, давай я расскажу о нем.
<input type='image' src='cat.png'> выглядит как обычное изображение, но на самом деле это изображение-кнопка, которая при нажатии также передает x и y координаты того места, в котором на нее нажали. При этом, если не указано изображение для атрибута src, «изображение-кнопка» станет кнопкой с надписью «Submit». А если дело будет происходить Firefox, то текст кнопки изменится на «Submit Query» и кнопка будет выглядеть как обычный текст. А если открыть пример в IE, в этом случае ты не увидишь ничего.
А вот вопрос, достойный вашего местного клуба «Что? Где? Когда?», если тип элемента = 'file', то текст который будет отображаться на месте этого элемента, когда не выбран ни один из файлов: «No file chosen», «no file selected», «No file selected», и просто пустой текстовый блок в Chrome, Safari, Firefox и IE соответственно.
Ладно, хорошо.
А вот, тирада в честь <textarea>
Я всегда думала, что input и textarea были придуманы в разное время, и это объясняет, почему они так безумно отличаются. Это в целом так, input был частью браузера Mosaic по крайней мере с 1993, и он был, по сути, исправленной имплементацией ISINDEX. Тем не менее, официально, они оба были детьми HTML2, который решил, что <input> это самозакрывающийся тег в котором значение тега помещаяется в атрибут value, в то время как для тега <textarea> нужен закрывающий тег, а его значение хранится в его содержимом, даже если они оба тега используются только для хранения простого текста, который кто-то ввел:
<input value="batman">
<textarea rows="1">batman</textarea>
Небольшой апдейт: некто подсказывает, что <textarea> должен поддерживать ввод многострочного текста, а символы переноса строки не допускаются внутри значений атрибутов, вот почему мы должны использовать содержимое тега. А это имеет смысл!
1995-2011, средние года
В 1999 году, в HTML4 добавили только один новый тип тега = 'button'. Что мне в этом особенно нравится, что без каких-либо дополнительных пользовательских стилей, <input type='button'> и <input type='button' value='Submit'> помещенные в одну и ту же строку, не совпадают по вертикали в Chrome/Safari/Edge.
Все становится еще хуже
Позже, в 2011 году, спецификация HTML5 добавила тысячи новых типов для <input>. И на сегодняшний день, большинство из них не реализовано. Вот краткий перечень неработающих фич: type=color работает только в Firefox/Chrome, date/time работает только в Chrome/EDGE/iOS, и все, что работает в Chrome работает в Opera. Вот сравнение всех доступных типов input'ов на сегодня, так что ты можешь сравнить и поплакать наедине.
Давай поговорим о некоторых интересных фактах.
<input type='search'> имеет странные случайно выбранные отступы между текстом и границей, а так же популярные в середине 2000-х закругленные углы, внешний вид которых отличается от браузера к браузеру и от которых почти невозможно избавиться.
А если твоему браузеру повезло поддерживать тип = 'date', не волнуйся о стилизации элемента выбора даты – у нас есть 8 чудесных ::webkit псевдо-селекторов, которые помогут настроить стиль текстового блока, но ни одного для стилизации выпадающего блока выбора даты! В любом случае, знай, CSS вреден для твоего здоровья.
И в тот момент, когда ты подумал, что хуже уже не будет: JavaScript
Видите ли, я могу оправдать причуды CSS. Я работала над браузером Chrome в течение 2 лет, сейчас я работаю с командой Blink, я понимаю, что мы все пишем разные рендеры и что для них свойственны свои собственные баги CSS. Тем не менее, <input> API даже нельзя назвать странным – он буквально как банка пауков, и в тот момент, когда ты открываешь банку, уже слишком поздно. Ты весь в пауках. Даже твоя кошка стала пауком. Лучше сжечь тут всё.
Начиная с 1995 года, элементы формы с типами radio и checkbox имеет дополнительный атрибут 'checked' который позволяет определить состояние элемента. Поскольку HTMLInputElement это HTMLInputElement и это HTMLInputElement, выходит, что во всех остальных типах элемента <input> этот атрибут также присутствует, но его просто игнорируют. Да, даже если это и не имеет смысла, это восхитительно:
var textInput = document.querySelector('input[type="text"]');
console.log(textInput.checked); // prints false.
textInput.checked = true;
console.log(textInput.checked); // prints true.
// did not open the hellmouth.
Круто. Круто-круто-круто!
<input> содержит текст, текст может быть выделен, а в прототипе HTMLInputElement определенны два свойства: selectionStart и selectionEnd – два числа которые определяют диапазон выбора. Таким образом, ты можешь сделать:
document.querySelector('input').selectionStart += 2;
И продвинуться на 2 символа вперед от начала выделения текста. Мега-заурядное исключение из этого факта то, что selectionStart – атрибут который доступен только для input'ов c типами text, url и password. А еще он доступен для вызова (это даже не настраивается) бросая исключение так же для всех остальных типов элемента :
Uncaught DOMException: Failed to read the 'selectionStart'
property from 'HTMLInputElement': The input elements type ('number')
does not support selection.
Даже если я могу вручную выделить этот текст:
Вот и получается, что в некоторых случаях свойства могут реально использоваться, а в других случаях они покажут свой «волчий оскал». Ве-ли-ко-леп-но. Именно такого постоянства я ищу в API.
К сожалению, это еще не все. Я уверена, что это еще не все. Дело в том, у разработчиков браузеров был 21 год, чтобы починить отображение input'ов, но им даже не удалось договориться о том, как писать «you haven't picked a file».
Теперь представь будущее, в котором веб-компоненты поддерживаются нативно, кто угодно может написать свой <better-input> – реальный инкапсулированный DOM элемент, а не просто лапшу из DIV'ов. Представь, как ты используешь этот <better-input>, что его реализация не отличается от браузера к браузеру, он выглядит одинаково везде, и что, вероятно, он знает, как испечь тебе вишневый торт. Только представь.
Комментарии (51)
Hint
27.10.2015 12:36+2Как-то установил на проекте тип email для всех полей, предназначенных для ввода почты. Сначала радовался (автоматическое добавление нужных символов на клавиатурах мобильных устройств), а потом вернул тип text. Почему? Потому что если случайно добавить лишний пробел (перед адресом или после), то браузеры считают значение неверным и не дают отправить форму. А ведь это происходит очень часто и вводит пользователей в заблуждение (особенно при copy-paste). В итоге, бесполезный тип без костылей на JavaScript, хотя при отправке достаточно было бы сделать trim. Возникает еще проблема с доступом к value из скрипта, но могли бы что-нибудь придумать.
AreD
27.10.2015 14:18+1Возникает еще проблема с доступом к value из скрипта, но могли бы что-нибудь придумать.
А о какой проблеме идет речь?Hint
27.10.2015 14:32Я имею в виду, что если делать trim при отправке формы, то как быть, например, в случае ajax (когда скрипт сам берет значение из поля)? Как быть с псевдоклассами валидации? Т. е. визуально значение одно (с пробелом), а фактически для скриптов другое?
jrip
27.10.2015 15:32+1Ну в идеале в такое поле не должен просто никак попасть пробел и не надо тогда никаких неожиданных trim`ов.
kykint
27.10.2015 14:37+1Кстати с value действительно был косяк, однажды столкнулся. Если в number поле было вбить невалидное значение, то value говорит, что там пусто
Hint
27.10.2015 14:44На самом деле проблема не только с email. Например, тип url. Значение www.ya.ru по сути неверное, но браузер может преобразовать в www.ya.ru и отправлять именно это значение (и предоставлять его скриптам).
WaveCut
27.10.2015 16:16В помощь призывается pattern!
Hint
27.10.2015 17:00И он воспринимается мобильными устройствами? Они добавляют на клавиатуру все нужные спец. символы?
WaveCut
27.10.2015 17:04Когда я последний раз тестировал — судя по поведению типизированные поля вроде url, email, tel просто используют некий pattern «по-умолчанию». Если его переписать — валидация будет плностью своя. При этом поле не теряло своих новых качеств на клавиатуре.
Но конечно, все может меняться от платформы к платформе. У меня яблоко.SelenIT2
28.10.2015 12:51Для url и email паттерны описаны в самом стандарте (для второго — явно в виде регулярки, для первого — в виде ссылки на отдельный стандарт WHATWG).
volk0ff
27.10.2015 14:44+1Интересно мнение экспертов: если по аналогии с ВК, вместо инпутов использовать повсеместно дивы с атрибутом
contenteditable="true"
, ничего страшного не произойдет? (там то со стилями все менее плачевно)trikadin
27.10.2015 15:23+1Придётся писать апи самому, хех. И насколько я помню (давно таким не занимался) — там далеко не нулевое количество кроссбраузерных несовместимостей.
volk0ff
27.10.2015 15:29Стоит ли так все усложнять? (Я, например, просто брал яваскриптом содержимое этих div-ов и отправлял на сервер аяксом)
trikadin
27.10.2015 15:32Ну это же в первую очередь от задачи зависит О_о
Если вам понадобиться делать произвольные формы с валидацией, разными вариантами отображения, подгрузкой подсказок аяксом и т. д. — всё будет сложно, и гарантированно сложнее, чем на инпутах, к сожалению.
Нет, конечно, наверняка есть фреймворки, которые такое делают… Но в таком случае ты просто променяешь одно АПИ на другое.volk0ff
27.10.2015 15:45Не исключаю, что у меня имеются пробелы в знаниях JS, но какая разница, куда, например, выводить сообщение об ошибке, которое пришло от сервака — под соответствующий инпут, или под такой же див? :)
jrip
27.10.2015 15:55+1Со стандартными формами есть, например, специальные JS библиотеки для валидации, отображением ошибок и тд, которые можно просто настроить.
Вообще в целом, кажется что разницы не будет, если элементы формы сделать свои, но на практике оказывается, что не хватает многого из стандартного поведения, например переходы по табу, клик по лейблу — клик по элементу и тд. В целом приходится нифигово всякого дописывать самому.
trikadin
27.10.2015 16:07+5Я вам не про то. Давайте я вам набросаю краткий список того, что нужно дописать для редактируемого дива, чтобы они сравнился по функционалу с современным инпутом.
- Типы форм. Придётся делать проверку на ввод только чисел для чисельного ввода, отображение календарика для ввода даты, проверку ввода корректной даты и времени для соотв. типов, цветовой ввод для color, ползунок для rangе… Да и что я вам перечисляю, вот тут всё есть. Это уж не говоря про всякие радиобаттоны и флажки.
- Перехватывать нажатия таба для нормального перехода по элементам формы, и вообще разные сочетания клавиш для accesskey.
- Модификаторы поведения — disabled, readonly, checked, autofocus...
- Валидация. Это поля pattern, min, max, minlength, maxlength, step, встроенная регулярка для урлов, емейлов, телефонов...
- Разные другие плюшки — placeholder, list.
- Связь с формой отправки на сервер.
- Событийная система.
Наконец, есть вещи, которые вы никак не сможете эмулировать — автозаполнение, переключение видов клавиатуры на телефонах в зависимости от типа инпута, выбор файла (это можно кое-где, но тоже очень геморройно). Смекаете? Чтобы реально замениить инпуты чем-то другим, надо проделать огромную работу.
jrip
27.10.2015 15:35+4Можно и на канвасе полностью свой интефрейс намутить, вопрос в поддержке всеми браузерами, сложности разработки и с поддержкой нестандартных решений следующим разработчиком. Как по мне, так лучше использовать стандартные элементы и запариваться со стилями.
dshster
27.10.2015 16:40+3Придётся очень дико обрабатывать событие copy/paste потому что в contenteditable вставляется текст в формате text/html со всеми вытекающими. Но это еще не всё — так же придётся обрабатывать события перетаскивания в это поле и (внимание!) событие document.execCommand из консоли, через которое в contenteditable можно вставить всё что угодно. А это уже довольно объёмная портянка кода.
Ohar
27.10.2015 18:27Госпожа Динкулеску написала поток нытья вместо статьи.
Из перечисленных проблем реальная только одна — ограничение дляselectionStart
.
если тип элемента = 'file', то текст который будет отображаться на месте этого элемента, когда не выбран ни один из файлов: «No file chosen», «no file selected», «No file selected», и просто пустой текстовый блок в Chrome, Safari, Firefox и IE соответственно.
он выглядит одинаково везде
Почему это вообще кого-то беспокоит? Это нативные элементы браузера и пользователь этого конкретного браузера узнаёт их и понимает что от него в этом месте хотят.AreD
27.10.2015 21:14+3да, статья написана в достаточно ироничном ключе, но скажите, вас устраивает, например, отсутсвие возможности стилизировать выпадающий список тега select с помощью css?
Ohar
27.10.2015 21:25+2И для каких именно браузеров вы собрались его стилизовать?
Вы, например, видели, как он отображается в мобильных браузерах? Скорее всего, видели.
Вы уверены что сможете полностью корректно стилизовать его для этого случая? Для других случаев?
Вы уверены что он будет выглядеть как минимум не хуже нативного, работать не хуже нативного и что после вашей кастомизации пользователь поймёт что это именно селект, а не что-либо ещё?Drag13
27.10.2015 21:55Это вы шутите так?
Когда код уходит на продакшн он тоже теоретически может не работать. Но никого это не останавливает почему-то…
AreD
28.10.2015 00:52+7На современном мобильном селект выглядит как часть интерфеса операционной системы, все ок, я не вижу проблемы.
Проблема с моей стороны с десктопными браузерами. Да, я знаю доводы за нативное отображение системных элементов и отчасти поддерживаю их, но вот нифигаж оно не работает, посмотрите на «нативный» селект фаерфокса под OS X. И как написал Drag13 ниже, мы за то, что бы мы могли выбирать, сделать веб приложение как нативное под ОС, или сделать дизайнерский сайт с теми формами которые мне надо. Да посмотрите же, никого это ограничение не останавливает и появляются монстры написанные на jQuery которые эмулируют селект, но с настраиваемым внешним видом, но вот не задача, то он криво под старыми бразуерами отобразиться, то он с помощью клавиатуры не выделяется, и прочие баги. Печальное это, 21 год уже как печально. Что касаемо вашего мнения, я его понимаю и принимаю, но спорить с вами не буду.
SelenIT2
28.10.2015 12:59+2Загадочность и нелогичность дефолтных стилей контролов форм — тоже проблема. Другой вопрос, стандарта или браузеров (в первую очередь всякие
::-moz-focus-inner
ы и их друзья, без которых нормально не сбросить отступов кнопке). Скорее всего, и того и другого).
jorgen
27.10.2015 21:29+1Проблемы роста, проблемы архитектуры, неудачных решений, непредвиденных технологий, обратной совместимости, и много чего ещё. В программировании эти все грабли известны, изучены и классифицированы.
Но главное — если с такими проблемами не может справиться, например, десктопное приложение — его обгоняет конкурент, который молод, который другой, которому удаётся принять и похендлить новые вызовы. А в интернете, к сожалению, кто-то решил что конкуренция платформ не нужна, а всем хватит одного стандарта.
bolk
28.10.2015 07:43+1У четвёртого «Нетскейка» была тогда масса странных типов тега INPUT. Например, TYPE=OBJECT, TYPE=READONLY или TYPE=JOT.
fetis26
28.10.2015 09:59+3Хаха, детка. Это все ерунда. Вот попробуй застайлить дропдаун, вот где БОЛЬ. Причем дизайнеры этой проблемы не замечают и год из года рисуют всякое безумие в виде дропдаунов
Eefrit
28.10.2015 16:58-3Что касается <better-input>… что тут скажешь.
Старо как мирСобрались как-то инженеры и говорят — что-то у нас слишком много стандартов для проводов, передающих данные от устройства к устройству. Тут тебе и USB, и COM, и LPT, и FireWire, и ThunderBolt, и так далее. Аж 14 штук насчитали. А давайте, говорят, придумаем единый стандарт, который будет удовлетворять всем условиям, будет максимально универсальным, самым гибким и широкораспространённым? «Хорошая идея,» — поддержали все. И теперь у нас 15 стандартов.
aml
Когда не знаешь ещё, в какую сторону будет развиваться технология, придумать правильный стандарт, правильный API очень сложно. Удачная абстракция быстро стала популярной, стала обрастать фичами, не вписывающимися в первоначальный дизайн, и вот результат. Своего рода проклятие первопроходцев.
AreD
Да, но какие то легаси проблемы, например, решаются. Те же флекс блоки позволяют вертикальное позиционирование делать. А тут казалось бы простая вещь – стандартизация и возможность широкой настройки стилей элементов форм. Веб состоит из дизайна/верстки и пользовательского взаимодействия с сайтом. Вот когда верстаешь, делаешь красоту и душа прям радуется, вот тебе веб инспектор, вот тебе и инструменты для оптимизации CSS, вот тебе удобные медиа-запросы. Но как только ты доходишь до пользовательского взаимодействия – боль.