Оформление элемента 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.

Отличная идея и реализация!
Однако есть небольшой минус - если раскрыть обычный выпадающий список, то не обязательно выбирать значение - можно кликнуть в пустое место на странице и тогда он автоматически уберется с экрана (закроется). Однако в этой реализации список убирается только если выбрать одно из значений. Понимаю, что не всем нужна такая функциональность, но мало ли ;)
P.S. проверял только в Maxthon (IE6).
Доступность и удобство таких элементов — это главный их баг, недостаток и аргумент против использования.
Давайте опишем плюсы: красиво выглядит.
Давайте опишем минусы:
— Пытаюсь попасть на этот элемент клавишей таб: никак, в том числе и открыть.
— Открываю этот «селект» руками: курсор почему-то pointer. М?
— Пытаюсь работать: клавиши вверх/вниз и пробел/ввод не воспринимает, подбор по первой букве слова не работает.
— Пытаюсь закрыть выпадающий список без выбора чего-либо (в случае, если это поле не является обязательным для заполнения) — ни фокус на друом элементе формы, ни клик по окружающим элементам не приводит к закрытию этого существа.
И что же мы имеем? Абсолютно бесполезную вещь.
За статью как всегда спасибо ;) Но на вещи всё-таки стоит смотреть более трезво. А то вернёмся к DHTML'ю муравьями, которые бегают за курсором…
Tab работает, элемент при этом подсвечивается, и можно листать список стрелками, как в обычном select.
Pointer убрал.
Подбор по первой букве сделать можно, но скрипт станет сложнее, а ведь такой функционал нужен только если опций много.
Закрытие по клику на другие элементы, действительно упустил, сделаю.
Сорри, я смотрел в Safari, там от всего этого великолепия мало что осталось и кое-что неожиданное появляется. В Firefox при выборе пункта после передвижения по Tab'у одна за другой вылезают ошибки «li has no properties».
Я высказался в предыдущем комменте не потому, что считаю, что у этого скрипта чего-то не хватает, а потому, что в принципе являюсь противником замены системных/браузерных контролов чем-либо. Правда, за небольшим исключением: если у вас получится сделать контрол который нельзя будет отличить по поведению от системного, то это можно рассматривать как рабочий вариант. Пока, к сожалению, нет даже половины стабильных признаков системного селекта.
Вот сейчас и отладим (а скорее всего завтра), спасибо за ценные замечания и конструктивную критику.
Насчет ненужности подбора по первым буквам - это вы зря. Я редко раскрываю селекты с названием страны. Проще нажать 3 раза u (USA -> Uganda -> Ukraine), чтобы выбралась нужная мне страна. Я вообще не знаю, сколько там стран с этом списке! Если элементов больше чем 2 (Yes/No, True/False -- и вообще для этого лучше радиобаттоны) или 3 (Male/Female/Undefined),-- то обязательно нужна работа с клавиатуры.
Не работает Alt-ArrowDown, у меня вообще не получается его раскрыть с клавиатуры :-( Про ошибку в JavaScript "li has no properties" уже сказали.
ЗЫ. Я не имею ничего против замены стандартных контролов нестандартными, если только это работает ожидаемо.
Эх... статья хорошая, но в этот раз, по-моему, в ней дан хороший пример того, как не надо делать.
Прокомментирую, почему (хотя частично это уже было сказано, скорее, добавлю):
— нехарактерное поведение для этого элемента управления (даже если научить его скрываться при потере фокуса): возможность перемещаться по элементам по мере ввода с клавиатуры, может, и не нужна для коротких списков, но пользователь может попытаться пользоваться ей просто по привычке. И будет неприятно удивлен. Думаю, добиться полного соответствия в поведении стандартного элемента и эмулированного на JS будет очень нелегко, а пока этого не произойдет, пользователей будут ждать разные сюрпризы, преимущественно неприятные.
— но есть и более серьезный недостаток, который неизбежно вытекает из того преимущества, ради которого все и было затеяно (сам плохо понял, что написал). Это — настраиваемость внешнего вида. То есть вместо привычного для себя select'а пользователь увидит что? Да что угодно, только не нормальный select! И нет никакой гарантии, что он сразу поймет, что это перед ним, и что надо с этим делать. Если бы я не знал, о чем читаю, то полагаю, что увидев такой контрол, растерялся бы на несколько секунд как минимум. Пользователь привык видеть контролы такими, какими он привык их видеть, -) и не надо ломать его привычек. В этом вопросе я бы отдал предпочтение принципу "пусть безобразно, зато однообразно". Хотя все это — сугубо мое личное мнение.
Впрочем статья от этого не теряет ценности в качестве учебного материала. Спасибо за нее -)
Да, еще если уйти по TAB, список тоже не сворачивается. Про клик на свободном месте уже говорили... Но больше всего смущает то, что пропадает стрелка, и вместо нее рисуется список длинный. Как-то не очень ожидаемо - нажал кнопку "выпасть вниз" (именно так я читаю стрелку), а оно мне черт знает что сделало.
Кстати о длине, что будет, если элементов 50-100? скроллбар? как оно будет себя вести? Какие элементы покажутся при раскрытии списка? Как будут работать стрелки вверх/вниз, page up/page down, home/end? Обычно все скрипты страдают этой проблемой...
Ошибку "li has no properties" исправил.
Понял, что возможность перемещения по буквам нужна обязательно, добавлю.
Хм, я посмотрел, стандартные селекты листаются стрелками не открываясь.
Ух, завалили комментами :-) Ну тем лучше, даешь скрипты народу, идеи дизайнерам и пятилетку за три года!
А если alt зажать? :-)
Alt+стрелка вниз
я и не знал, что их с альтом можно :)
как выловить такую комбинацию подскажете?
Есть тут одна мысль - стандартный select оставить.
Разве что из спортивного интереса этот скрипт можно еще помучить -)
event.shiftKey
event.altKey
event.ctrlKey
Спасибо.
Есть одна идея, но требует полной реорганизации этого скрипта займусь завтра.
Хм, выбор по буквам работает (унаследован от select) просто не во всех браузерах.
В FF и Opere выбор по буквам не работает даже в обычном селекте, или я что-то не так смотрю, в IE все без проблем выбирается по буквам.
О! ты наткнулся на баг ФФ и оперы :-) Там не работает поиск по первым русским буквам. Всегда бесило
бесполезно.
Сомнительная полезность напрасного украшательства.
Совершенно бесполезная фишка, на мой взгляд, осознание этого скорее всего придет не сразу, но обязательно придет.
Подумайте как это будет работать на коммуникаторах? С отключенной графикой?
не везде. В сафари открываются.
Хороший скрипт, есть цель для развития.
Мне нравится такое решение
http://c82.net/samples/checklist-samples.html
Хотя оно громоздкое и поиск по буквам не работает, но красивое.
Для НЕмультивыбора, checkbox заменить на radio.
Не нравится нестандартный селект? Давайте тогда заменим меню на сайтах как [File] [Edit] [View]. Вы пользователей совсем уж тупыми считаете.
Я конечно понимаю, что консервативность нынче в моде, но не до такой же степени? Опять же дизайн и цветовая гамма сайта могут уж слишком контрастировать со стандартными контролами.
Едея класс! Реализация тоже. Но с точки зрения юзабилити есть минус: если человек передумал делать выбор в нашем Select'е, он никак не может скрыть его не нажав на элемент самого Select'а (в котором он передумал что-то нажимать). Во всех операционках при нажатии на пустую область экрана элементы Select'а прячутся, а тут нет. Для пользователя это не удобно, хоть и выглядит красиво.
Исправил проблему с закрытием по клику на свободной области окна или по уходу через tab, потестите кому не лень.
С раскрытием по alt+вниз проблемы, причем не столько с самим раскрытием сколько с тем что в опере раскрывается и оригинальный select, к тому же, при раскрытом табе onchange только после выбора пункта происходит.
Видимо оринальный select придется скрыть совсем, а для получание фокуса использовать спрятанное текстовое поле, минус решения в том, что придется по буквам выбирать самостоятельно, но как плюс будут работать русские буквы в Opera и FF.
Что касается нестандартности оформления, то помоему не проблема нарисовать select по которому будет однозначно видно, что это select.
Повторять поведение стандартного элемента, тоже не проблема, а вот отключенная графика это да, стрелку ведь без графики ни как не нарисуешь. Хотя можно попробовать изобразить ее несколькими блоками, но возникает вопрос, как определить, что графика не загружена? (есть, конечно, идеи, но хочется сначала узнать, что нибудь новое).
Поскольку скрипт постоянно изменяется убрал второй пример.
Ни в FF, ни в Safari не получается выбрать другой пункт из выпавшего списка.
Исправил.
Для соблюдения taborder`а при раскрытии списка передаваля фокус соответствующему select, а при нажатии tab список должен был сворачиваться по blur. При клике мышкой получалось, что select терял фокус и список сворачивался по blur, события click вообще не происходило (почему? ведь должно быть хотя бы в пустое место). Вместо blur теперь обрабатываю клавишу tab, заработало.
Оффтоп: пошел качать Safari немного удивило то? что по умолчанию галочка стоит на версии для Windows, как-то не патриотично для Apple :)
Это нормально. Лучше недооценить пользователя и сделать интерфейс проще, чем чуть-чуть переоценить и получать жалобы.
Где-то даже попадались обобщенные правила, как создать юзабильный интерфейс. Там (утрированно) говорилось, что лучше предполагать, что пользователь читать не умеет. Потому что он никогда не читает хелп и текст в диалоговых окнах, и ведь это так!
Пусть лучше контрастируют, чем причиняют неудобства.
Вы, судя по всему (без обид) судите о юзабилити только по личным наблюдениям. Ознакомьтесь с литературой по этой теме, часто это "открывает глаза".
Думаю, у них просто определяется ОС посетителя -) Это, как раз, пример внимательного отношения к юзабилити.
У меня в сафари все работает, тока стрелками нельзя по списку передвигаться! Спасибо за интересную статью!
Как веб-дизайнер, и как разработчик, я считаю эмуляцию селекта неэтичным.
Большинство из нас тут пропагандируют стандарты. И тут замена стандартных элементов. Чем вас не устраивает работа селекта? Видом? Оставьте. Графическое исполнение селекта - это отголосок той эпохи, когда дизайнеры ворочаясь во сне бормотали себе под нос: "эффекты, эффекты", эпохи фотографических background и надписей с тенями через drop shadow. Проще, правильнее и профессиональнее составить композицию элементов сайта и подобрать цветовую гамму так, что бы сдандартный селект смотрелся как отрисованная картинка. Зайдите в рейтинг Alexa, просмотрите топ российских сайтов, как видите я не голословен.
Но если вернуться к спортивному интересу, то скрипт достоин похвалы. Оригинальную версию скрипта изучал еще давно, ваши дополнения его значительно улучшили. Не тестировали его на скорость загрузки, скажем сайта с тремя селектами?
В скрипте нет ничего столь затратного, чтобы можно было заметить замедление на трех селектах поэтому я потестил с 50 селектами, никаких заметных тормозов нет, при том что обработчик стоит на событии load, а можно поставить на DOMContentLoaded (на реальных сайтах так и нужно) тогда замена будет происходить еще раньше.
Работа очень полезная! Только я бы применял ее не украшения ради, а чтобы развить возможности стандартного селекта. Из очень полезного развития можно отметить Ajax и добавление в качестве пуктов в списке других контроллов (как в Гугле - выпадает список, а там чекбокс), дополнительное оформление пунктов.
не плохо и интересно...
Еще один недостаток: посмотрел пример без графики и вообще не увидел select-ов. С первого взгляда это обычные текст.
Как дизайнер, скажу, что решение просто супер! Бывают случаи когда действительно необходимо использовать нетипичные списки, несмотря на "негласное табу". Вот только непонятно, как сделать так, чтобы на странице часть списков была дефолтной, а часть вот такой красивой?
Сделать так достаточно просто, в функцию srReplaceSelects добавим проверку класса элемента перед его заменой, и будем заменять только элементы с классом replaceThisSelect
пример
Я конечно извиняюсь, но, когда я открыл селект то из под низу просвечивался другой обычный селект. Разве в ИЕ вы такого не видели?
Странно ничего подобно не видел. Cейчас посмотрел еще раз, в IE обычный селект нигде не просвечивается. Какую версию IE вы используете?
А я просто пользуюсь готовыми JavaScript компонентами NWS... В частности выпадающим списком NWSselect. По-моему очень удобно, вот можете сами глянуть http://neversleep.ru/?NWSselect
Спасибо, Евгений! Интересный подход.
Чуть не забыл.. Ещё бы добавить скрытие списка при нажатии Escape
Я тоже с в основном пользуюсь готовыми компонентами, но иногда хочется придумать, что-то свое, а иногда готовым компонентам не хватает чего нибудь, что нужно в конкретном случае.
Спасибо за ценное замечание, доработаю.
При использовании скажем 2-ух селектов, как в примере выше, где 1 селект заменяемый, а другой нет, вылезает проблемма (IE) наложения diva (я раньше с этим сталкивался только при использовании divov) на select, когда нижний select просвечивается сквозь div.
Да дивы вылезают всегда поверх всего на странице в ИЕ. Нормального способа обойти это еще не найдено. Ошибка не исправлена. Поэтому это аргумент в сторону использования не настоящий селектов, а их эмуляции. Вот например NWSselect о котором я уже говорил
NWSselect - вы сами не работаете в NWS, уважаемый=)? В интернете про него только на этом сайте можно найти, да на самом NWS. Что, конечно, его достоинств не умаляет. Но, к сожалению, ребята по данной вами ссылки и на вопросы не отвечают, и продавать не хотят.
Так что пришлось дорабатывать выложенный тут пример под свои нужды, как умеем.
Может быть, я не просто не нашел версии тут нужной, но, Евгений, сворачивание без выбора так и не было вами доделано?
Главный баг, в общем. ради которого я пишу сюда - в IE6 при прокрутке мышью страницы с открытым селектом, почему-то не страница прокручивается, а выбранный предыдущий элемент! Т.е., если список будет большим, то страницу просто напросто не прокрутить..=(
Хотя, похоже, это наследие всех доделанных прелестей, таких как управление стрелками..
Главный баг, как вы его обозвали, это поведение элемента select по дефолту, если на нем фокус то колесиком мышки можно перелистывать пункты.
Именно спрятанные дефолтный селект создает некоторые проблемы, в ближайшее время я постараюсь вернуться к этой тематике и сделать более функциональный элемент. Основная мысль такая, избавляемся от select совмем, фокус и события ловим через input text, а отсылаем значение через input hidden (через text будет не очень удобно), при этом можно организовать удобный поиск по списку и при желании что-то вроде suggestions.
Да, заметил, что если даже поставить display:none у исходного селекта, сразу ругается как раз про фокус..
Для сворачивания, если мышь уходит, лично я доделал так, хотя, наверное, коряво это весьма:
srAddEvent(window, 'mousemove', srOnDocumentClick); //для FF
document.body.attachEvent("onmousemove",srOnDocumentClick); //для IE и остального..почему-то с srAddEvent такой же код добавления события не сработал...
а у самого списка и таблицы обрамляющей добавлено onmousemove="event.cancelBubble = true"
Будем ждать ваших профессиональных решений =)
Разработка конечно-же интересная, с точки зрения примера возможностей JS. Афтару респект.
Но на практике, ради дропдауна увеличивать страницу на порядка 10-ти килобайт кода, помоему это многовато, для обычного дропдауна.
Опуская вышесказанное, нужна ещё функциональность, а именно:
1. Ширина. В обычном дропдауне, она подбирается автоматически, в зависимости от максимальной ширины строки. Зачастую заранее не знаешь, какой ширины будет используемая строка. А в этом, она прибита гвоздями, причём для нескольких дропдаунов на странице, она всегда будет одинакова, что не простительно.
Нужно иметь выбор, либо прибить ширину гвоздями, либо она подбирается автоматически.
2. Стили. Какой ни какой стандартный дропдаун, но всётаки, коекакие стили на него можно положить. Этот-же имеет всегда один стиль. Нужна возможность задания стиля для каждого дропдауна на странице.
3. Неприятность с большим выбором. Что мы имеем в обычном дропдауне, когда его список очень велик, и не умещается на одной странице? Правильно. Появляется роллер прокрутки всего списка. Что мы видим тут? Ого. Растягивание всей страницы на всю длинну дропдауна. Это просто непростительно, ни с какой точки зрения, даже если этот дропдаун идеально заточен под общую стилизацию страницы.
4. Первая строка. Было-бы не плохо, таки оставлять первую строку выбора (та что со стрелкой), и уже под ней разворачивать весь список. Это будет более наглядно, и красноречиво говорить о том, что это именно дропдаун, а не что-то другое.
да, из-за неприятности с большим списком эта разработка не юзабельна :(
На остальное можно еще глаза как-то закрыть в большинстве случаев.
http://alx.vingrad.ru/fwc/ru/doc-intro
Может это кому-то подойдет?
Сам еще не пробывал реализовать.
Отличная разработка, правда примера с длинным списком я не нашел.
Если вынести код js из вашего примера в отдельный файл и затем подключать его в заголовке страницы, то в IE6 его работа не наблюдается.
Если же вставить полностью весь код в страничку, то все прекрасно работает.
Подскажите пожалуйста, как переделать код, чтобы работало с вынесенного файла js?
Пробовал srAddEvent(window, 'load', srReplaceSelects); подставлять непосредственно в html, но это не принесло результатов.
Идея неплоха, но реализовывать ее везде и всюду без КРАЙНЕЙ НЕОБХОДИМОСТИ не рекомендую.
Единственный случай, с которым я сталкивался и когда реализация такого решения была оправданна - написание WYSIWYG на javascript. Там надо было выбирать стили применяемые к текту и в выпадающем списке надо было каждый вариант написать своим стилем. А стандартный селект такого не дает :(
Очень не советую применять такое в стандартных формах. У меня например для всех форм стоит автозаполнение и такие вещи как страна например и т.п. всегда предустановлены. Попробуйте реализовать это в нестандартном селекте.
З.Ы. Дизайн должен точиться под функционал, а не функционал под диз. Если стандартный селект сильно контрастирует с дизом, измените дизайн.
А как на счет javascript событий? oncange как минимум.
Или еще лучше, когда события обрабатываются с помощью JQ, YUI, Prototype или чем-нибудь-еще.
статья и творение автора порадовали, интересно было ознакомится ;)
НО!
на мой взгляд у настоящего селекта, помимо всех названых и неназванных тут преимуществ есть ещё одно, делающее его иногда незаменимым при создании интерфейсов и меню - это его способность быть и открыватся поверх чего угодно, даже поверх фреймов на странице.
кстати, менять его всёже можно неплохо, цвет фона, стиль и размер шрифта, габариты, цвет бордюра вплоть до полного отсутствия оного, убирание кнопки справа... к тому же весьма некислая система возможностей управления и контролирования состояний ( не хватает к сожалению лишь событий по сворачиванию и генерации события onchange ещё и в случае выбора элемента который и так был в фокусе.)
Все супер, замечательно!
НО! Есть такое событие onChange. И как его тут использовать. У меня на сайте везде юзается obChange. т.е. при выборе сразу активируется форма. Тут не работает вообще ((( HELP!
таже проблема... а было обрадовался!
Евгений есть решение можеть быть?
---
iNos 30 сентября, 2008 15:57 #
Все супер, замечательно!
НО! Есть такое событие onChange. И как его тут использовать. У меня на сайте везде юзается obChange. т.е. при выборе сразу активируется форма. Тут не работает вообще ((( HELP!
нашел вариант на хабрхабре и походу дела решит мою проблему.
Евгений посмотрите тоже -))
http://habrahabr.ru/blogs/mootools/40468/
Посмотрел, нужно будет сделать подобное для jQuery (или найти).
onChange вообще просто добавить, нужно
sel.onchange = function() { /* --- */ }заменить на
srAddEvent(sel, 'change', function() { /* --- */ });после можно крепить любой onchange к исходному select
(решение на первый взгляд, не тестил)
Но я вижу еще несколько проблем с этим скриптом, и сейчас сделал бы его иначе, поэтому пока не соберусь полностью переделать под jQuery.
Для начала, спасибо за статью, Евгений!
Основная причина нашей возни с селектами - использование системного элемента в эксплорере, что приводит к его просвечиванию.
Вопрос решили с использованием подсадного input type='text', можете развить идею, если хотите. :)
Евгений
в поиске на jquery я нашел вот что:
http://www.adelaidewebdesigns.com/2008/08/01/adelaide-web-designs-releases-customselect-with-icons/
оттуда в комментах можно найти ссылку на модификацию
http://www.ildavid.com/dblog/selectcustomizer/
а тут же и на следующую модификацию
http://dblog.com.au/general/jquery-plugin-custom-styled-select-input-wkeyboard-interaction/comment-page-1/#comment-1224
единственное что меня тормозит все это использовать - нужно событие onchange и при option selected чтобы пункт был активен в селекте (надеюсь правильно обьяснил)
если сможете добавить в последнюю модификацию эти дела было бы здорово....
на http://ajaxblog.ru/2008/07/240-udivitelnykh-plaginov-dlya-jquery/
есть что-то..
jLook Nice Forms (http://plugins.jquery.com/project/jLook - но файлы пропад и куда-то) который вроде хвалят больше jNice
что еще нашел:
http://clproject.info/archives/253
...
Продолжу небольшой список моих находок - не успел проверь все
каждый для наверняка найдет что-то новое в реализациях
Linkselect jQuery Plug-in
Яhttp://www.givainc.com/labs/linkselect_jquery_plugin.htm
jquery.combobox
http://jquery.sanchezsalvador.com/page/jquerycombobox.aspx
Select Box Factory 2.0
http://www.headcircus.com/uiguy/selectboxfactory/selectboxfactory.html
За идею спасибо.
Баг №1 при использовании js откаазываются работать события js прописанные в select
Баг №2 не обрабатывает select вызванный на страницу с использованием innerHTML
Баг №3 в мозиле(3.5.5) при первой загрузке стиль не применяется, теряя при этом и стандартный стиль
А как сделать что бы на сайте не все селекты заменялись, а один конкретный, пробовал по id найти никак не получается