Как обычно все началось из-за отсутствия или плохого поиска, или не полной реализации того что мне было необходимо.
А необходимы мне были кастомные radio и checkbox'ы которые я смог бы применять в своей повседневной работе при верстке. При этом они должны были бы работать в IE6+ и всех основных браузерах.
Также элементы должны были реагировать по клику на лейбл. И еще одно их могло быть на 1 странице сколько угодно с разными стилями(да иногда случаются такие мего дизайны).
Поэтому я решил взять все лучшее, что встречал в реализациях чекбоксов и радиобатонов на JS. И написать свой плагин jQuery который удовлетворял бы мои потребности.
Вид элементов реализовывался с помощью спрайта с 4 состояниями:
— неактивный вид;
— неактивный мышь нажата;
— активный;
— активный мышь нажата.
Т.к. придется работать со спрайтом то необходимо знать смешения по вертикали картинок в нем. Для этого необходимо передавать в плагин высоту элемента, а в спрайте расположить их так чтоб они занимали одинаковое по высоте пространство.
Также необходимо чтоб при вызове плагина передавался класс, через который я смогу стилизовать элемент.
После первоначальной реализации я понял, что необходимы еще пару условий, без которых плагин будет работать некорректно. Это возможность смены состояний элементов(актинвный/неактивный) и стилизация динамически созданных элементов.
Приведу для начала весь код плагина:
(function ($) {
$.CustomData = {
elements:$()
};
$.fn.extend({
Custom:function (options) {
var elements = this;
$.CustomData.elements = $.CustomData.elements.add(elements);
/*Дефолтные значения параметров*/
var defaults = {
customStyleClass:"checkbox",
customHeight:"16"
};
/*Заменяем дефолтные опции на переданные если таковые есть*/
options = $.extend(defaults, options);
/*Вид при нажатии на активный и неактивный элементы*/
var pushed = function () {
var element = $(this).children('input');
if (element.is(':checked')) {
/*смещения в спрайте*/
$(this).css('backgroundPosition', "0px -" + options.customHeight * 3 + "px");
} else {
$(this).css('backgroundPosition', "0px -" + options.customHeight + "px");
}
};
/*Отмечаем нажатый елемент все остальные сбрасываем, если они в групе(radio)*/
var check = function () {
var element = $(this).children('input');
if (element.is(':checked') && element.attr('type') === 'checkbox') {/*Отмеченный чекбокс*/
$(this).css('backgroundPosition', '0px 0px');
$(this).children('input').attr('checked', false).change();
/*Меняем атрибут на неотмеченный и вызываем событие смены состояния элемента*/
} else {
if (element.attr('type') === 'checkbox') {/*Неотмеченный чекбокс*/
$(this).css('backgroundPosition', "0px -" + options.customHeight * 2 + "px");
} else {
/*Радиобатоны*/
$(this).css('backgroundPosition', "0px -" + options.customHeight * 2 + "px");
$('input[name=' + element.attr('name') + ']').not(element).parent().css('backgroundPosition', '0px 0px');
}
$(this).children('input').attr('checked', 'checked').change();
}
};
/*Обновление картинки при клике по лейблу и загрузке документа*/
var update = function () {
$.CustomData.elements.each(function () { /*Проходим по всем елементам и проверяем их состояние*/
if ($(this).is(':checked')) {
$(this).parent().css('backgroundPosition', "0px -" + $(this).attr('data-height') * 2 + "px");
} else {
$(this).parent().css('backgroundPosition', "0px 0px");
}
});
};
/*Обновление при изменении состояния disabled/enabled */
var refresh = function () {
if (!$(this).prop('disabled')) {
$(this).parent().mousedown(pushed);
$(this).parent().mouseup(check);
$(this).parent().removeClass('disabled');
} else {
$(this).parent().addClass('disabled');
$(this).parent().unbind('mousedown', pushed);
$(this).parent().unbind('mouseup', check);
}
};
return this.each(function () {
if ($(this).attr('data-init') != '1') {
$(this).attr('data-init', '1');
$(this).attr('data-height', options.customHeight);
/*Оборачиваем в <span></span>*/
$(this).wrap('<span/>');
/*Приписываем класс оформления переданный в параметрах*/
var span = $(this).parent().addClass(options.customStyleClass);
if ($(this).is(':checked') === true) { /*Задаем картинку еси элемент отмечен*/
span.css('backgroundPosition', "0px -" + (options.customHeight * 2) + "px");
}
/*Бинд на изменение состояния элемента и кастомное событие для обновления после программного изменения состояния кнопки*/
$(this).bind('change', update);
$(this).bind('custom.refresh', refresh);
if (!$(this).prop('disabled')) {
/*Бинд функций на span*/
span.mousedown(pushed);
span.mouseup(check);
} else {
/*Добавление класса если элемент неактивен*/
span.addClass('disabled');
}
}
});
}
});
})(jQuery);
Начал я с реализации структуры плагина, а вид она имеет такой:
(function ($) {
$.fn.extend({
Custom:function (options) {
/*Дефолтные значения параметров*/
var defaults = {
customStyleClass:"checkbox",
customHeight:"16"
};
/*Заменяем дефолтные опции на переданные если таковые есть*/
options = $.extend(defaults, options);
};
return this.each(function () {
});
}
});
Можно встретить такое описание выше напечатанного — «используется для создания типичного дополнения jQuery".
Начнём с конца:
return this.each(function () {
if ($(this).attr('data-init') != '1') {
$(this).attr('data-init', '1');
$(this).attr('data-height', options.customHeight);
/*Оборачиваем в <span></span>*/
$(this).wrap('<span/>');
/*Приписываем класс оформления переданный в параметрах*/
var span = $(this).parent().addClass(options.customStyleClass);
if ($(this).is(':checked') === true) { /*Задаем картинку еси элемент отмечен*/
span.css('backgroundPosition', "0px -" + (options.customHeight * 2) + "px");
}
/*Бинд на изменение состояния элемента и кастомное событие для обновления после программного изменения состояния кнопки*/
$(this).bind('change', update);
$(this).bind('custom.refresh', refresh);
if (!$(this).prop('disabled')) {
/*Бинд функций на span*/
span.mousedown(pushed);
span.mouseup(check);
} else {
/*Добавление класса если элемент неактивен*/
span.addClass('disabled');
}
}
});
В принципе в комментариях к коду все описано, могу добавить лишь
$(this).attr('data-init', '1');
использую для того чтоб знать стилизован уже элемент или нет(конечно можно и через родителя узнать но я решил протестировать такой вот метод).
$(this).attr('data-height', options.customHeight);
Здесь сохраняю высоту элемента т.к. элементов у меня с различными спрайтами может быть сколько угодно.
$(this).bind('custom.refresh', refresh);
Тоже интересный метод, привязывается на кастомное событие «custom.refresh» вызов функции refresh.
Например изменилось состояние элемента на неактивное, тогда необходимо изменить вид элементы и снять все события с него.
Например:
$('#radio3').removeAttr('disabled').trigger('custom.refresh');
элемент стал неактивен и произошло события «custom.refresh» благодаря которому выполнилась функция refresh.
Далее идут функции которые описаны в этой части:
Custom:function (options) {...};
Функция pushed — ставит смещение в спрайте для активного и неактивного вида, она довольно простая:
var pushed = function () {
var element = $(this).children('input');
if (element.is(':checked')) {
/*смещения в спрайте*/
$(this).css('backgroundPosition', "0px -" + options.customHeight * 3 + "px");
} else {
$(this).css('backgroundPosition', "0px -" + options.customHeight + "px");
}
};
Фукция check:
/*Отмечаем нажатый элемент все остальные сбрасываем, если они в группе (radio)*/
var check = function () {
var element = $(this).children('input');
if (element.is(':checked') && element.attr('type') === 'checkbox') {/*Отмеченный чекбокс*/
$(this).css('backgroundPosition', '0px 0px');
$(this).children('input').attr('checked', false).change();
/*Меняем атрибут на неотмеченный и вызываем событие смены состояния элемента*/
} else {
if (element.attr('type') === 'checkbox') {/*Неотмеченный чекбокс*/
$(this).css('backgroundPosition', "0px -" + options.customHeight * 2 + "px");
} else {
/*Радиобатоны*/
$(this).css('backgroundPosition', "0px -" + options.customHeight * 2 + "px");
$('input[name=' + element.attr('name') + ']').not(element).parent().css('backgroundPosition', '0px 0px');
}
$(this).children('input').attr('checked', 'checked').change();
}
};
Также все довольно ясно из комментариев в коде добавлю лишь об этом
$(this).children('input').attr('checked', false).change();
.attr('checked', false).change() — необходимо чтоб событие change() файрилось и атрибут checked был изменен. Пришлось поискать это на stackoverflow.
Функция Update:
/*Обновление картинки при клике по лейблу*/
var update = function () {
$.CustomData.elements.each(function () { /*Проходим по всем елементам и проверяем их состояние*/
if ($(this).is(':checked')) {
$(this).parent().css('backgroundPosition', "0px -" + $(this).attr('data-height') * 2 + "px");
} else {
$(this).parent().css('backgroundPosition', "0px 0px");
}
});
};
Тут не вышло обойтись без переменной, куда можно было б сохранить все элементы, на которых вызывается плагин($.CustomData.elements). Описывается она выше $.fn.extend({...})
$.CustomData = {
elements:$()
};
и при вызове плагина в нее помещается набор элементов
var elements = this;
$.CustomData.elements = $.CustomData.elements.add(elements);
И последняя функция, которая необходима при изменении состояния элемента refresh:
/*Обновление при изменении состояния disabled/enabled */
var refresh = function () {
if (!$(this).prop('disabled')) {
$(this).parent().mousedown(pushed);
$(this).parent().mouseup(check);
$(this).parent().removeClass('disabled');
} else {
$(this).parent().addClass('disabled');
$(this).parent().unbind('mousedown', pushed);
$(this).parent().unbind('mouseup', check);
}
};
Довольно простая в понимании, просто снимаем обработчики или добавляем вновь и дописываем/убираем класс «disabled», через который можно задать вид элемента в неактивном состоянии(обычно прозрачность меняют).
Пример класса с описанием вида радиобатона:
.radio {
display:block;
height: 25px;
width: 19px;
background: url("radio.png") no-repeat 0 0 transparent;
position:relative;
}
.radio.disabled{
opacity:0.5;
filter:Alpha(opacity="50");
}
.radio input{
position:absolute;
right:-400px;
top:0px;
}
Вызов плагина:
$("input[type='radio']").Custom({
customStyleClass:'radio',
customHeight:'25'
});
Надеюсь это поможет таким как я в написании своих первых плагинов на jQuery.
Это все о чем я хотел поведать в связи со своими плохими поисками нужного мне плагина. Благодарю хорошего человека Андрея за помощь в познании jQuery и написании плагина а также разработчика вот этого плагина http://ryanfait.com/resources/custom-checkboxes-and-radio-buttons/ за то что он был на js и не до конца допилен, дав мне возможность для написания своего с необходимыми мне фишками. Буду рад коментам и критике для улучшения работы плангина.
Плагин тут https://github.com/n0r8/Custom-radio-checkbox
Автор: n0r8