Xiper

Нестандартные checkbox

Автор: Евгений Рыжков Дата публикации:

Задача

Стильно оформить элемент формы input type="checkbox". Так выглядит обычный чекбокс:

обычный чекбокс в ff обычный чекбокс в opera
обычный чекбокс в ff обычный чекбокс в opera

В дизайне же могут быть оформлены совершенно по другому:

примеры стилизированных чекбоксов

Обычные чекбоксы практически не поддаются изменению внешнего вида — ни размеры, ни бордюры, ни фон, ни тем более цвет и вид галочки изменить нельзя (по крайней мере кроссбраузерно). Это сделано разработчиками браузеров, для того что элементы управления оставлять на всех сайтах одинаковыми, что сделает их узнаваемыми для посетителей, что в свою очередь повысит удобность (юзабилити) сайтов. В данной статье не будет подниматься вопрос о целесообразности и корректности изменения внешнего вида элементов форм и как это отобразится на удобстве для пользователя. Задача — научиться менять вид чекбоксов, т.к. довольно часто приходится на практике с этим сталкиваться.

При внедрении красивого чекбокс будем придерживаться следующих требований:

  • точное соответствие дизайну
  • программистам работать с таким чекбоксом должно быть так же как с обычным (значения передаются в том же виде)
  • при загрузке страницы вид чекбокса устанавливается в зависимости от значения
  • простота реализации
  • минимум дополнительного кода в html
  • минимум веса скрипта
  • кроссбраузерность
  • соответствие стандартам

Решение

Алгоритм очень простой:

  1. оборачиваем обычный input type="checkbox" в контейнер
  2. задаем оформление этому контейнеру
  3. скрываем input внутри этого контейнера
  4. с помощью javascript делаем нестандартный чекбокс рабочим

В качестве контейнера идеально подходит <span> — нейтральный строчный элемент. Строчный, а если точнее inline-block, нам нужен для того чтоб можно было задать размеры элементу (width / height) и при этом он вел себя как обычный чекбокс (находится на одном уровне с другими полями формы и текстом).

<span class="niceCheck"><input type="checkbox" name="ch1" /></span>

Для избежания недоразумений лучше явно задать начальное значение полю (value="off" или value="on"). Для CSS подготовим картинки для двух состояний чекбокса или можно воспользоваться техникой спрайтов — одна склеенная картинка:

подготвленный спрайт для нестандартного чекбокс
.niceCheck {
width: 17px;
height: 17px;
display: inline-block;
cursor: pointer;
background: url(paht-to/checkbox.png);
}
.niceCheck input {
display: none;
}
		

Несмотря на то, что мы скрыли чекбокс, его значение все равно будет отправляться на сервер. Далее подключаем скрипт, который оживит наш стилизированный чекбокс.

Скрипт на jQuery

Т.к. практически во всех своих проектах использую библиотеку jQuery чекбокс оживляю тоже с помощью этой библиотеки (скачать можно на официальном сайте jQuery). Подключаем скрипты:

<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/jquery.checkbox.js"></script>

где содержимое jquery.checkbox.js это:

<script type="text/javascript">
jQuery(document).ready(function(){
jQuery(".niceCheck").mousedown(
/* при клике на чекбоксе меняем его вид и значение */
function() {
     changeCheck(jQuery(this));
});
jQuery(".niceCheck").each(
/* при загрузке страницы нужно проверить какое значение имеет чекбокс и в соответствии с ним выставить вид */
function() {
     changeCheckStart(jQuery(this));
});
                                        });
function changeCheck(el)
/* 
	функция смены вида и значения чекбокса
	el - span контейнер дял обычного чекбокса
	input - чекбокс
*/
{
     var el = el,
          input = el.find("input").eq(0);
   	 if(!input.attr("checked")) {
		el.css("background-position","0 -17px");	
		input.attr("checked", true)
	} else {
		el.css("background-position","0 0");	
		input.attr("checked", false)
	}
     return true;
}
function changeCheckStart(el)
/* 
	если установлен атрибут checked, меняем вид чекбокса
*/
{
var el = el,
		input = el.find("input").eq(0);
      if(input.attr("checked")) {
		el.css("background-position","0 -17px");	
		}
     return true;
}
</script>
		

Update — убраны специально введенные значения on и off, т.к. поведение чекбоксов для серверных скриптов немного отличалось. Теперь привязываем оформление к наличию атрибута checked.

Демо пример. Проверено в:

Недостаток метода — обязательное наличие библиотеки весом примерно 20-54 Kb в зависимости от версии. Если используешь в своих проектах jQuery — это недостатком не является, если нет, тогда можно воспользоваться чистым javascript.

Решение на javascript без библиотек

Менем немного html

<span class="niceCheck" onclick="changeCheck(this)" id="niceCheckbox1"><input type="checkbox" name="ch1"  /></span>

Обрати внимание, что если на странцие несколько чекбоксов, контенерам нужно указать уникальные id (нужны для определения состояния полей при загрузке страницы):

<div>
<span class="niceCheck" onclick="changeCheck(this)" id="niceCheckbox1"><input type="checkbox" name="ch1"  /></span>
</div>
<div>
<span class="niceCheck" onclick="changeCheck(this)" id="niceCheckbox2"><input type="checkbox" name="ch1"  /></span>
</div>

CSS остается тем же, а javascript будет таким:

<script type="text/javascript">
function changeCheck(el)
/* 
	функция смены вида и значения чекбокса
	el - span контейнер дял обычного чекбокса
	input - чекбокс
*/
{
     var el = el,
          input = el.getElementsByTagName("input")[0];
     if(input.checked)
     {
	     el.style.backgroundPosition="0 0"; 
		 input.checked=false;
     }
     else
     {
          el.style.backgroundPosition="0 -17px"; 
		  input.checked=true;
     }
     return true;
}
function startChangeCheck(el)
/*
	если значение установлено в on, меняем вид чекбокса на включенный
*/
{
	var el = el,
          input = el.getElementsByTagName("input")[0];
     if(input.checked)
     {
          el.style.backgroundPosition="0 -17px";     
      }
     return true;
}
function startCheck()
{
	/*
		 при загрузке страницы заменяем проверяем значение чекбокса в указанном контенере.
		 если чекбоксов несколько, нужно будет несколько раз вызвать функциую с нужными id
	 */
	startChangeCheck(document.getElementById("niceCheckbox1"));
}
window.onload=startCheck;
</script>

Демо пример. Проверено в:

update — вышеприведенные примеры имеют ряд недостатков:

  • если у пользователя отключен javascript, с этим чекбоксом ничего не сможет сделать
  • довольно примитивная имитация реального checkbox (не предусмотрен атрибут disabled, tabindex и прочее)

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

  • при отключенном javascript на странице остаются обычные checkbox
  • менять значение можно кликами по связанному с полем label (через атрибут for)
  • поле блокируется атрибутом disabled
  • сохраняется порядок tab обхода
  • сохранять значение поля (value), если таковое есть

Стильный checkbox с более высокими требованиями

В форме обычным checkbox, которым хотим изменить внешний вид добавляем class="niceCheck"

<div><label for="ch1">label 1</label><input type="checkbox" class="niceCheck" id="ch1" tabindex="1" /> </div>
<div><label for="ch2">label 2</label><input type="checkbox" class="niceCheck" checked="checked" id="ch2" tabindex="2" value="2" /></div>
<div><label for="ch3">label 3</label><input type="checkbox" class="niceCheck" id="ch3" checked="checked" disabled="disabled" /></div>

В стилях добавляем оформление для заблокированного поля (класс .niceCheckDisabled, для которого подготавливаем отдельную картинку) и checkbox прячем вместо display: none отрицательным отступом (для того чтобы поле могло получать фокус и изменять значение по клику на label)

.niceCheck {
	width: 17px;
	height: 17px;
	display: inline-block;
	cursor: pointer;
	background: url(path-to/checkbox.png);
	overflow: hidden;
}
.niceChecked {
	background-position: 0 -17px;
}
.niceCheck input {
	margin-left: -100px; /* можно задать побольше чтобы наверняка скрыть поле */
}
.niceCheckDisabled {
	background-image: url(path-to/checkbox-disabled.png);
}

Заметка: для скрытия натурального checkbox тут уже используется отрицательный отступ и overflow у контейнера. Иначе в IE не будет работать смена состояния поля по клику на метку, т.к. при display: none поле не может получить фокус.

Усложнился скрипт, работает с jQuery 1.3.2:

jQuery(document).ready(function(){
jQuery(".niceCheck").each(
/* при загрузке страницы меняем обычные на стильные checkbox */
function() {
     changeCheckStart(jQuery(this));
});
                                        });
function changeCheck(el)
/* 
	функция смены вида и значения чекбокса при клике на контейнер чекбокса (тот, который отвечает за новый вид)
	el - span контейнер для обычного чекбокса
	input - чекбокс
*/
{
	var el = el,
		input = el.find("input").eq(0);
	if(el.attr("class").indexOf("niceCheckDisabled")==-1)
	{	
   		if(!input.attr("checked")) {
			el.addClass("niceChecked");
			input.attr("checked", true);
		} else {
			el.removeClass("niceChecked");
			input.attr("checked", false).focus();
		}
	}
    return true;
}
function changeVisualCheck(input)
{
/*
	меняем вид чекбокса при смене значения
*/
var wrapInput = input.parent();
	if(!input.attr("checked")) {
		wrapInput.removeClass("niceChecked");
	}
	else
	{
		wrapInput.addClass("niceChecked");
	}
}
function changeCheckStart(el)
/* 
	новый чекбокс выглядит так <span class="niceCheck"><input type="checkbox" name="[name check]" id="[id check]" [checked="checked"] /></span>
	новый чекбокс получает теже name, id и другие атрибуты что и были у обычного
*/
{
try
{
var el = el,
	checkName = el.attr("name"),
	checkId = el.attr("id"),
	checkChecked = el.attr("checked"),
	checkDisabled = el.attr("disabled"),
	checkTab = el.attr("tabindex"),
    checkValue = el.attr("value");
	if(checkChecked)
		el.after("<span class='niceCheck niceChecked'>"+
			"<input type='checkbox'"+
			"name='"+checkName+"'"+
			"id='"+checkId+"'"+
			"checked='"+checkChecked+"'"+
            "value='"+checkValue+"'"+
			"tabindex='"+checkTab+"' /></span>");
	else
		el.after("<span class='niceCheck'>"+
			"<input type='checkbox'"+
			"name='"+checkName+"'"+
			"id='"+checkId+"'"+
             "value='"+checkValue+"'"+
			"tabindex='"+checkTab+"' /></span>");
	/* если checkbox disabled - добавляем соотвсмтвующи класс для нужного вида и добавляем атрибут disabled для вложенного chekcbox */		
	if(checkDisabled)
	{
		el.next().addClass("niceCheckDisabled");
		el.next().find("input").eq(0).attr("disabled","disabled");
	}
	/* цепляем обработчики стилизированным checkbox */		
	el.next().bind("mousedown", function(e) { changeCheck(jQuery(this)) });
	el.next().find("input").eq(0).bind("change", function(e) { changeVisualCheck(jQuery(this)) });
	if(jQuery.browser.msie)
	{
		el.next().find("input").eq(0).bind("click", function(e) { changeVisualCheck(jQuery(this)) });	
	}
	el.remove();
}
catch(e)
{
	// если ошибка, ничего не делаем
}
    return true;
}

Демо пример. Проверено в:

Как сделать выбор всех checkbox

Алгоритм: меняем всем полям (input), вложенным в контейнер с классом .niceCheck, значение атрибута checked на true. Затем контейнерам с классом .niceCheck добавляем класс .niceChecked. Пример:

jQuery("#chooseAll").click(
/* выбрать | снять выделение все checkbox */
function() {
	if(jQuery(this).val()=="выбрать все")
	{
		jQuery("#myForm .niceCheck > input").attr("checked",true);
		jQuery("#myForm .niceCheck").addClass("niceChecked");
		jQuery(this).val("снять у всех выделение");
	}
	else
	{
		jQuery("#myForm .niceCheck > input").attr("checked",false);
		jQuery("#myForm .niceCheck").removeClass("niceChecked");
		jQuery(this).val("выбрать все");
	}
	return;
});

Демо пример.

Динамическое добавление checkbox

update: Алгоритм:

  • дабавляется checkbox с классом niceCheck
  • вызывается функция changeCheckStart, параметром которой является ссылка на добавленный checkbox
/* 
	динамическое добавление checkbox
*/
jQuery("#addCheck").click(
function() {
	jQuery("#testForm").append("<div><input type='checkbox' class='niceCheck' /></div>");
	var el = jQuery("input.niceCheck");
	changeCheckStart(el);
});

Демо пример.

По теме