Направление текста RTL

В веб-интерфейсах, рассчитанных на языки с направлением письма справа налево, поведение компонентов формы должно учитывать не только визуальное выравнивание текста, но и логику позиционирования элементов управления. К таким языкам относятся арабский, иврит, персидский и ряд других. При использовании Choices.js поведение селекторов требует дополнительной настройки, поскольку библиотека изначально ориентирована на LTR-макет.


Базовая модель RTL в браузере

Современные браузеры определяют направление текста через атрибут dir, применяемый к DOM-элементу:

<html dir="rtl">

или локально:

<div dir="rtl">
  <select></select>
</div>

Этот атрибут влияет на:

  • выравнивание текста
  • направление курсора
  • порядок визуального расположения inline-элементов
  • базовую модель позиционирования

Однако большинство JavaScript-компонентов, включая Choices.js, не полагаются полностью на dir, а используют собственные классы и inline-стили.


Поведение Choices.js без дополнительной настройки

При инициализации стандартного селектора:

const element = document.querySelector('select');

const choices = new Choices(element, {
  searchEnabled: true,
  removeItemButton: true
});

в RTL-контексте проявляются типичные несоответствия:

  • выпадающий список открывается с LTR-логикой выравнивания
  • иконка удаления элемента смещается вправо некорректно
  • строка поиска остается левосторонней
  • маркеры выбранных элементов не зеркалируются

Эти эффекты особенно заметны в мультиселекте.


Применение RTL через HTML и контейнер

Наиболее предсказуемая модель — установка направления на контейнере:

<div class="form-wrapper" dir="rtl">
  <select id="countries" multiple></select>
</div>

Choices.js наследует базовое направление, но визуальные компоненты требуют дополнительной CSS-коррекции.


CSS-коррекция для RTL в Choices.js

Основная адаптация выполняется через переопределение классов библиотеки.

Выравнивание текста и контейнеров

.choices {
  direction: rtl;
  text-align: right;
}

Коррекция внутреннего input-поля поиска

.choices__input {
  text-align: right;
}

Элементы выбранных значений

.choices__list--multiple .choices__item {
  float: right;
}

Позиционирование выпадающего списка

Одной из ключевых проблем является направление раскрытия dropdown. По умолчанию Choices.js вычисляет позицию относительно левого края.

Для RTL-режима требуется переопределение:

.choices__list--dropdown {
  right: 0;
  left: auto;
  text-align: right;
}

При необходимости фиксированного позиционирования:

.choices[data-type*="select-one"] .choices__list--dropdown {
  right: 0;
  left: auto;
}

Инверсия иконок и кнопок управления

В RTL-интерфейсе стандартная логика кнопок удаления и стрелок становится визуально некорректной.

.choices__button {
  margin-left: 0;
  margin-right: 8px;
}

Для крестика удаления в мультиселекте:

.choices__item .choices__button {
  float: left;
}

Многострочные значения и переносы

В RTL-среде перенос строк и обтекание элементов может нарушать читаемость выбранных тегов.

Рекомендуемая настройка:

.choices__inner {
  white-space: normal;
}

и дополнительное управление переполнением:

.choices__list--multiple {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
}

Обработка поисковой строки

Поисковый input внутри Choices.js не всегда корректно наследует направление текста.

.choices__input--cloned {
  direction: rtl;
  text-align: right;
}

При активном поиске важно сохранить поведение курсора справа налево, иначе ввод становится нелогичным для пользователя.


Динамическое переключение LTR и RTL

В некоторых интерфейсах направление может меняться в зависимости от локали.

function setDirection(instance, dir) {
  const container = instance.containerOuter.element;
  container.setAttribute('dir', dir);
}

Пример переключения:

setDirection(choices, 'rtl');
setDirection(choices, 'ltr');

При динамическом изменении направления требуется повторная корректировка dropdown-позиционирования, так как Choices.js не пересчитывает layout автоматически.


Проблемы позиционирования при overflow и scroll

В сложных контейнерах с overflow: hidden dropdown может смещаться в неправильную сторону.

Решение:

.choices__list--dropdown {
  position: absolute;
  max-width: 100%;
}

и контроль контекста позиционирования:

.form-wrapper {
  position: relative;
}

Особенности мультиселекта в RTL

Мультиселект в RTL требует зеркалирования логики потока элементов:

  • новые элементы визуально добавляются слева
  • кнопки удаления должны оставаться доступными слева
  • контейнер должен использовать flex с правым выравниванием

Рекомендуемая модель:

.choices__list--multiple {
  display: flex;
  flex-direction: row-reverse;
  flex-wrap: wrap;
  justify-content: flex-start;
}

Комбинированная конфигурация для стабильного RTL

Наиболее устойчивое поведение достигается при совместном применении HTML-атрибута, CSS и ограниченной логики JS:

<div class="choices-rtl" dir="rtl">
  <select multiple></select>
</div>
.choices-rtl .choices {
  direction: rtl;
}

.choices-rtl .choices__inner,
.choices-rtl .choices__input {
  text-align: right;
}

.choices-rtl .choices__list--dropdown {
  right: 0;
  left: auto;
}

.choices-rtl .choices__list--multiple {
  display: flex;
  flex-direction: row-reverse;
}

Влияние RTL на производительность и рендеринг

Сам по себе RTL не увеличивает нагрузку, однако при частых переключениях направления возникают повторные перерасчеты layout:

  • reflow dropdown-меню
  • пересчет flex-контейнеров
  • пересоздание позиционирования floating элементов

При большом количестве экземпляров Choices.js это может приводить к заметной задержке при массовых обновлениях DOM.


Стилизация состояния фокуса в RTL

Фокусное состояние также требует зеркальной логики:

.choices.is-focused {
  box-shadow: 0 0 0 2px rgba(0,0,0,0.15);
}

При необходимости усиления визуального контроля:

.choices__inner:focus-within {
  border-color: #666;
}

Совместимость с кастомными темами

При использовании кастомных тем важно учитывать, что любые абсолютные значения left и right должны быть условными.

Типичная ошибка:

.choices__list--dropdown {
  left: 0; /* ломает RTL */
}

Корректный подход:

.choices__list--dropdown {
  inset-inline-start: auto;
  inset-inline-end: 0;
}