Пользовательские интерфейсы. Дропдауны

Современные веб-сайты, зачастую содержат множество различных интерфейсных компонент, которые будут использованы во многих частях сайта: это различные попапы, формы с вводом данных, таблицы, меню и рассматриваемые сегодня дропдауны. Оговорюсь сразу, под дропдауном будем понимать следующий элемент интерфейса: При создании такого элемента интерфейса, как дропдаун, разработчик должен решить ряд задач: В статье предлагаю разобрать плюсы и минусы различных подходов к построению поведения и логики работы дропдауна.

Как дропдаун будет получать данные

У данной задачи существует несколько решений. В общем случае их можно выделить в две основные группы:

Контент находится в DOM

В данном случае контент находится в DOM внутри некоторого тега, например, div. Как дропдаун может узнать, где находится его контент?
Первый способ: уникальные data атрибуты - в данном случае, активный элемент имеет некоторый уникальный параметр как и контент дропдауна. При нажатии на активный элемент остается лишь найти контент и отобразить его. Плюсы данного подхода: + Элементы могут располагаться в любом месте DOM дерева + Имеется возможность сделать следующую модель - контент отображается на сайте в некотором месте, при вызове дропдауна контент перемещается и отображается в дропдауне + Контент может быть как видимым на странице, так и скрытым - получаем дополнительную гибкость Минусы: - При увеличении количества дропдаунов на странице, необходимо присваивать каждой паре (активный элемент \ контент) свои уникальные значения - Если контент динамический и подгружается не в момент загрузки страницы, необходимо позаботиться, чтобы дропдаун "получал" необходимый элемент только при своем открытии, а не в момент инициализации компонента Как бороться с минусами: 1) Добавлять значение счетчика к каждому новому dropdown (необходимо завести счетчик в шаблонизаторе) 2) Получать элемент-контент во время вызова дропдауна
Второй способ: контент дропдауна находится скрытым внутри активного элемента. Плюсы: + Получить контент не составляет труда - достаточно взять child активного элемента Минусы: - При использовании таких тегов как input, a, button не весь контент будет доступен Например, если в IE9< использовать активный элемент button и в контенте использовать input, получится так: Как чинить? Для того, чтобы устранить данный минус, можно помещать контент внутри тега script. Таким образом, можно будет избежать искажения контента. (Важное условие - при отображении элемент дропдауна должен находиться вне активного элемента. Этот момент рассмотрим далее) Однако, данный способ "ремонта" несет другие минусы. Например, у нас есть форма: При клике на dropdown получаем следующее содержимое: Таким образом, форма состоит из двух компонентов - input'а и двух checkbox'ов в дропдауне. Если держать контент внутри тега script, то при закрытии дропдауна значение чекбокосов будет теряться. Решением данной проблемы будет копирование чекбоксов в input type=hidden в момент закрытия дропдауна, либо изменения состояния чекбокса

Контент генерируется JS, вызывающем dropdown

Здесь существует два подхода. Первый подход - скрипт, на этапе создания дропдауна (вызова конструкторв), передает поле, в котором содержится html, который необходимо отобразить при клике на активный элемент. + Подход предоставляет гибкость для расположения контента Из неудобств можно выделить необходимость использовать некоторые уникальные аттрибуты (классы, data-аттрибуты, id) Здесь важно понимать, что если у нас будет представлена форма, внутри которой есть дропдаун с дополнительными полями - очень важно, чтобы при закрытии контент формы обновлялся. (как пример - контент дропдауна перемещался в дропдаун <-> в форму) Второй подход - скрипт задает необходимый html для дропдауна явно. При клике на дропдаун js отправляет весь необходимый дропдауну контент для отображения. В данном случае контент генерируется динамически и данный способ может быть удобен только в таких случаях, когда содержимое дропдауна отвязана от статических форм, данных. В данном случае вопрос работы корректной дропдауна и формы проявляется наиболее остро. Таким образом, подведя итог, можно выделить наиболее удобных способ работы с контентом - контент генерируется js, причем в дропдаун должен передаваться либо необходимый html, либо определенный уникальный селектор для содержимого дропдауна.

Положение дропдауна

Содержимое дропдауна может находиться как на одном уровне со своим активным элементом-триггером (иметь общих родителей), либо находиться в самом конце body. Рассмотрим плюсы и минусы каждого подхода. В конце body: + Нет зависимости от того, скрыты родители или нет, установленные overflow: hidden или нет + зависимость отображения дропдауна только от view'порта - Дропдаун не перемещается вместе с элементом-триггером: C данным минусом можно бороться путем создания различных таймеров-наблюдателей за положением элемента-триггера, либо, что представляется лучшим решением - комбинировать реализацию вместе с отображением дропдауна на одном уровне с элементом-триггером На одном уровне со своим активным элементом-триггером: + Дропдаун перемещается со своим элементом-триггером - Любой overflow: hidden у родителя может обрезать дропдаун. Например, так: В данном случае решения, которое было бы универсальным - не существует. Поэтому поведение дропдауна может различаться в зависимости от его задачи и HTML окружения. Поэтому для решения можно реализовать такой дропдаун, который при необходимости может менять свое поведение, например так, как это реализовано в UIComponents: Дропдаун