Этот скрип довольно простой в освоении и успешно работает в разных браузерах на различных устройствах. Частенько применялся мною в "боевых условиях" и всегда стабильно отрабатывал свои функции.
HTML разметка не требует никаких дополнительных оберток. Все необходимое добавляет скрипт.
<form class="select_form"> <div class="form_line"> <select> <option>Пункт 1</option> <option>Пункт 2</option> <option>Пункт 3</option> <option>Пункт 4</option> </select> </div> <div class="form_line"> <select> <option>Пункт 1</option> <option>Пункт 2</option> <option>Пункт 3</option> <option>Пункт 4</option> </select> </div> <div class="form_line"> <input type="checkbox" class="check" name="check_1" /> <label>Чекбокс 1</label> <input type="checkbox" class="check" name="check_2" /> <label>Чекбокс 2</label> </div> <div class="form_line"> <input type="radio" class="radio" name="radio_1" /> <label>Радио кнопка 1</label> <input type="radio" class="radio" name="radio_1" /> <label>Радио кнопка 2</label> </div> </form>
Теперь очередь за jQuery скриптом - он подменяет кастомные элементы на заложенную в нем конструкцию, которой в дальнейшем можно придать какой угодно вид.
$(function(){ initCustomForms(); }); // custom forms init function initCustomForms() { $('select').customSelect(); $('input:radio').customRadio(); $('input:checkbox').customCheckbox(); } // custom forms plugin ;(function(jQuery){ // custom checkboxes module jQuery.fn.customCheckbox = function(_options){ var _options = jQuery.extend({ checkboxStructure: '<div></div>', checkboxDisabled: 'disabled', checkboxDefault: 'checkboxArea', checkboxChecked: 'checkboxAreaChecked', filterClass:'default' }, _options); return this.each(function(){ var checkbox = jQuery(this); if(!checkbox.hasClass('outtaHere') && checkbox.is(':checkbox') && !checkbox.hasClass(_options.filterClass)){ var replaced = jQuery(_options.checkboxStructure); this._replaced = replaced; if(checkbox.is(':disabled')) replaced.addClass(_options.checkboxDisabled); else if(checkbox.is(':checked')) replaced.addClass(_options.checkboxChecked); else replaced.addClass(_options.checkboxDefault); replaced.click(function(){ if(checkbox.is(':checked')) checkbox.removeAttr('checked'); else checkbox.attr('checked', 'checked'); changeCheckbox(checkbox); }); checkbox.click(function(){ changeCheckbox(checkbox); if(!checkbox.is(':checked')) checkbox.removeAttr('checked'); else checkbox.attr('checked', 'checked'); }); checkbox.click(function(){ changeCheckbox(checkbox); }); replaced.insertBefore(checkbox); checkbox.addClass('outtaHere'); } }); function changeCheckbox(_this){ _this.change(); if(_this.is(':checked')) _this.get(0)._replaced.removeClass().addClass(_options.checkboxChecked); else _this.get(0)._replaced.removeClass().addClass(_options.checkboxDefault); } } // custom radios module jQuery.fn.customRadio = function(_options){ var _options = jQuery.extend({ radioStructure: '<div></div>', radioDisabled: 'disabled', radioDefault: 'radioArea', radioChecked: 'radioAreaChecked', filterClass:'default' }, _options); return this.each(function(){ var radio = jQuery(this); if(!radio.hasClass('outtaHere') && radio.is(':radio') && !radio.hasClass(_options.filterClass)){ var replaced = jQuery(_options.radioStructure); this._replaced = replaced; if(radio.is(':disabled')) replaced.addClass(_options.radioDisabled); else if(radio.is(':checked')) replaced.addClass(_options.radioChecked); else replaced.addClass(_options.radioDefault); replaced.click(function(){ if(jQuery(this).hasClass(_options.radioDefault)){ radio.attr('checked', 'checked'); changeRadio(radio.get(0)); } }); radio.click(function(){ if(!jQuery(this).hasClass(_options.radioDefault)){ radio.attr('checked', 'checked'); changeRadio(radio.get(0)); } }); radio.click(function(){ changeRadio(this); }); replaced.insertBefore(radio); radio.addClass('outtaHere'); } }); function changeRadio(_this){ jQuery(_this).change(); jQuery('input:radio[name='+jQuery(_this).attr("name")+']').not(_this).each(function(){ if(this._replaced && !jQuery(this).is(':disabled')) this._replaced.removeClass().addClass(_options.radioDefault); $(this).removeAttr('checked'); }); _this._replaced.removeClass().addClass(_options.radioChecked); } } // custom selects module jQuery.fn.customSelect = function(_options) { var _options = jQuery.extend({ selectStructure: '<div class="selectArea"><span class="left"></span><span class="center"></span><a href="#" class="selectButton"></a><div class="disabled"></div></div>', hideOnMouseOut: false, copyClass: true, selectText: '.center', selectBtn: '.selectButton', selectDisabled: '.disabled', optStructure: '<div class="optionsDivVisible"><div class="select-center"><ul></ul></div></div>', optList: 'ul', filterClass:'default' }, _options); return this.each(function() { var select = jQuery(this); if(!select.hasClass('outtaHere') && !select.hasClass(_options.filterClass)) { if(select.is(':visible')) { var hideOnMouseOut = _options.hideOnMouseOut; var copyClass = _options.copyClass; var replaced = jQuery(_options.selectStructure); var selectText = replaced.find(_options.selectText); var selectBtn = replaced.find(_options.selectBtn); var selectDisabled = replaced.find(_options.selectDisabled).hide(); var optHolder = jQuery(_options.optStructure); var optList = optHolder.find(_options.optList); if(copyClass) optHolder.addClass('drop-'+select.attr('class')); if(select.attr('disabled')) selectDisabled.show(); select.find('option').each(function(){ var selOpt = jQuery(this); var _opt = jQuery('<li><a href="#">' + selOpt.html() + '</a></li>'); if(selOpt.attr('selected')) { selectText.html(selOpt.html()); _opt.addClass('selected'); } _opt.children('a').click(function() { optList.find('li').removeClass('selected'); select.find('option').removeAttr('selected'); jQuery(this).parent().addClass('selected'); selOpt.attr('selected', 'selected'); selectText.html(selOpt.html()); select.change(); optHolder.hide(); return false; }); optList.append(_opt); }); replaced.width(select.outerWidth()); replaced.insertBefore(select); optHolder.css({ width: select.outerWidth(), display: 'none', position: 'absolute' }); jQuery(document.body).append(optHolder); var optTimer; replaced.hover(function() { if(optTimer) clearTimeout(optTimer); }, function() { if(hideOnMouseOut) { optTimer = setTimeout(function() { optHolder.hide(); }, 200); } }); optHolder.hover(function(){ if(optTimer) clearTimeout(optTimer); }, function() { if(hideOnMouseOut) { optTimer = setTimeout(function() { optHolder.hide(); }, 200); } }); selectBtn.click(function() { if(optHolder.is(':visible')) { optHolder.hide(); } else{ if(_activeDrop) _activeDrop.hide(); optHolder.find('ul').css({height:'auto', overflow:'hidden'}); optHolder.css({ top: replaced.offset().top + replaced.outerHeight(), left: replaced.offset().left, display: 'block' }); //if(optHolder.find('ul').height() > 200) optHolder.find('ul').css({height:200, overflow:'auto'}); _activeDrop = optHolder; } return false; }); replaced.addClass(select.attr('class')); select.addClass('outtaHere'); } } }); } // event handler on DOM ready var _activeDrop; jQuery(function(){ jQuery('body').click(hideOptionsClick) jQuery(window).resize(hideOptions) }); function hideOptions() { if(_activeDrop && _activeDrop.length) { _activeDrop.hide(); _activeDrop = null; } } function hideOptionsClick(e) { if(_activeDrop && _activeDrop.length) { var f = false; jQuery(e.target).parents().each(function(){ if(this == _activeDrop) f=true; }); if(!f) { _activeDrop.hide(); _activeDrop = null; } } } })(jQuery);
Теперь с помощью css можно "лепить" все, что только подскажет фантазия. Все кастомные элементы на странице примут тот вид, который требуется по дизайну.
Самое время заняться подготовкой нужных изображений для select, radio и checkbox. Тут уж дизайнер может себя не сдерживать и творить, что ему фантазия навеет. Единственное условие - для radio и checkbox требуются картинки как для обычного, так и для активного состояния.
Для уменьшения запросов к серверу, все фрагменты засунем в спрайт.
С select дело обстоит чуть сложнее. Если ширина у селектов фиксированная, то можно обойтись одной картинкой, но, как правило, на сайте частенько ширина этих элементов отличается и что бы не резать картинку для каждого такого случая, лучше сразу сделать select резиновым по ширине. В этом случае понадобится разрезать изображение на три части. Должно получиться примерно так:
Левую и правую части можем положить в спрайт, центральная часть будет тянуться по оси х на всю ширину select.
Теперь добавим css стили и можем наслаждаться результатом:
.select_form { margin-top:30px; } .form_line { margin:0 auto; padding:0 0 10px 20px; width:380px; } label { padding:0 20px 0 10px; width:110px; display:inline-block; line-height:12px; } /*all*/ .outtaHere { position:absolute; left:-3000px; } span.left, span.center, .selectArea a, .checkboxArea, .checkboxAreaChecked, .radioArea, .radioAreaChecked { display:inline-block; *display:inline; zoom:1; vertical-align:top; } /*select*/ .form_line select { width:265px; } .selectArea { margin:0 25px 0 10px; position:relative; background:url(select_center.png) repeat-x; } a.selectButton, span.left { height:27px; position:absolute; top:0; } span.left { width:10px; left:-10px; background:url(select_sprite.png) no-repeat 0 -27px; } span.center { line-height:27px; } a.selectButton { width:25px; right:-25px; background:url(select_sprite.png) no-repeat 0 0; } /*option*/ .optionsDivVisible { margin-left:-5px; } .select-center { padding:5px 5px; background:#fff; border:1px solid #ccc; border-top:0; } .select-center a { padding-left:10px; line-height:18px; color:#666; text-decoration:none; } .select-center a:hover { color:#333; } .radioArea, .radioAreaChecked, .checkboxArea, .checkboxAreaChecked { margin-top:4px; width:15px; } /*checkbox*/ .checkboxArea, .checkboxAreaChecked { height:14px; } .checkboxArea { background:url(radio_sprite.png) no-repeat 0 -22px; } .checkboxAreaChecked { background:url(radio_sprite.png) no-repeat 0 -36px; } /*radio*/ .radioArea, .radioAreaChecked { height:11px; } .radioArea { background:url(radio_sprite.png) no-repeat 0 0px; } .radioAreaChecked { background:url(radio_sprite.png) no-repeat 0 -11px; }
Скрипт оттестирован и успешно работает во всех браузерах настольных машин и мобильных устройств (в ИЕ6 не тестировался)