Еще раз о плавающей метке на CSS

Перевод статьи Revisiting the Float Label pattern with CSS с сайта thatemil.com, опубликовано на css-live.ru с разрешения автора — Эмиля Бьёрклунда (твиттер — @ThatEmil).

Паттерн с «плавающей» меткой — нехитрый паттерн, полюбившийся разработчиками. Не скажу, что сам влюблён в него на 100%, но не смог удержаться и не состряпать быструю демо-реализацию. В этой версии используются несколько приятных трюков для стилизации формы, повстречавшихся мне в последнее время, в частности селектор :placeholder-shown.

Первым делом: это ни в коем случае не готовое практическое решение. Она работает в последних версиях некоторых браузеров — прежде всего в Chrome/Opera и Safari/WebKit. А вот в Firefox с треском проваливается. И учтите, что я не тестировал его как следует.

Здесь я полагаюсь на несколько механизмов:

  1. Flexbox — паттерн Хьюго Жироделя, чтобы сначала разместить метку после поля в разметке, а затем поменять их порядок на обратный.
  2. transform для смещения label вниз поверх поля. Когда это состояние активно, текст плейсхолдера получает opacity: 0, так что два текста не перекрываются.
  3. Смещаем 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. Это тоже может быть интересно:

9 Комментарии

  1. Виталий

    Селектор input:placeholder-shown label { вроде не корректен.
    Корректно —
    input:placeholder-shown + label {

    1. SelenIT

      Справедливо и логично. Опечатка у автора оригинала, котору мы действительно было пропустили (хотя в рабочем примере верно). Исправлено.

  2. Максим

    Не работает в Firefox

    1. SelenIT

      К сожалению, пока да, но автор об этом честно предупреждает.

      1. Максим

        Понимаю, что не вы автор. Но мне интересно, в чем польза данного метода? Он не дает ничего нового.

        1. SelenIT

          Как минимум, он демонстрирует применение нового стандартного (ну, почти:) псевдокласса для достаточно частой практической задачи, к тому же вполне подходит для прогрессивного улучшения форм. Ну и, как отмечает сам автор, не привязан к валидации поля.

          Сейчас, да, это скорее занятный эксперимент. Но Firefox планирует «когда-нибудь» ввести поддержку этого псевдокласса, так что знать о такой возможности заранее не помешает.

          1. максим

            Я до сих пор не могу использовать flex-box из-за поддержки старых браузерах, что уж говорить про новые фичи, которые даже Мозилла не поддерживает.

  3. Тахир

    Я немного не понимаю, почему нельзя обойтись позиционированием и без всяких флексбоксов это сделать?

    1. SelenIT

      Можно, но придется подгонять размеры элементов и величину смещения с точностью до пикселя и следить, чтобы ничего не съехало при масштабировании и т.п. Во флексбоксах это делает сам алгоритм размещения, автоматически. Правда, в этом конкретном решении частично подгонять всё равно приходится (для анимации), так что тут, возможно, преимущество флексбоксов и впрямь не столь критично.

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.