«Узерлист»

3 февраля 2008 года, 14:56
К данной статье привязаны следующие примеры:

Все мы прекрасно знаем как выглядят списки в (X)HTML по умолчанию. Сегодня мы попробуем представить их в совершенно ином свете. Для начала мы назовём наш кораблик: его будут звать «Узерлист». Теперь можно приступить к разработке.

Для начала мы определимся с тем, что хотим сделать. Как уже было сказано, мы сделаем пользовательские списки со своим собственным интерфейсом. Для разработки такого типа виджета нам понадобятся те самые три компонента: (X)HTML, CSS и JavaScript, причём последнего будет гораздо больше, чем двух первых.

Рисунок 1

Список — он и в Малайзии список, то есть для его создания логичнее всего будет использование ul в качестве основного элемента (и никаких глубоковложенных div). Помимо самой структуры, мы должны будем привязать наш список к определённой форме в документе, так как мы сделаем возможность отсылать данные, которые были выбраны в списке, через формы. Но об этом позднее. Сейчас же, вернёмся к структуре списков.

Размечать мы их будем подобным образом:

<ul id="идентификатор" > <li value="значение">Элемент 1</li> </ul>

По идентификатору мы сможем передать наш список в класс-обработчик. У каждого элемента li есть атрибут value, который отвечает за отсылаемое формой значение (по аналогии со стандартными списками select). Элементы могут встречаться неограниченное количество раз, то есть размер списка (количество элементов в нём) ничем не ограничен.

Список выглядит немного неопрятно? Согласен, поэтому сейчас мы ему припудрим носик с помощью CSS:

/* Индивидуальный класс для списка */ .userlist { list-style: none; list-style-image: none; padding: 0px; margin: 0px; width: 150px; font-size: 0.7em; } /* Параметры каждого элемента списка */ .userlist li { padding: 4px; border: dotted 1px #bbb; border-bottom: none; color: #666; cursor: hand; cursor: pointer; width: 100%; }

После всех приготовлений можно приступить к разработке ядра нашего списка на JavaScript.

Для начала мы подготовимся и создадим суплементарные функции для дальнейшей разработки. Это всем уже, наверное, знакомые замечательные простые фунцкии bind и listen, предназначенные для обработки событий на JavaScript.

function $(elementid) { return document.getElementById(elementid); } function bind(toObject, methodName) { return function(e){toObject[methodName](e)} } function listen(object, hevent, hfunc) { if (object.addEventListener) object.addEventListener(hevent,hfunc,false); else if (object.attachEvent) object.attachEvent('on'+hevent,hfunc); } function listenex(object, hevent, lobject, lfunc) { listen(object, hevent, bind(lobject, lfunc)); }

Мы добавили функцию listenex, которая немного упростит совместное использование двух других функций.

Так как списков может быть много, чтобы каждый список можно было настраивать и перенастраивать многократно и отдельно от других списков на странице, мы будем использовать объектно-ориентированные возможности JavaScript. Для начала создадим каркас нашей функции.

function UserList(listid, assocform) { //Получаем список по идентификатору this.ulist = $(listid); //Создаём связанный со списком элемент //Он необходим для отправки формы this.usubm = document.createElement("input"); //Массив ответов this.usubm_values = Array(); //Настройка списка, возможность мультивыбора this.multi = true; //Ассоциируемая со списком форма this.uform = $(assocform); }

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

Мы договорились, что предоставим пользователю возможность настраивать список, поэтому внутри нашего класса мы создадим функцию Setup, которая этим и будет заниматься.

this.Setup = function(ismulti, fixedheight, setwidth, setheight) { //Здесь совершенно скоро будет наша функция. }

Пройдёмся по опциям новоявленной функции: ismulti — логическое значение (true или false), определяющее возможность количества выбранных вариантов: один или несколько.

Рисунок 2

fixedheight — фиксировать высоту списка. При установке значения в true, появляется вертикальная полоса прокрутки.

Рисунок 3

setwidth и setheight — установка длины и высоты списка, соответственно.

Рисунок 4

Теперь приступим к реализации самой функции.

//Устанавливаем имя класса CSS для нашего списка this.ulist.className = "userlist"; //Устанавливаем размеры списка, если присутствуют соответствующие опции if (setwidth != null) this.ulist.style.width = setwidth + "px"; else this.ulist.style.width = "150px"; if (setheight != null) this.ulist.style.height = setheight + "px"; //Синхронизируем параметр типа списка if (ismulti != null) this.multi = ismulti; //Если список будет закреплённым по высоте, то добавляем новый класс CSS к списку. if (fixedheight != null && fixedheight != false) this.ulist.className += " fixedsize"; //Устанавливаем настройки для возможности отсылки списка через GET/POST this.usubm.type = "hidden"; this.usubm.name = this.ulist.id; this.ulist.parentNode.appendChild(this.usubm);

CSS-класс fixedsize представит совершенно несложно.

.userlist.fixedsize { overflow: auto; } .userlist.fixedsize li { width: auto; }

Тем самым мы обеспечим фиксированную высоту списка при любом количестве элементов в нём.

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

//Получаем все элементы списка var ulistelements = this.ulist.getElementsByTagName("li"); //Устанавливаем обработчики на каждый элемент списка for (var i = 0; i < ulistelements.length; i++) { //Обработчик мыши listenex(ulistelements[i], "mouseover", this, "handler_mouseover"); listenex(ulistelements[i], "mouseout", this, "handler_mouseout"); //Обработчик действия listenex(ulistelements[i], "click", this, "handler_click"); //Если мы имеем дело с последним элементом списка... if (i+1 == ulistelements.length) { //...то применяем небольшой CSS-fix для нашего конкретного стиля ulistelements[i].className += " lastelement"; } }

Готово. Осталось только установить обработчик на саму форму, к который привязан список.

listenex(this.uform, "submit", this, "handler_submitting");

Вот теперь функция Select разработана полностью и мы можем приступать к разработке дополнительных функций управления элементами списка. Мы будем хранить состояние выбора каждого элемента списка в атрибуте этого элемента и нам нужен будет слой функций для прозрачного управления данными атрибутами.

SetSelect — устанавливает состояние выбора конкретного элемента списка. Через аргументы данной функции передаётся объект элемента и логическая переменная состояния выбора (true — выбран, false — не выбран). IsSelected — проверка наличия выбора на текущем элементе. UnselectAll — снятие флага выбора с каждого элемента списка.

Теперь реализуем эти функции.

//Установка флага выбора на элемент this.SetSelect = function(element, state) { //Установка атрибута element.setAttribute("uselist_is_selected", state.toString()); //Установка нового значения в массиве выбранных значений (для последующей отсылки в форме) this.SetSubmitting(element, state); } //Проверка флага выбора на указанном элементе this.IsSelected = function(element) { if (element.getAttribute("uselist_is_selected") == null || element.getAttribute("uselist_is_selected") == "false") return false; else return true; } //Отмена выбора каждого элемента this.UnselectAll = function() { //Получение элементов var ulistelements = this.ulist.getElementsByTagName("li"); for (var i = 0; i < ulistelements.length; i++) { //Отмена выбора this.SetSelect(ulistelements[i], false); ulistelements[i].className = ulistelements[i].className.replace("selected", "").replace("active", ""); this.SetSubmitting(ulistelements[i], false); } }

Помимо этого, понадобятся следующие функции:

//Получение элемента через событие (X-bro-функция) this.GetElementByEvent = function(handler_event) { if (!handler_event) handler_event = window.event; return handler_event.srcElement ? handler_event.srcElement : handler_event.target; } //Установка флага выбранности в массив для последующей отсылки с помощью форм this.SetSubmitting = function(element, state) { this.usubm_values[element.value] = state; }

Все необходимые функции реализованы, теперь приступаем к реализации обработчиков событий элементов.

//Обработчик отсылки формы this.handler_submitting = function() { var submit_string = ""; //Собираем строку отсылки (разделяем переменные пробелом) for (entry in this.usubm_values) { if (this.usubm_values[entry] == true) submit_string += entry + " "; } //Устанавливаем значение ассоциированного элемента формы this.usubm.value = submit_string; } //Стилизация — обработчик наведения мыши на элемент this.handler_mouseover = function(e) { //Получаем элемент e = this.GetElementByEvent(e); //Проверяем флаг выбора элемента и по его результату выделяем элемент if (!this.IsSelected(e)) e.className += " active"; } //Стилизация — обработчик события, при котором мышь покидает элемент this.handler_mouseout = function(e) { //Получаем элемент e = this.GetElementByEvent(e); //Проверяем флаг выбора и по его результату выделяем элемент if (!this.IsSelected(e)) e.className = e.className.replace("active", ""); } //Обрабатываем клик по элементу this.handler_click = function(e) { //Получаем элемент e = this.GetElementByEvent(e); //Если это список с возожностью выбора лишь одного элемента //Убираем выбор со всех других if (!this.multi) this.UnselectAll(); //Если элемент не выбран... if (!this.IsSelected(e)) { //...устанавливаем на него выбор this.SetSelect(e, true); e.className += " selected"; } else { //...иначе снимаем выбор с него this.SetSelect(e, false); e.className = e.className.replace("selected", ""); } }

Всё готово! Попробуем применить?

var ulist1 = new UserList("place", "listtest"); ulist1.Setup(); var ulist2 = new UserList("set", "listtest"); ulist2.Setup(false, true, 150, 100);

Данный список будет отсылаться на форме, образуя примерно такой результат:

Рисунок 5

Мнения (0)

Все эти хорошие люди уже прокомментировали запись. Поделитесь собственным мнением, расскажите, что вы думаете о поставленной проблеме, задаче, озвученных мыслях.

Не оставлено никаких мыслей к статье. Не желаете ли?

Я тоже знаю!

Вы можете тоже написать собственный комментарий. Если хотите к кому-то обратиться, используйте символ @, после которого не забудьте написать имя того, к кому обращаетесь. Не забывайте про существование XHTML-элементов, с помощью которых вы можете оформить ваш комментарий как вам угодно. И, да: ведите себя достойно, вы же не роботы, правда? Если вам интересно, можете подписаться на комментарии по RSS.