Еще раз о плавающей метке на CSS
Перевод статьи Revisiting the Float Label pattern with CSS с сайта thatemil.com, опубликовано на css-live.ru с разрешения автора — Эмиля Бьёрклунда (твиттер — @ThatEmil).
Паттерн с «плавающей» меткой — нехитрый паттерн, полюбившийся разработчиками. Не скажу, что сам влюблён в него на 100%, но не смог удержаться и не состряпать быструю демо-реализацию. В этой версии используются несколько приятных трюков для стилизации формы, повстречавшихся мне в последнее время, в частности селектор :placeholder-shown
.
Первым делом: это ни в коем случае не готовое практическое решение. Она работает в последних версиях некоторых браузеров — прежде всего в Chrome/Opera и Safari/WebKit. А вот в Firefox с треском проваливается. И учтите, что я не тестировал его как следует.
Здесь я полагаюсь на несколько механизмов:
- Flexbox — паттерн Хьюго Жироделя, чтобы сначала разместить метку после поля в разметке, а затем поменять их порядок на обратный.
transform
для смещенияlabel
вниз поверх поля. Когда это состояние активно, текст плейсхолдера получаетopacity: 0
, так что два текста не перекрываются.- Смещаем
label
только если плейсхолдер не отображается, т.е. когда поле заполнено или находится в фокусе, по мотивам статьи Джереми «Псевдостоинства» (кстати, перевод есть у нас на сайте — прим. перев.)
Вот чем эта реализация отличается от решений Криса Койера и Джонатана Снука: они используют псевдокласс :valid
. Думаю, мой пример избавлен от этого ограничения, но у него есть проблемы с браузерной поддержкой, как я сказал вначале.
Вместо этого я использую псевдокласс :placeholder-shown
, но только внутри псевдокласса :not()
, чтобы выдвигать метку наружу, лишь когда текст плейсхолдера скрыт — что хорошо стыкуется с тем, как этот паттерн должен работать.
Вот соответствующий HTML:
<div class="field"> <input type="text" name="fullname" id="fullname" placeholder="Jane Appleseed"> <label for="fullname">Name</label> </div>
… и CSS:
/* Делаем поле flex-контейнером и меняем порядок на обратный, чтобы метка была вверху. */ .field { display: flex; flex-flow: column-reverse; } /* Добавляем переход для метки и поля. */ label, input { transition: all 0.2s; } input { font-size: 1.5em; border: 0; border-bottom: 1px solid #ccc; } /* Изменяем границу поля при фокусе. */ input:focus { outline: 0; border-bottom: 1px solid #666; } /* Когда метка следует за полем, соответствующим псевдоклассу :placeholder-shown: * 1. Убеждаемся, что метка помещается в одну строку, занимая не больше 2/3 поля — чтобы гарантировать, что метка правильно масштабируется и не переносится на другую строку. * 2. Меняем курсор. * 3. Перемещаем вниз и масштабируем метку, чтобы она покрывала плейсхолдер. */ input:placeholder-shown + label { /* [1] */ max-width: 66.66%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; /* |2] */ cursor: text; /* [3 */ transform-origin: left bottom; transform: translate(0, 2.125rem) scale(1.5); } /** По умолчанию плейсхолдер должен быть прозрачным. Кроме того, он должен наследовать переход. */ ::-webkit-input-placeholder { transition: inherit; opacity: 0; } /* По фокусу на поле показываем плейсхолдер. */ input:focus::-webkit-input-placeholder { opacity: 1; } /* * 1. Когда элемент находится в фокусе, удаляем переход у метки. * Проделываем тоже самое, когда плейсхолдер _не_ отображается, т.е. когда поле чем-то заполнено. * 2. ... и устанавливаем курсор в виде указателя */ input:not(:placeholder-shown) + label, input:focus + label { transform: translate(0, 0) scale(1); /* [1] */ cursor: pointer; /* [2] */ }
Обновление 01.26.2016: я обновил селектор для метки, чтобы она трансформировалась только когда она следует за полем, соответствующим псевдоклассу :placeholder-shown
. Таким образом, не поддерживающие его браузеры «откатываются» до «обычного» паттерна «метка над полем». Большое спасибо Маттео Капуччи за то, что указал мне на такое простое улучшение — делая пример в спешке, я полностью упустил это из виду. Также, спасибо Патчу за идею улучшения с cursor:pointer
.
Вот итоговый пример на JSBin.
P.S. Это тоже может быть интересно:
Селектор input:placeholder-shown label { вроде не корректен.
Корректно —
input:placeholder-shown + label {
Справедливо и логично. Опечатка у автора оригинала, котору мы действительно было пропустили (хотя в рабочем примере верно). Исправлено.
Не работает в Firefox
К сожалению, пока да, но автор об этом честно предупреждает.
Понимаю, что не вы автор. Но мне интересно, в чем польза данного метода? Он не дает ничего нового.
Как минимум, он демонстрирует применение нового стандартного (ну, почти:) псевдокласса для достаточно частой практической задачи, к тому же вполне подходит для прогрессивного улучшения форм. Ну и, как отмечает сам автор, не привязан к валидации поля.
Сейчас, да, это скорее занятный эксперимент. Но Firefox планирует «когда-нибудь» ввести поддержку этого псевдокласса, так что знать о такой возможности заранее не помешает.
Я до сих пор не могу использовать flex-box из-за поддержки старых браузерах, что уж говорить про новые фичи, которые даже Мозилла не поддерживает.
Я немного не понимаю, почему нельзя обойтись позиционированием и без всяких флексбоксов это сделать?
Можно, но придется подгонять размеры элементов и величину смещения с точностью до пикселя и следить, чтобы ничего не съехало при масштабировании и т.п. Во флексбоксах это делает сам алгоритм размещения, автоматически. Правда, в этом конкретном решении частично подгонять всё равно приходится (для анимации), так что тут, возможно, преимущество флексбоксов и впрямь не столь критично.