Оформление элемента select
Евгений, 10 сентября 2007
Все знают, как уродливо иногда смотрится элемент select, изменить некоторые особенности его оформления невозможно, поэтому часто он становиться худшим местом превосходного дизайна. Не нужно с этим мириться, тем более что решение проблемы уже давно найдено.
<Select>
В коде элемент select выглядит, примерно, так:
<select id="something" name="something" tabindex="101">
<option value="1">Вариант 1</option>
<option value="2">Вариант 2</option>
<option value="3">Вариант 3</option>
<option value="4">Вариант 4</option>
<option value="5" selected="selected">Вариант 5</option>
</select>
а на странице он может выглядеть так:

или так:

в зависимости от операционной системы и браузера.
Давайте представим, что мы хотим, чтобы он выглядел одинаково во всех браузерах и идеально подходил к дизайну сайта, например, нам нужен такой select:

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

При этом сохраняется вся функциональность элемента select, в том числе возможность работать с клавиатуры и назначения ему tabindex, а возможности его оформления ограничиваются только вашей фантазией. Я подготовил пример, демонстрирующий возможности этого скрипта. Чтобы изучить скрипт вы можете скачать, подробно комментированный, исходник примера (zip), на странице код будет сильно сжат и не подсвечен, что значительно затрудняет просмотр.
Для оформления использовано два изображения, и техника Sliding Doors, единственный нюанс в том, что в нижнем изображения есть прозрачная область в которую помещается стрелка с верхнего изображения.
Скрипт протестирован в браузерах IE6, IE7, FF2, Opera 9, если вы обнаружите баги, сообщите, пожалуйста.
Базой для написания этого скрипта послужили, статьи Аарона Густафсона, <select> Something New, Part 1 и <select> Something New, Part 2.

















Ilya 10 сентября, 2007 21:59 #
Отличная идея и реализация!
Однако есть небольшой минус - если раскрыть обычный выпадающий список, то не обязательно выбирать значение - можно кликнуть в пустое место на странице и тогда он автоматически уберется с экрана (закроется). Однако в этой реализации список убирается только если выбрать одно из значений. Понимаю, что не всем нужна такая функциональность, но мало ли ;)
P.S. проверял только в Maxthon (IE6).
pepelsbey 10 сентября, 2007 22:08 #
Доступность и удобство таких элементов — это главный их баг, недостаток и аргумент против использования.
Давайте опишем плюсы: красиво выглядит.
Давайте опишем минусы:
— Пытаюсь попасть на этот элемент клавишей таб: никак, в том числе и открыть.
— Открываю этот «селект» руками: курсор почему-то pointer. М?
— Пытаюсь работать: клавиши вверх/вниз и пробел/ввод не воспринимает, подбор по первой букве слова не работает.
— Пытаюсь закрыть выпадающий список без выбора чего-либо (в случае, если это поле не является обязательным для заполнения) — ни фокус на друом элементе формы, ни клик по окружающим элементам не приводит к закрытию этого существа.
И что же мы имеем? Абсолютно бесполезную вещь.
За статью как всегда спасибо ;) Но на вещи всё-таки стоит смотреть более трезво. А то вернёмся к DHTML'ю муравьями, которые бегают за курсором…
Евгений 10 сентября, 2007 22:25 #
Tab работает, элемент при этом подсвечивается, и можно листать список стрелками, как в обычном select.
Pointer убрал.
Подбор по первой букве сделать можно, но скрипт станет сложнее, а ведь такой функционал нужен только если опций много.
Закрытие по клику на другие элементы, действительно упустил, сделаю.
pepelsbey 10 сентября, 2007 23:15 #
Сорри, я смотрел в Safari, там от всего этого великолепия мало что осталось и кое-что неожиданное появляется. В Firefox при выборе пункта после передвижения по Tab'у одна за другой вылезают ошибки «li has no properties».
Я высказался в предыдущем комменте не потому, что считаю, что у этого скрипта чего-то не хватает, а потому, что в принципе являюсь противником замены системных/браузерных контролов чем-либо. Правда, за небольшим исключением: если у вас получится сделать контрол который нельзя будет отличить по поведению от системного, то это можно рассматривать как рабочий вариант. Пока, к сожалению, нет даже половины стабильных признаков системного селекта.
Евгений 10 сентября, 2007 23:32 #
Вот сейчас и отладим (а скорее всего завтра), спасибо за ценные замечания и конструктивную критику.
Dmytro Shteflyuk 10 сентября, 2007 23:38 #
Насчет ненужности подбора по первым буквам - это вы зря. Я редко раскрываю селекты с названием страны. Проще нажать 3 раза u (USA -> Uganda -> Ukraine), чтобы выбралась нужная мне страна. Я вообще не знаю, сколько там стран с этом списке! Если элементов больше чем 2 (Yes/No, True/False -- и вообще для этого лучше радиобаттоны) или 3 (Male/Female/Undefined),-- то обязательно нужна работа с клавиатуры.
Не работает Alt-ArrowDown, у меня вообще не получается его раскрыть с клавиатуры :-( Про ошибку в JavaScript "li has no properties" уже сказали.
ЗЫ. Я не имею ничего против замены стандартных контролов нестандартными, если только это работает ожидаемо.
smmurf 10 сентября, 2007 23:38 #
Эх... статья хорошая, но в этот раз, по-моему, в ней дан хороший пример того, как не надо делать.
Прокомментирую, почему (хотя частично это уже было сказано, скорее, добавлю):
— нехарактерное поведение для этого элемента управления (даже если научить его скрываться при потере фокуса): возможность перемещаться по элементам по мере ввода с клавиатуры, может, и не нужна для коротких списков, но пользователь может попытаться пользоваться ей просто по привычке. И будет неприятно удивлен. Думаю, добиться полного соответствия в поведении стандартного элемента и эмулированного на JS будет очень нелегко, а пока этого не произойдет, пользователей будут ждать разные сюрпризы, преимущественно неприятные.
— но есть и более серьезный недостаток, который неизбежно вытекает из того преимущества, ради которого все и было затеяно (сам плохо понял, что написал). Это — настраиваемость внешнего вида. То есть вместо привычного для себя select'а пользователь увидит что? Да что угодно, только не нормальный select! И нет никакой гарантии, что он сразу поймет, что это перед ним, и что надо с этим делать. Если бы я не знал, о чем читаю, то полагаю, что увидев такой контрол, растерялся бы на несколько секунд как минимум. Пользователь привык видеть контролы такими, какими он привык их видеть, -) и не надо ломать его привычек. В этом вопросе я бы отдал предпочтение принципу "пусть безобразно, зато однообразно". Хотя все это — сугубо мое личное мнение.
Впрочем статья от этого не теряет ценности в качестве учебного материала. Спасибо за нее -)
Dmytro Shteflyuk 10 сентября, 2007 23:43 #
Да, еще если уйти по TAB, список тоже не сворачивается. Про клик на свободном месте уже говорили... Но больше всего смущает то, что пропадает стрелка, и вместо нее рисуется список длинный. Как-то не очень ожидаемо - нажал кнопку "выпасть вниз" (именно так я читаю стрелку), а оно мне черт знает что сделало.
Кстати о длине, что будет, если элементов 50-100? скроллбар? как оно будет себя вести? Какие элементы покажутся при раскрытии списка? Как будут работать стрелки вверх/вниз, page up/page down, home/end? Обычно все скрипты страдают этой проблемой...
Евгений 10 сентября, 2007 23:44 #
Ошибку "li has no properties" исправил.
Понял, что возможность перемещения по буквам нужна обязательно, добавлю.
Хм, я посмотрел, стандартные селекты листаются стрелками не открываясь.
Dmytro Shteflyuk 10 сентября, 2007 23:44 #
Ух, завалили комментами :-) Ну тем лучше, даешь скрипты народу, идеи дизайнерам и пятилетку за три года!
Dmytro Shteflyuk 10 сентября, 2007 23:45 #
А если alt зажать? :-)
smmurf 10 сентября, 2007 23:46 #
Alt+стрелка вниз
Евгений 10 сентября, 2007 23:47 #
я и не знал, что их с альтом можно :)
как выловить такую комбинацию подскажете?
smmurf 10 сентября, 2007 23:49 #
Есть тут одна мысль - стандартный select оставить.
Разве что из спортивного интереса этот скрипт можно еще помучить -)
Dmytro Shteflyuk 10 сентября, 2007 23:54 #
event.shiftKey
event.altKey
event.ctrlKey
Евгений 10 сентября, 2007 23:59 #
Спасибо.
Есть одна идея, но требует полной реорганизации этого скрипта займусь завтра.
Евгений 11 сентября, 2007 0:26 #
Хм, выбор по буквам работает (унаследован от select) просто не во всех браузерах.
Евгений 11 сентября, 2007 0:32 #
В FF и Opere выбор по буквам не работает даже в обычном селекте, или я что-то не так смотрю, в IE все без проблем выбирается по буквам.
Dmytro Shteflyuk 11 сентября, 2007 0:43 #
О! ты наткнулся на баг ФФ и оперы :-) Там не работает поиск по первым русским буквам. Всегда бесило
remal 11 сентября, 2007 1:02 #
бесполезно.
Максим Россомахин 11 сентября, 2007 7:58 #
Сомнительная полезность напрасного украшательства.
Сергей 11 сентября, 2007 10:01 #
Совершенно бесполезная фишка, на мой взгляд, осознание этого скорее всего придет не сразу, но обязательно придет.
Подумайте как это будет работать на коммуникаторах? С отключенной графикой?
Шильгия 11 сентября, 2007 12:28 #
не везде. В сафари открываются.
Nikita 11 сентября, 2007 12:50 #
Хороший скрипт, есть цель для развития.
Мне нравится такое решение
http://c82.net/samples/checklist-samples.html
Хотя оно громоздкое и поиск по буквам не работает, но красивое.
Для НЕмультивыбора, checkbox заменить на radio.
Не нравится нестандартный селект? Давайте тогда заменим меню на сайтах как [File] [Edit] [View]. Вы пользователей совсем уж тупыми считаете.
Я конечно понимаю, что консервативность нынче в моде, но не до такой же степени? Опять же дизайн и цветовая гамма сайта могут уж слишком контрастировать со стандартными контролами.
ekwo 11 сентября, 2007 15:41 #
Едея класс! Реализация тоже. Но с точки зрения юзабилити есть минус: если человек передумал делать выбор в нашем Select'е, он никак не может скрыть его не нажав на элемент самого Select'а (в котором он передумал что-то нажимать). Во всех операционках при нажатии на пустую область экрана элементы Select'а прячутся, а тут нет. Для пользователя это не удобно, хоть и выглядит красиво.
Евгений 11 сентября, 2007 16:37 #
Исправил проблему с закрытием по клику на свободной области окна или по уходу через tab, потестите кому не лень.
С раскрытием по alt+вниз проблемы, причем не столько с самим раскрытием сколько с тем что в опере раскрывается и оригинальный select, к тому же, при раскрытом табе onchange только после выбора пункта происходит.
Видимо оринальный select придется скрыть совсем, а для получание фокуса использовать спрятанное текстовое поле, минус решения в том, что придется по буквам выбирать самостоятельно, но как плюс будут работать русские буквы в Opera и FF.
Что касается нестандартности оформления, то помоему не проблема нарисовать select по которому будет однозначно видно, что это select.
Повторять поведение стандартного элемента, тоже не проблема, а вот отключенная графика это да, стрелку ведь без графики ни как не нарисуешь. Хотя можно попробовать изобразить ее несколькими блоками, но возникает вопрос, как определить, что графика не загружена? (есть, конечно, идеи, но хочется сначала узнать, что нибудь новое).
Поскольку скрипт постоянно изменяется убрал второй пример.
pepelsbey 11 сентября, 2007 17:47 #
Ни в FF, ни в Safari не получается выбрать другой пункт из выпавшего списка.
Евгений 11 сентября, 2007 18:44 #
Исправил.
Для соблюдения taborder`а при раскрытии списка передаваля фокус соответствующему select, а при нажатии tab список должен был сворачиваться по blur. При клике мышкой получалось, что select терял фокус и список сворачивался по blur, события click вообще не происходило (почему? ведь должно быть хотя бы в пустое место). Вместо blur теперь обрабатываю клавишу tab, заработало.
Оффтоп: пошел качать Safari немного удивило то? что по умолчанию галочка стоит на версии для Windows, как-то не патриотично для Apple :)
smmurf 11 сентября, 2007 19:55 #
Это нормально. Лучше недооценить пользователя и сделать интерфейс проще, чем чуть-чуть переоценить и получать жалобы.
Где-то даже попадались обобщенные правила, как создать юзабильный интерфейс. Там (утрированно) говорилось, что лучше предполагать, что пользователь читать не умеет. Потому что он никогда не читает хелп и текст в диалоговых окнах, и ведь это так!
Пусть лучше контрастируют, чем причиняют неудобства.
Вы, судя по всему (без обид) судите о юзабилити только по личным наблюдениям. Ознакомьтесь с литературой по этой теме, часто это "открывает глаза".
Думаю, у них просто определяется ОС посетителя -) Это, как раз, пример внимательного отношения к юзабилити.
Руслан 11 сентября, 2007 21:01 #
У меня в сафари все работает, тока стрелками нельзя по списку передвигаться! Спасибо за интересную статью!
leMur 11 сентября, 2007 21:04 #
Как веб-дизайнер, и как разработчик, я считаю эмуляцию селекта неэтичным.
Большинство из нас тут пропагандируют стандарты. И тут замена стандартных элементов. Чем вас не устраивает работа селекта? Видом? Оставьте. Графическое исполнение селекта - это отголосок той эпохи, когда дизайнеры ворочаясь во сне бормотали себе под нос: "эффекты, эффекты", эпохи фотографических background и надписей с тенями через drop shadow. Проще, правильнее и профессиональнее составить композицию элементов сайта и подобрать цветовую гамму так, что бы сдандартный селект смотрелся как отрисованная картинка. Зайдите в рейтинг Alexa, просмотрите топ российских сайтов, как видите я не голословен.
Но если вернуться к спортивному интересу, то скрипт достоин похвалы. Оригинальную версию скрипта изучал еще давно, ваши дополнения его значительно улучшили. Не тестировали его на скорость загрузки, скажем сайта с тремя селектами?
Евгений 12 сентября, 2007 14:09 #
В скрипте нет ничего столь затратного, чтобы можно было заметить замедление на трех селектах поэтому я потестил с 50 селектами, никаких заметных тормозов нет, при том что обработчик стоит на событии load, а можно поставить на DOMContentLoaded (на реальных сайтах так и нужно) тогда замена будет происходить еще раньше.
Artima 21 сентября, 2007 17:26 #
Работа очень полезная! Только я бы применял ее не украшения ради, а чтобы развить возможности стандартного селекта. Из очень полезного развития можно отметить Ajax и добавление в качестве пуктов в списке других контроллов (как в Гугле - выпадает список, а там чекбокс), дополнительное оформление пунктов.
pro 21 сентября, 2007 21:41 #
не плохо и интересно...
larin 24 сентября, 2007 21:59 #
Еще один недостаток: посмотрел пример без графики и вообще не увидел select-ов. С первого взгляда это обычные текст.
Igor 7 октября, 2007 18:45 #
Как дизайнер, скажу, что решение просто супер! Бывают случаи когда действительно необходимо использовать нетипичные списки, несмотря на "негласное табу". Вот только непонятно, как сделать так, чтобы на странице часть списков была дефолтной, а часть вот такой красивой?
Евгений 7 октября, 2007 20:17 #
Сделать так достаточно просто, в функцию srReplaceSelects добавим проверку класса элемента перед его заменой, и будем заменять только элементы с классом replaceThisSelect
пример
daniar 27 октября, 2007 15:18 #
Я конечно извиняюсь, но, когда я открыл селект то из под низу просвечивался другой обычный селект. Разве в ИЕ вы такого не видели?
Евгений 27 октября, 2007 16:20 #
Странно ничего подобно не видел. Cейчас посмотрел еще раз, в IE обычный селект нигде не просвечивается. Какую версию IE вы используете?
Scooter 28 октября, 2007 15:10 #
А я просто пользуюсь готовыми JavaScript компонентами NWS... В частности выпадающим списком NWSselect. По-моему очень удобно, вот можете сами глянуть http://neversleep.ru/?NWSselect
Dirty element 30 октября, 2007 9:50 #
Спасибо, Евгений! Интересный подход.
Dirty element 30 октября, 2007 9:52 #
Чуть не забыл.. Ещё бы добавить скрытие списка при нажатии Escape
Евгений 30 октября, 2007 22:48 #
Я тоже с в основном пользуюсь готовыми компонентами, но иногда хочется придумать, что-то свое, а иногда готовым компонентам не хватает чего нибудь, что нужно в конкретном случае.
Спасибо за ценное замечание, доработаю.
Денис 27 ноября, 2007 16:59 #
При использовании скажем 2-ух селектов, как в примере выше, где 1 селект заменяемый, а другой нет, вылезает проблемма (IE) наложения diva (я раньше с этим сталкивался только при использовании divov) на select, когда нижний select просвечивается сквозь div.
Scooter 14 февраля, 2008 13:22 #
Да дивы вылезают всегда поверх всего на странице в ИЕ. Нормального способа обойти это еще не найдено. Ошибка не исправлена. Поэтому это аргумент в сторону использования не настоящий селектов, а их эмуляции. Вот например NWSselect о котором я уже говорил
Юрий 17 февраля, 2008 18:46 #
NWSselect - вы сами не работаете в NWS, уважаемый=)? В интернете про него только на этом сайте можно найти, да на самом NWS. Что, конечно, его достоинств не умаляет. Но, к сожалению, ребята по данной вами ссылки и на вопросы не отвечают, и продавать не хотят.
Так что пришлось дорабатывать выложенный тут пример под свои нужды, как умеем.
Может быть, я не просто не нашел версии тут нужной, но, Евгений, сворачивание без выбора так и не было вами доделано?
Главный баг, в общем. ради которого я пишу сюда - в IE6 при прокрутке мышью страницы с открытым селектом, почему-то не страница прокручивается, а выбранный предыдущий элемент! Т.е., если список будет большим, то страницу просто напросто не прокрутить..=(
Юрий 17 февраля, 2008 18:50 #
Хотя, похоже, это наследие всех доделанных прелестей, таких как управление стрелками..
Евгений 18 февраля, 2008 20:03 #
Главный баг, как вы его обозвали, это поведение элемента select по дефолту, если на нем фокус то колесиком мышки можно перелистывать пункты.
Именно спрятанные дефолтный селект создает некоторые проблемы, в ближайшее время я постараюсь вернуться к этой тематике и сделать более функциональный элемент. Основная мысль такая, избавляемся от select совмем, фокус и события ловим через input text, а отсылаем значение через input hidden (через text будет не очень удобно), при этом можно организовать удобный поиск по списку и при желании что-то вроде suggestions.
Юрий 18 февраля, 2008 21:38 #
Да, заметил, что если даже поставить display:none у исходного селекта, сразу ругается как раз про фокус..
Для сворачивания, если мышь уходит, лично я доделал так, хотя, наверное, коряво это весьма:
srAddEvent(window, 'mousemove', srOnDocumentClick); //для FF
document.body.attachEvent("onmousemove",srOnDocumentClick); //для IE и остального..почему-то с srAddEvent такой же код добавления события не сработал...
а у самого списка и таблицы обрамляющей добавлено onmousemove="event.cancelBubble = true"
Будем ждать ваших профессиональных решений =)
Slv 3 апреля, 2008 4:24 #
Разработка конечно-же интересная, с точки зрения примера возможностей JS. Афтару респект.
Но на практике, ради дропдауна увеличивать страницу на порядка 10-ти килобайт кода, помоему это многовато, для обычного дропдауна.
Опуская вышесказанное, нужна ещё функциональность, а именно:
1. Ширина. В обычном дропдауне, она подбирается автоматически, в зависимости от максимальной ширины строки. Зачастую заранее не знаешь, какой ширины будет используемая строка. А в этом, она прибита гвоздями, причём для нескольких дропдаунов на странице, она всегда будет одинакова, что не простительно.
Нужно иметь выбор, либо прибить ширину гвоздями, либо она подбирается автоматически.
2. Стили. Какой ни какой стандартный дропдаун, но всётаки, коекакие стили на него можно положить. Этот-же имеет всегда один стиль. Нужна возможность задания стиля для каждого дропдауна на странице.
3. Неприятность с большим выбором. Что мы имеем в обычном дропдауне, когда его список очень велик, и не умещается на одной странице? Правильно. Появляется роллер прокрутки всего списка. Что мы видим тут? Ого. Растягивание всей страницы на всю длинну дропдауна. Это просто непростительно, ни с какой точки зрения, даже если этот дропдаун идеально заточен под общую стилизацию страницы.
4. Первая строка. Было-бы не плохо, таки оставлять первую строку выбора (та что со стрелкой), и уже под ней разворачивать весь список. Это будет более наглядно, и красноречиво говорить о том, что это именно дропдаун, а не что-то другое.
Александр 13 апреля, 2008 16:39 #
да, из-за неприятности с большим списком эта разработка не юзабельна :(
На остальное можно еще глаза как-то закрыть в большинстве случаев.
http://alx.vingrad.ru/fwc/ru/doc-intro
Может это кому-то подойдет?
Сам еще не пробывал реализовать.
Евгений 13 апреля, 2008 19:14 #
Отличная разработка, правда примера с длинным списком я не нашел.
Nuxx 26 апреля, 2008 11:17 #
Если вынести код js из вашего примера в отдельный файл и затем подключать его в заголовке страницы, то в IE6 его работа не наблюдается.
Если же вставить полностью весь код в страничку, то все прекрасно работает.
Подскажите пожалуйста, как переделать код, чтобы работало с вынесенного файла js?
Пробовал srAddEvent(window, 'load', srReplaceSelects); подставлять непосредственно в html, но это не принесло результатов.