Как сделать чудесные, анимированные всплывающие подсказки с помощью CSS.
Russian (Pусский) translation by Ellen Nelson (you can also view the original English article)
Всплывающие подсказки — это отличный способ улучшить пользовательский интерфейс — UI, когда вашим пользователям нужно дополнительное описание для той причудливой иконки, или когда они хотят перестраховаться перед тем, как нажать на кнопку, ну или может быть заголовок для "пасхального яйца" который идёт вместе с изображением. Так давайте сделаем анимированные всплывающие подсказки прямо сейчас, используя не что иное как HTML и CSS.
Образец
Вот над чем мы собираемся работать:
Прежде чем прыгать в котёл, давайте взглянем на то, что мы собираемся сварганить. Наша цель — добавить всплывающую подсказку простым способом, поэтому мы сделаем это добавив атрибут tooltip
:
1 |
<span tooltip="message">visible text or icon, etc.</span> |
Замечание о доступности и возможностях
Если вы ищете всплывающие подсказки совместимые с 508-м разделом (стандарт, принятый в США, для создания веб-ресурсов для людей с ограниченными возможностями) или вам нужны более умные подсказки с обнаружением столкновения контейнеров и/или поддержкой HTML содержимого или обычного текста, существует множество решений, в которых для этих задач используются сторонние скрипты.
«JavaScript необходим для создания полностью доступных интерактивных компонентов» — Сара Суэйдан, Построение полнодоступной справочной подсказки... сложнее, чем я думала
В этом руководстве потребности в доступности особо не рассматриваются. Вы знаете своих пользователей и то, что им нужно, поэтому убедитесь что вы учили и уважаете их нужды.
Давайте определим чего следует ожидать
- JavaScript не требуется
- Мы будем использовать селектор атрибута (не название класса), используя встроенные возможности CSS.
- Будем использовать существующие DOM элементы (никакие новые элементы не потребуются в вашей разметке*)
- В примерах кодов не используются префиксы (поэтому если вам нужно, сами добавьте префикс для вашего браузера)
- Для переключения всплывающих подсказок будем использовать событие в наведении мыши — mouseover/hover
- Всплывающие подсказки поддерживают только простой текст (HTML, изображения и прочее не поддерживается)
- Лёгкая анимация при вызове всплывающих подсказок
Отлично! Давайте раскачаем эту лодку!
Ой, подождите! Есть сноска (*), давайте сначала договоримся об “дополнительная разметка не нужна”. Всё-таки это магия! Нашим всплывающим подсказкам действительно не нужны дополнительные DOM элементы, так как они сделаны полностью на псевдо-элементах (::before
и ::after
), которыми мы можем управлять с помощью CSS.
Если вы уже используете псевдоэлементы из другого набора стилей и хотите создать всплывающую подсказку для этого элемента, вам может потребоваться немного изменить структуру.
Никакой вечеринки без всплывающих подсказок!
Подождите. Господа! Ещё одна оговорка: CSS-позиционирование. Чтобы всплывающие подсказки функционировали должным образом, их родительский элемент (то, к чему мы прикрепляем подсказку) должен быть
-
position: relative
, или -
position: absolute
, или position: fixed
По существу, что-либо кроме position: static
— это режим позиционирования по умолчанию, назначенный практически на все элементы вашим браузером. Всплывающие подсказки имеют абсолютное позиционирование и таким образом им нужно знать границы, в которых их абсолютность имеет место быть. Указанное по умолчанию статическое позиционирование не имеет свои собственные границы и не даст вашим всплывающим подсказкам что-то, от чего можно оттолкнуться, поэтому подсказки будут использовать ближайший родительский элемент, у которого четко объявлена граница.
Вам нужно определить, какое позиционирование работает лучше, когда вы используйте подсказки. По этому руководству подразумевается, что родительский элемент настроен на position: relative
. Если ваш интерфейс основывается на абсолютном позиционировании элементов, тогда могут потребоваться некоторые изменения (дополнительная разметка), чтобы навесить подсказку на этот элемент.
Ну что ж, запрыгиваем и понеслась!
Селектор атрибута: Быстрое напоминание
Большинство CSS правил написаны для использования по названию класса, например .this-thing
, но в CSS есть несколько типов селекторов. Для наших чудесных подсказок мы будем использовать селектор атрибута/свойства — объявляется квадратными скобками.
1 |
[foo] { |
2 |
background: rgba(0, 0, 0, 0.8); |
3 |
color: #fff; |
4 |
}
|
Когда браузер встречает что-то вроде этого:
1 |
<span foo>Check it out!</span> |
он поймет, что нужно применить правила [foo]
, потому что в теге <span>
имеется атрибут названный foo. В этом случае, сам span будет полупрозрачного черного цвета с белым текстом.
HTML элементы имеют различные встроенные атрибуты, а также мы можем придумать свои собственные. Наподобие foo
, или tooltip
. По умолчанию, HTML не знает что он означает, но с помощью CSS мы можем подсказать HTML, что с этим делать.
Почему селекторы атрибутов?
Мы будем использовать селекторы атрибутов в первую очередь для разделения задач. Использование атрибутов вместо имён классов не дает нам никаких бонусных очков в этой битве; классы и атрибуты имеют одинаковое предназначение. Однако, используя атрибуты, мы можем держать наш контент в содержимом, поскольку атрибуты HTML могут иметь значения, в то время как имена классов не могут.
Рассмотрим в этом примере кода: имя класса .tooltip
и атрибут [tooltip]
. Имя класса является одним из значений атрибута [class]
, в то время, как атрибут tooltip имеет значение, а содержащее текст, который мы хотим отобразить.
1 |
<span class="tooltip another-classname">lorem ipsum</span> |
2 |
|
3 |
<span tooltip="sit dolar amet">lorem ipsum</span> |
Теперь добавляем Алхимию в подсказку
Наши подсказки будут использовать два атрибута:
-
tooltip
: содержит содержимое подсказки (строка простого текста) -
flow
: не обязательно; позволяет нам управлять с тем, как раскрыть подсказку. Мы можем использовать много способов размещения, но мы рассмотрим 4 обычных направления:
сверху, слева, справа, снизу.
Теперь давайте настроим основу для всех подсказок. Правила 1-5 пунктов применяются для всех подсказок, независимо от того какое направление flow мы им зададим. Пункты 6–7 имеют различия для значений flow
.
1. Относительность
Это для родительского элемента подсказки. Давайте назначим позицию так, чтобы абсолютное расположение частей подсказки (псевдоэлементы ::before
и ::after
) находилось в контексте родительского элемента, а не в контексте страницы в целом или прародительского элемента, или какого-то другого внешнего элемента в дереве DOM.
1 |
[tooltip] { |
2 |
position: relative; |
3 |
}
|
2. Настало время для псевдо-элементов
Пришло время для выхода псевдоэлементов на сцену. Пока что, мы зададим общее свойство для обоих частей ::before
и ::after
. Свойство content
— выполняет основную работу псевдо-элемента, и скоро мы до него доберемся.
1 |
[tooltip]::before, |
2 |
[tooltip]::after { |
3 |
line-height: 1; |
4 |
user-select: none; |
5 |
pointer-events: none; |
6 |
position: absolute; |
7 |
display: none; |
8 |
opacity: 0; |
9 |
|
10 |
/* opinions */
|
11 |
text-transform: none; |
12 |
font-size: .9em; |
13 |
}
|
3. Dink
Я не знаю, какое отношение имеет к этому "dink, но я всегда называл это так. Эта маленькая, треугольная, заострённая часть, придающая подсказкам вид, как у пузыря с диалогом, указывающая туда, откуда оно появилось. Заметьте, что мы используем прозрачный цвет (transparent
) для границы; мы добавим цвет позже, также как и зависимость подсказки от направления flow
.
1 |
[tooltip]::before { |
2 |
content: ''; |
3 |
z-index: 1001; |
4 |
border: 5px solid transparent; |
5 |
}
|
Это не опечатка, что значение свойства имеет пустую строку — content: '';
. Нам там ничего не нужно, но нам нужно это свойство для работы псевдо-элемента.
Чтобы создать треугольник, мы задаем сплошную границу с некоторой толщиной для пустого прямоугольника (без содержимого), без ширины и высоты, и цвет будет только у одной стороны границы. Для получения дополнительной информации ознакомьтесь со следующим руководством:
4. Пузырики!
Мы добрались до сути. Заметьте что content: attr(tooltip)
говорит, “Этот псевдо-элемент должен использовать значение атрибута tooltip
в качестве контента.” Вот почему использование атрибутов вместо названия классов является отличным решением!
1 |
[tooltip]::after { |
2 |
content: attr(tooltip); /* magic! */ |
3 |
z-index: 1000; |
4 |
|
5 |
/* most of the rest of this is opinion */
|
6 |
font-family: Helvetica, sans-serif; |
7 |
text-align: center; |
8 |
|
9 |
/*
|
10 |
Let the content set the size of the tooltips
|
11 |
but this will also keep them from being obnoxious
|
12 |
*/
|
13 |
min-width: 3em; |
14 |
max-width: 21em; |
15 |
white-space: nowrap; |
16 |
overflow: hidden; |
17 |
text-overflow: ellipsis; |
18 |
|
19 |
/* visible design of the tooltip bubbles */
|
20 |
padding: 1ch 1.5ch; |
21 |
border-radius: .3ch; |
22 |
box-shadow: 0 1em 2em -.5em rgba(0, 0, 0, 0.35); |
23 |
background: #333; |
24 |
color: #fff; |
25 |
}
|
Заметьте, что значения z-index
присутствуют для обоих элементов и "dink", и пузырика. Это произвольные значения, но имейте в виду, что значения z-index
являятся относительными. Разъяснение: значение z-index: 1001 внутри элемента с z-index: 3, означает, что элемент 1001 будет самым верхним элементом внутри этого контейнера с z-index: 3.
Свойство пузырика z-index
должно быть как минимум на 1 меньше чем z-index
у "dink". Если это значение такое же или выше чем у "dink", вы можете столкнуться с эффектом неравномерной цветовой окраски "dink", если вы применяете box-shadow
для ваших подсказок.
Чтобы узнать подробнее о свойстве z-index, взгляните на следующее руководство:
5. Взаимодействие с действием
Наши подсказки активируются при наведении мыши на элемент с подсказкой… Почти.
1 |
[tooltip]:hover::before, |
2 |
[tooltip]:hover::after { |
3 |
display: block; |
4 |
}
|
Если вы вернётесь нашему блоку со стилями на Шаге 2, вы должны увидеть, что мы использовали opacity: 0;
наряду с display: none;
для частей нашей подсказки. Мы сделали так, чтобы использовать эффекты анимации CSS, при отображении и скрытии подсказки.
Свойство display
не может быть анимировано, а свойство opacity
может! И напоследок мы займёмся анимацией. Если вас не интересуют анимированные подсказки, просто уберите opacity: 0;
на Пункте 2 и не обращайте внимания на анимацию в Пункте 7.
Последнее, что нам нужно, все еще относится ко всем подсказкам — это способ подавить всплывающую подсказку, если у нее нет содержимого. Если вы заполняете всплывающие подсказки некоторой динамической системой (Vue.js, Angular, или React, PHP и т.д.), нам не нужны тупые пустые пузырьки!
1 |
/* don't show empty tooltips */
|
2 |
[tooltip='']::before, |
3 |
[tooltip='']::after { |
4 |
display: none !important; |
5 |
}
|
6. Управление направлением
Этот шаг может быть довольно сложным, поскольку мы будем использовать некоторые не столь распространенные селекторы, чтобы помочь нашим подсказкам определиться с их местами размещения на основе их flow
значений (или их отсутствия).
«В Круге-K происходят странные вещи» — Тед Теодор Логан
Прежде чем перейти к стилям, давайте посмотрим на некоторые шаблоны селекторов, которые мы будем использовать.
1 |
[tooltip]:not([flow])::before, |
2 |
[tooltip][flow^="up"]::before { |
3 |
/* ...
|
4 |
properties: values
|
5 |
... */
|
6 |
}
|
Это говорит браузеру: «Для всех элементов с атрибутом tooltip
, которые либо не имеют атрибута flow
, либо имеют flow
со значением, которое начинается с “наверх”: применить эти стили к своему псевдониму ::before
.»
Мы используем шаблон, так что они могут быть распространены на другие flow без необходимости повторять CSS. Этот шаблон flow^="up"
использует сопоставитель ^=
(начинается с). Это позволяет стилям также применяться к up-right и up-left (вправо-вверх и влево-верх), вы можете захотеть использовать такие направления. Мы не будем описывать их здесь, но вы можете увидеть, как они используются в моей оригинальной демонстрации всплывающей подсказки в CodePen.
Ниже перечислены блоки CSS для всех четырех направлений, рассматриваемых в этом уроке.
Наверх (по умолчанию):
1 |
/* ONLY the ::before */
|
2 |
[tooltip]:not([flow])::before, |
3 |
[tooltip][flow^="up"]::before { |
4 |
bottom: 100%; |
5 |
border-bottom-width: 0; |
6 |
border-top-color: #333; |
7 |
}
|
8 |
|
9 |
/* ONLY the ::after */
|
10 |
[tooltip]:not([flow])::after, |
11 |
[tooltip][flow^="up"]::after { |
12 |
bottom: calc(100% + 5px); |
13 |
}
|
14 |
|
15 |
/* Both ::before & ::after */
|
16 |
[tooltip]:not([flow])::before, |
17 |
[tooltip]:not([flow])::after, |
18 |
[tooltip][flow^="up"]::before, |
19 |
[tooltip][flow^="up"]::after { |
20 |
left: 50%; |
21 |
transform: translate(-50%, -.5em); |
22 |
}
|
Вниз:
1 |
[tooltip][flow^="down"]::before { |
2 |
top: 100%; |
3 |
border-top-width: 0; |
4 |
border-bottom-color: #333; |
5 |
}
|
6 |
|
7 |
[tooltip][flow^="down"]::after { |
8 |
top: calc(100% + 5px); |
9 |
}
|
10 |
|
11 |
[tooltip][flow^="down"]::before, |
12 |
[tooltip][flow^="down"]::after { |
13 |
left: 50%; |
14 |
transform: translate(-50%, .5em); |
15 |
}
|
Влево:
1 |
[tooltip][flow^="left"]::before { |
2 |
top: 50%; |
3 |
border-right-width: 0; |
4 |
border-left-color: #333; |
5 |
left: calc(0em - 5px); |
6 |
transform: translate(-.5em, -50%); |
7 |
}
|
8 |
|
9 |
[tooltip][flow^="left"]::after { |
10 |
top: 50%; |
11 |
right: calc(100% + 5px); |
12 |
transform: translate(-.5em, -50%); |
13 |
}
|
Вправо:
1 |
[tooltip][flow^="right"]::before { |
2 |
top: 50%; |
3 |
border-left-width: 0; |
4 |
border-right-color: #333; |
5 |
right: calc(0em - 5px); |
6 |
transform: translate(.5em, -50%); |
7 |
}
|
8 |
|
9 |
[tooltip][flow^="right"]::after { |
10 |
top: 50%; |
11 |
left: calc(100% + 5px); |
12 |
transform: translate(.5em, -50%); |
13 |
}
|
7. Анимируем всё
Анимация просто потрясающая штука. Анимация может:
- помочь пользователям чувствовать себя комфортно
- помочь пользователям с пространственной ориентацией в вашем интерфейсе
- привлечь внимание к вещам, которые необходимо увидеть
- смягчить элементы интерфейса, которые в противном случае имели бы только резкий эффект вкл/выкл
Наши подсказки будут подпадать под это последнее описание. Вместо того, чтобы всплывать и мгновенно выскакивать, давайте сделаем нашим текстовым пузырям более легкий эффект.
@keyframes
Нам понадобятся два вида анимации @keyframe
. Подсказки вверх/вниз будут использовать keyframe tooltips-vert
, а подсказки влево/вправо будут использовать keyframe tooltips-horz
. Обратите внимание, что в обоих этих keyframe мы определяем только желаемое конечное состояние всплывающих подсказок. Нам не нужно знать, откуда они появляются (в подсказках есть информация о стиле). Мы просто хотим управлять тем, куда они направляются.
1 |
@keyframes tooltips-vert { |
2 |
to { |
3 |
opacity: .9; |
4 |
transform: translate(-50%, 0); |
5 |
}
|
6 |
}
|
7 |
|
8 |
@keyframes tooltips-horz { |
9 |
to { |
10 |
opacity: .9; |
11 |
transform: translate(0, -50%); |
12 |
}
|
13 |
}
|
Теперь нам нужно применить эти keyframe к подсказкам, когда пользователь наводит курсор мыши на элементы с атрибутами [tooltip]
. Поскольку мы используем различные направления для управления отображением всплывающих подсказок, нам необходимо определить эти возможности в стилях.
Используйте :hover, чтобы передать управление анимации
1 |
[tooltip]:not([flow]):hover::before, |
2 |
[tooltip]:not([flow]):hover::after, |
3 |
[tooltip][flow^="up"]:hover::before, |
4 |
[tooltip][flow^="up"]:hover::after, |
5 |
[tooltip][flow^="down"]:hover::before, |
6 |
[tooltip][flow^="down"]:hover::after { |
7 |
animation: |
8 |
tooltips-vert
|
9 |
300ms
|
10 |
ease-out
|
11 |
forwards; |
12 |
}
|
13 |
|
14 |
[tooltip][flow^="left"]:hover::before, |
15 |
[tooltip][flow^="left"]:hover::after, |
16 |
[tooltip][flow^="right"]:hover::before, |
17 |
[tooltip][flow^="right"]:hover::after { |
18 |
animation: |
19 |
tooltips-horz
|
20 |
300ms
|
21 |
ease-out
|
22 |
forwards; |
23 |
}
|
Помните, что мы не можем анимировать свойство display
, но мы можем дать всплывающим подсказкам эффект затухания, манипулируя opacity
. Мы также анимируем свойство transform, которое придает подсказкам еле-заметное движение, как будто они вылетают, указывая на их теггированне элементы.
Обратите внимание на ключевое слово forwards
в объявлении анимации. Это говорит анимации не сбрасываться после одного выполнения, а двигаться вперед и оставаться до конца.
Вывод
Фантастическая работа! Мы многое рассказали на этом уроке, и теперь у нас есть аккуратная коллекция всплывающих подсказок, показывающих нашу напряженную работу:
Мы лишь нацарапали поверхность того, что можно делать с помощью всплывающих подсказок CSS. Повеселитесь, поиграйте с ними, продолжайте экспериментировать и придумывайте свои собственные рецепты!