Использование CSS для управления выделением текста

CSS позволяет управлять, поведением и внешним видом выделенного текста на страницах HTML-документа. Это может помочь улучшить юзабилити в определенных ситуациях и добавить немного визуального чутья.

Выделить всё

Иногда бывает полезно, чтобы весь текст в элементе автоматически выделялся при нажатии на него. Это особенно удобно для текста, который копируется/вставляется полностью (фрагменты кода, одноразовые пароли, промокоды и т.д.).

Это можно сделать с помощью только простого CSS и без какого-либо JavaScript!

div {
  -webkit-user-select: all; /* для Safari */
  user-select: all;
}

Например, так:

See this code Select All on x.xhtml.ru.

Выделить всё… Затем выделить часть

Хотя это работает должным образом, можно заметить кое-что неприятное: невозможно выбрать что-то отдельное, кроме всего фрагмента кода. Хорошо бы первым кликом выбрать всё, но оставить возможность кликнуть ещё раз и выбрать только часть. Это можно сделать, всё ещё, с помощью только CSS.

Во-первых, надо использовать tabindex, чтобы сделать HTML-элемент, содержащий текст, доступным для фокусировки. Это позволит CSS узнать о клике по HTML-элементу.

<code tabindex="0">Это фрагмент кода</code>

Теперь немного CSS.

code {
  -webkit-user-select: all;
  user-select: all;
}

code:focus {
  animation: select 100ms step-end forwards;
}

@keyframes select {
  to {
    -webkit-user-select: text;
    user-select: text;
  }
}

Идея состоит в том, чтобы сначала установить для HTML-элемента CSS-свойство user-select: all, а затем, когда фокус переместится на этот элемент, переключиться на «обычный» user-select: text, чтобы текст можно было выбирать по частям. Выбор времени переключения – дело непростое. Если сделать переключение сразу после перемещения фокуса на элемент, то от user-select: all не будет никакого эффекта, т.к. оно успеет сменить значение на text. Поможет решить проблему animation.

CSS-свойство user-select можно анимировать. Оно дискретно анимированное, это означает, что нет постепенной интерполированной анимации, по истечении указанного времени происходит немедленный переход от одного состояния к другому. Поэтому, можно использовать animation, чтобы отложить изменение поведения выделения текста до 100 миллисекунд после передачи фокуса HTML-элементу.

See this code Select All… Then Select Some on x.xhtml.ru.

Предотвращение выделения текста

CSS можно использовать ещё и для того, чтобы сделать текст в элементе недоступным для выбора, т.е. запретить выделение текста.

label {
  -webkit-user-select: none;
  user-select: none;
}

Это плохая идея для основного текста, но будет полезно для элементов управления, которые можно быстро переключать или «гневно щелкать» в настольных браузерах, поскольку двойной щелчок вызывает выбор текста и подсветку его выделения – выглядеть немного странно и дико.

Проверить можно в следующей демонстрации. Обратите внимание, как переключатель слева становится подсвеченным при быстром нажатии, а переключатель справа – нет.

See this code Preventing Text Highlights from Rage Clicking on x.xhtml.ru.

Этот метод также работает с виджетами раскрытия содержимого (HTML-элемент details) или фальшивыми кнопками – например, <div> с обработчиком кликов на нём. Правда, использование настоящего элемента <button> предпочтительнее не только с точки зрения семантики и доступности, но и потому, что текст в такой кнопке по умолчанию не может быть выделен, а это позволяет избежать проблемы с самого начала.

Выборочное выделение текста

Невыделяемый текст можно смешивать с выделяемым текстом. Невыделяемые части просто пропускаются при выборе текста и будут пропущены при копировании/вставке выделения.

В примере ниже используется user-select: none на числовых маркерах сносок. Поэтому, когда выделенное копируется/вставляется, маркеры автоматически удаляются.

See this code Declaring Bits of Unselectable Text on x.xhtml.ru.

Но это работает не во всех браузерах. Safari (iOS и компьютер) и Android Chrome по-прежнему будут копировать маркеры.

Стилизация выделения

Стилизовать выделенный текст можно с помощью псевдоэлемента ::selection. Однако настройки ограничены тремя CSS-свойствами: color, background-color и text-shadow (в спецификации их больше, но всё портит поддержка свойств браузерами).

Вот пример стилизации выделенного текста в HTML-элементе <p>.

p::selection {
  color: #fffb07;
  background-color: #ff063b;
  text-shadow: 2px 2px #028e4a;
}

Чтобы увидеть результат, выделяйте текст в примере ниже.

See this code Styling Text Selections on x.xhtml.ru.

Немного особенностей

Есть еще одна декларация user-select: contain, которая должна ограничивать выделение текста внутри элемента, как это работает с <textarea>. Однако, IE11 был последним браузером, который поддерживал это. В настоящее время значение contain не поддерживают все современные браузеры.

Тем не менее, все редактируемые элементы (такие как <textarea>) обрабатываются, как если бы они имели user-select: contain. Псевдоэлементы ::before и ::after нельзя выбрать, как если бы у них было установлено user-select: none. Изменить такое поведение не получится.

Всё вышенаписанное было про CSS, но всё-таки стоит упомянуть JavaScript в контексте выделения текста и копипасты.

Если нужен полный контроль над выделением текста, пригодится JavaScript Selection API. Если конечной целью является копирование/вставка текста, то JavaScript позволяет взаимодействовать с буфером обмена.