Onatolich Interface Solutions

601
.
Данная тема является более-менее логическим продолжением исчерпавшей себя jQuery--большой функционал за короткое в...
И посвящается она моим личным Frontend решениям более высокого уровня реализации. Надеюсь изложенная здесь информация будет полезной для вас.

Первое, о чем я хотел бы здесь написать: стилизация радиокнопок и чекбоксов на чистом CSS, без JS, дополнительной разметки и прочих извращений, в чем и состоит уникальность данного метода(по крайней мере пока в сети такого не встречал).

CSS код прост как 3 копейки:

Радио кнопки:
input[type=radio]{ position: relative; }
input[type=radio]:after{
  background: url('images/radios.png') no-repeat 0 0;
  content: '';
  position: absolute;
  top: -1px; right: -2px;
  height: 17px; width: 17px;
}
input[type=radio]:hover:after{ background-position: 0 -35px; }
input[type=radio]:checked:after{ background-position: 0 -17px; }


Чекбоксы аналогично:
input[type=checkbox]{ position: relative; }
input[type=checkbox]:after{
  background: url('images/checkboxes.png') no-repeat 0 0;
  content: '';
  position: absolute;
  top: -1px; right: -2px;
  height: 17px; width: 17px;
}
input[type=checkbox]:hover:after{ background-position: 0 -34px; }
input[type=checkbox]:checked:after{ background-position: 0 -17px; }


Спрайт под данный код прилагается.
Прикрепленные файлы:
.
Спрайт для чекбоксов
Прикрепленные файлы:
.
Onatolich
И собственно скриншот.

Обрамляющий Groupbox реализован так:

СSS:
.group{
  background: #fff;
  border: 1px solid #d6d8ff;
  color: #6890ba;
  margin: 15px 0;
  padding: 10px 15px;
  position: relative;
}

.group .caption{
  background: #fff;
  color: #6890ba;
  font-size: 10pt;
  padding: 0 5px;
  position: absolute;
  top: -10px; left: 20px;
}


HTML:
<div class="group">
    <div class="caption">Заголовок</div>
    Контент
</div>


Прикрепленные файлы:
.
Теперь более интересная фишка:

Стилизация блоков Select. Решил я данную проблему в виде jQuery плагина, код которого выглядит так:

/*-------------------------------------------------------------------*/
/* SelectBar */
/*-------------------------------------------------------------------*/

(function($){
  jQuery.fn.selectbar = function(){

    var e, oisSbTimer, oisElement;

/*-------------------------------------------------------------------*/

    var handler = function(){
        e = $(this);
        if(e.attr('multiple'))
        {
            e.multiselect();
            return true;
        }

        i = 1;
        e.hide().find('option').each(function(){ $(this).attr('oid', i); ++i; });

        var selected = e.find('option:selected').attr('selected', 'selected').html();
        var name = e.attr('name');

        $('<section></section>').html('<span class="selected">' + selected + '</span>').attr('id', 'opb-' + name).addClass('ois-selectbar').insertAfter(e);
        oisElement = $('#opb-' + name);

        $('<section></section>').addClass('ois-variants').appendTo(oisElement);

        e.find('option').each(function(){
            $('<a></a>').html($(this).html()).attr('oid', $(this).attr('oid')).appendTo(oisElement.find('section.ois-variants'));
            if($(this).attr('selected'))
                oisElement.find('a[oid=' + $(this).attr('oid') + ']').addClass('selected');
        });

        e.live('change', function(){ setSelected($(this).find('option:selected').attr('oid')); });

        oisElement.width(oisElement.find('section.ois-variants').width() - 20);
        oisElement.find('section.ois-variants').css('width', '100%');

        oisElement.bind('click', function(){ toggleVars(); return false; });
        $('#opb-' + name + ' section.ois-variants a').bind('click', function(){ setSelected($(this).attr('oid')); return false; });
        $('body').bind('click', function(){ toggleVars(true); });

        oisElement.bind({
            mouseleave: function(){
                if($(this).find('section.ois-variants').hasClass('showed'))
                    oisSbTimer = setTimeout(function(){ toggleVars(true); }, 3000);
            },
            mouseover: function(){ clearTimeout(oisSbTimer); }
        });
    }

/*-------------------------------------------------------------------*/

    var toggleVars = function(f){
        if(!f)  oisElement.toggleClass('ois-opened').find('section.ois-variants').toggleClass('showed').fadeToggle(150);
        else    oisElement.removeClass('ois-opened').find('section.ois-variants').removeClass('showed').fadeOut(150);
    }

/*-------------------------------------------------------------------*/

    var setSelected = function(oid){
        var text = oisElement.find('section.ois-variants a').removeClass('selected').parent().find('a[oid=' + oid + ']').addClass('selected').text();

        oisElement.find('span.selected').html(text);
        !oisElement.find('section.ois-variants').hasClass('showed') || toggleVars();
        e.find('option').removeAttr('selected').parent().find('option[oid=' + oid + ']').attr('selected', 'selected');
    }

/*-------------------------------------------------------------------*/

    return this.each(handler);
  }
})(jQuery);


Мой CSS выглядит таким образом:

/* Контейнер */
.ois-selectbar{
  background: #fff;
  border: 1px solid #d6dfe8;
  color: #8195c1;
  cursor: pointer;
  font-family: Helvetica;
  font-size: 8pt;
  font-weight: bold;
  display: inline-block;
  margin-left: 2px;
  padding: 5px 7px 4px 7px;
  position: relative;
  min-width: 70px;
  width: 100%;
  z-index: 2;
}

/* Стрелочка справа */
.ois-selectbar:after{
  background: url('images/b_arrow.png') no-repeat center center;
  content: '';
  position: absolute;
  top: 0; right: 0;
  width: 20px; height: 23px;
}

/* Изменение вида стрелочки при наведении на блок */
.ois-selectbar:hover:after, .oi-selectbar.oi-opened:after{ background-color: #f3f5ff; border-left: 1px solid #d5d8ff; }

/* Блок с вариантами выбора */
.ois-variants{
  background: #fff;
  border: 1px solid #d6dfe8;
  display: none;
  font-weight: normal;
  max-height: 200px;
  overflow-y: auto; overflow-x: hidden;
  position: absolute;
  top: 110%; left: -1px;
}

/* Каждый отдльный вариант */
.ois-variants a{
  display: block;
  padding: 3px 10px;
  text-decoration: none;
}

/* Каждый отдельный вариант при наведенном курсоре */
.ois-variants a:hover{ background: #eef; }

/* Выбранный вариант */
.ois-variants a.selected{
  background: #637bad;
  color: #fff;
  font-weight: bold;
  margin: 0 -1px;
}


Теперь чтобы стилизировать все Selectbar'ы просто пропишите следующий JS код:
$('select').selectbar();


Стоит отметить, что при отключенном JS вы увидите обычный Select, то есть данный плагин я писал так, чтобы он не создавал новый Select, а создавал отдельный стилизированый селектбар, связанный с существующим.

Результат на скриншоте.
Прикрепленные файлы:
.
И последнее на сегодня: стилизация Select с мультивыбором.

jQuery плагин:
/*-------------------------------------------------------------------*/
/* Multiple selectbar */
/*-------------------------------------------------------------------*/

(function($){
  jQuery.fn.multiselect = function(){

    var e;

/*-------------------------------------------------------------------*/

    var handler = function(){
        e = $(this);
        var eName = e.attr('name').substring(0, (e.attr('name').length - 2));

        var i = 1;
        e.find('option').each(function(){ $(this).attr('oid', i); ++i; });
        e.attr('eid', 'oms-' + eName).hide();

        $('<section></section>').addClass('ois-multiselect').attr('sid', 'oms-' + eName).insertAfter(e);
        e.find('option').each(function(){
            $('<section></section>').addClass('ois-item').text($(this).text())
            .attr('oidms', $(this).attr('oid'))
            .appendTo($('section[sid=oms-' + eName + ']'));

            ($(this).attr('selected') != 'selected') || $('section[sid=oms-' + eName + '] section.ois-item[oidms=' + $(this).attr('oid') + ']').addClass('checked');
        });

        $('section[sid=oms-' + eName + ']').find('section.ois-item').bind('click', function(){
            $(this).toggleClass('checked');

            e.find('option[oid=' + $(this).attr('oidms') + ']').removeAttr('selected');
            if($(this).hasClass('checked'))
                e.find('option[oid=' + $(this).attr('oidms') + ']').attr('selected', 'selected');
        });
    }

/*-------------------------------------------------------------------*/

    return this.each(handler);
  }
})(jQuery);


CSS:
/* Контейнер */
.ois-multiselect{
  background: #fff;
  border: 1px solid #d6dfe8;
  color: #6890ba;
  display: inline-block;
  min-height: 160px; max-height: 205px;
  width: 190px;
  overflow: auto;
}

/* Отдельный вариант выбора */
.ois-multiselect .ois-item{
  border-bottom: 1px solid #fff;
  border-top: 1px solid #fff;
  cursor: pointer;
  font-family: Helvetica;
  font-size: 9pt;
  font-weight: bold;
  margin-top: -1px;
  padding: 3px 15px;
}
.ois-multiselect .ois-item:first-child{ margin-top: 2px; }
.ois-multiselect .ois-item:last-child{ margin-bottom: 2px; }

/* Вариант выбора с наведенным курсором */
.ois-multiselect .ois-item:hover{ background: #f0f0ff; }

/* Выбранный вариант */
.ois-multiselect .ois-item.checked{
  background: #eaeaff;
  border-color: #fff;
}


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

var OIS = {
  home: 'http://' + document.domain,
  path: location.href,
  nextID: 1
}

/*-------------------------------------------------------------------*/

OIS.setID = (function(e){
  if(!e.attr('oisId'))
  {
    e.attr('oisid', OIS.nextID);
    ++OIS.nextID;
  }
});

/*-------------------------------------------------------------------*/

OIS.getID = (function(e){
  if(!e.attr('oisid'))
    OI.setID(e);
  return e.attr('oisid');
});

/*-------------------------------------------------------------------*/

OIS.getXHR = (function(){
  var xhr;
  try{
    xhr = new ActiveXObject("Msxml2.XMLHTTP");
  } catch(e) {
    try{
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
    } catch(e) {
        xhr = false;
    }
  }
  if(!xhr && typeof XMLHttpRequest != undefined)
    xhr = new XMLHttpRequest();

  return xhr;
});

/*-------------------------------------------------------------------*/

OIS.addPrefix = (function(path, prefix){
  if(!prefix) prefix = 'client';
  return OIS.home + '/' + prefix + path.substring(OIS.home.length, path.length);
});
.
Пожалуй порадую вас еще одной плюшкой. Tooltips(Подсказки).

С начала о реализации:

jQuery:
/*-------------------------------------------------------------------*/
/* Tooltip */
/*-------------------------------------------------------------------*/

(function($){
  jQuery.fn.tooltip = function(options){

    var e, tTimer;

/*-------------------------------------------------------------------*/

    var handler = function(){
        e = $(this);
        initialize(e);

        e.unbind();
        for(event in options.eShow) { e.bind(options.eShow[event], function(){ show(e); }); }
        for(event in options.eHide) { e.bind(options.eHide[event], function(){ hide(e); }); }
    }

/*-------------------------------------------------------------------*/

    var initialize = function(e){
        OIS.setID(e);

        nOptions = { attr: e.data('attr'), removeAttr: e.data('removeAttr'), eShow: e.data('eShow'), eHide: e.data('eHide'), delay: e.data('delay'), hAlign: e.data('hAlign'), vAlign: e.data('vAlign'), correction: e.data('correction'), width: e.data('width') }

        options = $.extend({
            attr: 'title',
            removeAttr: true,
            eShow: 'mouseover',
            eHide: 'mouseleave',
            delay: 100,
            hAlign: 'center',
            vAlign: 'top',
            correction: [0, 0],
            width: '200px'
        }, nOptions, options);
        setAttr(e);

        if(!(options.eShow instanceof Array)) options.eShow = [options.eShow];
        if(!(options.eHide instanceof Array)) options.eHide = [options.eHide];

        e.data('initialized', true).data('attr', options.attr).data('removeAttr', options.removeAttr).data('eShow', options.eShow).data('eHide', options.eHide).data('delay', options.delay).data('vAlign', options.vAlign).data('hAlign', options.vAlign).data('correction', options.correction).data('width', options.width);
    }

/*-------------------------------------------------------------------*/
    var setAttr = function(e){
        if(!e.attr(options.attr))
            return true;

        e.attr('oistooltip', e.attr(options.attr));
        !options.removeAttr || e.removeAttr(options.attr);
    }
/*-------------------------------------------------------------------*/

    var show = function(e){
        if($('section.ois-tooltip[eid=' + OIS.getID(e) + ']').length)
            return true;
        var tElement = $('<section></section>').addClass('ois-tooltip').attr('eid', OIS.getID(e)).html(e.attr('oistooltip')).appendTo($('body'));
        $('<section></section>').addClass('ois-arrow-top').appendTo(tElement);
        $('<section></section>').addClass('ois-arrow-bottom').appendTo(tElement);

        setPosition(e);
        tTimer = setTimeout(function(){
            tElement.fadeIn(200);
        }, options.delay);

        tElement.bind({
            mouseover: function(){ $(this).animate({opacity: 0}, 100); },
            mouseleave: function(){ $(this).animate({opacity: 1}, 100); }
        });
    }

/*-------------------------------------------------------------------*/
    var hide = function(e){
        clearTimeout(tTimer);

        var tElement = $('section.ois-tooltip[eid=' + OIS.getID(e) + ']');
        if(!tElement.length)
            return true;
        tElement.fadeOut(200, function(){ tElement.remove(); });
    }
/*-------------------------------------------------------------------*/

    var setPosition = function(e){
        var tElement = $('section.ois-tooltip[eid=' + OIS.getID(e) + ']').css('max-width', options.width);

        vAlign = options.vAlign
        if(vAlign == 'bottom')
        {
            if(e.offset().top + e.height() + tElement.height() + 30 > $(window).scrollTop() + $(window).height())
                vAlign = 'top';
        }
        else if(e.offset().top - $(window).scrollTop() < tElement.height())
            vAlign = 'bottom';

        if(vAlign == 'top')
        {
            tElement.css('top', (e.offset().top - tElement.height() - parseInt(e.css('padding-top')) + parseInt(options.correction[0]) - 15) + 'px');
            tArrow = tElement.find('section.ois-arrow-bottom');
        }
        else
        {
            tElement.css('top', (e.offset().top + e.height() + parseInt(e.css('padding-bottom')) - parseInt(options.correction[0]) + 15) + 'px');
            tArrow = tElement.find('section.ois-arrow-top');
        }

        if(options.hAlign == 'center')
        {
            tElement.css('left', (e.offset().left + (e.width() / 2) - (tElement.width() / 2) + parseInt(options.correction[1])) + 'px');
            tArrow.css('left', (tElement.width() / 2 + 3) + 'px');
        }
        else if(options.hAlign == 'right')
        {
            tElement.css('left', (e.offset().left + e.width() - tElement.width() + parseInt(options.correction[1])) + 'px');
            tArrow.css('left', (tElement.width() - 7) + 'px');
        }
        else
            tElement.css('left', (e.offset().left) + 'px');
        tArrow.show();
    }

/*-------------------------------------------------------------------*/

    return this.each(handler);
  }
})(jQuery);


CSS:
/* Контейнер с подсказкой */
.ois-tooltip{
  background: #000;
  border-radius: 3px;
  color: #fff;
  cursor: default;
  display: none;
  font-family: Segoe UI, Helvetica;
  padding: 5px 10px;
  position: absolute;
  text-align: center;
  top: 0; left: 0;
  max-width: 200px;
  z-index: 2;
}

/* Стрелочка вверх */
.ois-tooltip .ois-arrow-top{
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-bottom: 5px solid #000;
  display: none;
  position: absolute;
  top: -5px; left: 10px;
}

/* Стрелочка вниз */
.ois-tooltip .ois-arrow-bottom{
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 5px solid #000;
  display: none;
  position: absolute;
  bottom: -5px; left: 10px;
}


Плагин позволяет создавать динамические подсказки для элементов, при чем вряд ли вы найдете более гибкие подсказки чем эти.
Итак, самое интересное: привязка подсказок.

$('*[title]').tooltip({
  attr: 'title',
  removeAttr: true,
  eShow: 'mouseover',
  eHide: 'mouseleave',
  delay: 100,
  hAlign: 'center',
  vAlign: 'top',
  correction: [0, 0],
  width: '200px'
});


О свойствах по порядку:
attr -- название атрибута, в котором хранится текст подсказки
removeAttr -- определяет удалять ли атрибут после кеширования его содержимого(если этого не сделать для title, например, то при наведении мы будем видеть и стандартную подсказку и нашу)
eShow -- массив событий либо одно событие при котором подсказка будет показана
eHide -- то же, что и предыдущий пункт, но для скрытия
delay -- задержка в миллисекундах перед показом
hAlign -- горизонтальное выравнивание по блоку, к которому крепится подсказка(left, center или right)
vAlign -- Вертикальное выравнивание(top или bottom) определяет где показывать подсказку: сверху или снизу(если выйдет за экран, то будет инвертировано)
correction -- массив из двух элементов(вертикаль, горизонталь), корректирующий положение блока(например, вам надо, чтобы он был по-центру, но сдвинут на 50px левей: [0, -50])
width -- определяет максимальную ширину блока(если получится меньше, то будет меньше)

В контрольном примере значения параметров те, что ставятся по умолчанию.

Рассмотрим реализацию примера с картинки:

$('input[name=request]').tooltip({
        delay: 3000,
        correction: [-6, -60],
        eShow: 'focus',
        eHide: ['blur', 'keypress'],
        hAlign: 'center',
        vAlign: 'bottom'
});


Как мы видим подсказка будет показана под полем ввода при передаче ему фокуса через 3 секунды после этого, а скрыта при отборе фокуса либо при нажатии любой клавиши, при чем если клавиша будет нажата в течение этих 3 секунд, то подсказка не будет показана. Отобразится она снизу поля и отпозиционируется по его центру, а также будет откорректирована его позиция: на 6 пикселов ближе к полю чем было бы стандартно и на 60 пикселов влево. Таким образом мы получаем подсказку, появляющуюся только замешкавшимся юзерам, которые за 3 секунды не додумались что надо делать.

Также стоит отметить, что плагин каскадный. То есть вы можете определить подсказку, а потом переопределить её с другими параметрами, которые будут объеденены с ранее заданными.
Прикрепленные файлы:
Всего: 23