Эта статья является отрывком из книги Эрл Кастледин и Крейга Шарки «От новичка до ниндзя» . Смотрите более подробную информацию ниже.
Списки — настоящие незамеченные герои периода пост-таблицы в Интернете. Поскольку дизайнеры были освобождены от ограничений ячейки тиранической таблицы, они начали искать другие (семантически правильные) способы воссоздания общих элементов пользовательского интерфейса, таких как меню, панели навигации, облака тегов и т. Д.
И раз за разом, поскольку лишняя раскладка макета была удалена из базовых данных, все, что осталось, было — список!
StarTrackr! Сайт уже содержит обширный массив списков: они составляют основу наших вкладок, аккордеонов, меню, галерей изображений и многого другого, но мы можем сделать гораздо больше, чтобы расширить и улучшить скромный список.
JQuery UI Выбор
Океан контента, созданного пользователями, оказывается полезным для нашего клиента. Тысячи тегов поступают от пользователей сайта, но теперь юридический отдел говорит, что, будучи менеджером, он должен утверждать каждый из них вручную, чтобы избежать повторения недавнего неприятного судебного процесса.
Поскольку на сайте используется система тегов без ограничений, в списках есть стеки дублирующих тегов, а с текущей системой это означает стеки дополнительного администрирования. Что клиент действительно хочет, так это способ легко увидеть теги, выбрать их (и любые дубликаты) и нажать кнопку, чтобы одобрить или отклонить их.
Наш план атаки — добавить поведение выбора jQuery UI в наш список. Создание элемента по выбору дает пользователю возможность лассо любого из потомков элемента, чтобы выбрать их: если вы нажмете на один элемент, а затем перетащите на последующие элементы, они станут выделенными. Вы можете обработать выбор, как считаете нужным. Идеально подходит для администрирования скучных списков! Поведение, которое мы стремимся создать, показано ниже.
В дополнение к лассо, выбираемое поведение также позволяет добавлять непоследовательные элементы в список с помощью клавиши Ctrl (как это можно сделать в большинстве приложений для настольных компьютеров) — и даже перемещаться по выборкам с помощью клавиатуры.
Держите своих пользователей в курсе
Хотя возможность щелкать и перетаскивать выбранные элементы в списке очень полезна и полезна, она полезна и полезна, только если пользователи об этом знают! Подобный выбор — это нестандартная форма взаимодействия в Интернете, поэтому вам необходимо предоставить инструкции своим пользователям, чтобы научить их использовать новые функции.
Давайте посмотрим на разметку. Сервер выкладывает длинный список тегов, который является хорошей основой для нашей селекторной сетки. Мы также добавим несколько кнопок, позволяющих пользователям утверждать или отклонять теги в своем выборе, а также кнопку для сброса выбора:
<ul id="tags"> <li>bad singer</li> <li>old</li> <li>plastic surgery</li> <li>broke</li> ⋮ </ul> <button id="approve">Approve</button> <button id="reject">Reject</button> <button id="clear">Clear</button>
Большой длинный список немного пугает, поэтому мы используем базовый CSS, чтобы превратить этот список в сетку и преобразовать каждый тег в маленькую рамку. Когда наша сетка готова к работе, мы должны добавить библиотеку пользовательского интерфейса jQuery на страницу. Теперь пришло время сообщить списку тегов, что он может быть выбран:
$("#tags").selectable();
Запустите браузер и проверьте его. Хм … что-нибудь на самом деле произошло? Ну да, это так, но это невидимо! Метод selectable
работает путем добавления атрибутов class
к выбранным элементам, если мы не назначим стили этим классам, мы не сможем увидеть, что происходит. Если вы проверяете элементы списка с помощью Firebug по мере их выбора, вы увидите происходящие изменения. Давайте попробуем стилизовать выбранные элементы:
#tags .ui-selecting { background: #FEFF9F; } #tags .ui-selected { background-color:#eEeF8F; }
Класс ui-selecting class
пользовательского интерфейса применяется, когда пользователь находится в процессе выбора элементов, и ui-selected class
добавляется, как только они останавливаются. Если вы попробуете это сейчас, вы увидите, что можете лассо несколько квадратов. Это вполне естественное взаимодействие — именно то, что вы хотите от ваших компонентов страницы. Вы также можете нажать, удерживая клавишу Ctrl, чтобы выбрать отдельные элементы.
Следующая задача, которую мы хотим выполнить, — помочь с дублирующимися тегами. В системе тегов важно количество тегов для каждого термина — поэтому вместо того, чтобы просто удалять дубликаты, мы напишем некоторый код, чтобы выбрать любые теги, которые соответствуют выбору пользователя. Например, если они нажимают «A-lister», все метки «A-lister» будут подсвечены.
Нам нужно знать, к каким событиям мы можем подключиться из компонента jQuery UI. Изучив документацию, мы обнаружим, что можем фиксировать события start
, stop
, selecting
, unselecting
selecting
, selecting
и unselecting
selected
. Мы можем зафиксировать событие selecting
и удалить дубликаты, когда пользователь перемещает мышь, но это может немного сбить с толку. Мы будем придерживаться события остановки, которое срабатывает, как только пользователь завершает выбор:
$('#tags').selectable({ stop: function() { // The user stopped selecting! } });
Теперь мы можем начать наш квест, чтобы найти дубликаты тегов. Наш общий подход будет состоять в том, чтобы составить список всех тегов, выбранных пользователем, а затем найти любые дубликаты этих тегов, которые появляются в большом списке тегов:
var names = $.map($('.ui-selected, this'), function(element, i) { return $(element).text(); }); $('li', this) .filter(function() { if ($.inArray($(this).text(), names) != -1) { return true; } else { return false; }; }) .addClass('ui-selected');
Чтобы найти дубликаты, мы обратились в службу поддержки с набором новых функций jQuery, так что держитесь за свои шляпы!
Первый из них является самым странным: $('.ui-selected', this)
. Это похоже на обычный селектор jQuery, но есть второй параметр. Оказывается, что полное определение для селектора jQuery на самом деле $(expression, context)
мы только что пропустили второй параметр. context
определяет, где jQuery должен искать ваш селектор; по умолчанию он выглядит повсюду на странице, но, указав наш неупорядоченный список в качестве контекста, выражение будет ограничено элементами внутри списка.
$.map
и $.inArray
Далее мы используем несколько утилит jQuery: $.map
и $.inArray
для манипулирования элементами списка. Служебные методы, которые предоставляет jQuery, в основном предназначены для работы с массивами JavaScript — и вот что мы здесь делаем. Сначала мы создаем массив с именем names
, который заполняем с помощью метода $.map
.
Метод $.map
позволяет вам брать каждый элемент в массиве, обрабатывать его некоторым образом и возвращать результаты в виде нового массива. Вы используете его, когда хотите преобразовать каждый элемент одинаково. Мы хотим преобразовать нашу выборку jQuery в простой список текста тега — поэтому мы передаем выборку и определяем анонимную функцию для возврата текста каждого элемента. Эй, Presto: массив текста тега!
Затем мы используем трюк с контекстом, как и раньше, чтобы извлечь все элементы элемента списка и отфильтровать их на основе того, являются ли они дубликатами. Наша функция filter
использует служебный метод $.inArray
, который выполняет поиск в массиве (но только в простых массивах JavaScript, а не в выборках jQuery, к сожалению) для указанного значения. Если задан массив и поисковый термин, например $.inArray(value, array)
, он вернет индекс значения в массиве. Полезно, что он вернет -1
если значение не найдено в массиве. Помните, что filter
ожидает, что мы вернем либо true
либо false
поэтому мы просто проверяем, возвращает ли $.inArray
-1
, и возвращаем true
или false
в зависимости от ситуации. Использование filter
таким образом позволяет нам искать в нашем массиве текстов тегов текст каждого элемента списка — если он там есть, он является дубликатом, поэтому мы возвращаем его в фильтр, который нужно выбрать.
Доступ к данным
Теперь, когда мы можем делать выборки, как мы можем их использовать? Компонент jQuery UI Selectable работает с именами классов, так же как и мы. Чтобы получить список выбранных значений, мы просто ищем любые элементы, в которых есть ui-selected class
:
$('#approve').click(function() { $('#tags') .find('.ui-selected') .addClass('approve') .removeClass('ui-selected reject'); }); $('#reject').click(function() { $('#tags') .find('.ui-selected') .addClass('reject') .removeClass('ui-selected approve'); }); $('#clear').click(function() { $('#tags') .find('li') .removeClass('ui-selected approve reject'); $('#approved').val(''); });
Мы просто добавляем reject class
approve
или reject class
когда пользователь нажимает на наши кнопки, а также обязательно удаляем ui-selected class
, ui-selected class
в пользовательском ui-selected class
, поскольку мы хотим, чтобы стили утвержденных тегов отличались от выбранных.
Но что, если мы хотим, скажем, отправить эту информацию на сервер? Возможно, было бы хорошо сохранить список утвержденных тегов в скрытом поле формы, чтобы сервер мог получить к нему доступ для обработки. Давайте обновим #approve
кликов #approve
чтобы выполнить итерацию по утвержденным элементам, и добавим индекс каждого элемента в скрытое поле в простом формате с разделителями каналов:
$('#approve').click(function() { var approvedItems = ""; $('#tags') .find('.ui-selected') .addClass('approve') .removeClass('ui-selected reject') .each(function() { approvedItems += $(this).index() + "|"; }); $('#approved').val(approvedItems); });
Мы также добавим строку в наш #clear
нажатия кнопки #clear
, чтобы очистить значение этого ввода:
$('#approved').val('');
Благодаря методу index
мы теперь знаем, какие элементы в списке были утверждены. index сообщит вам позицию элемента внутри его родительского элемента. Наш контроль впечатляет простотой использования. selectable
поведение пользовательского интерфейса jQuery делает большую работу за кулисами, позволяя выбирать списки, но конечный результат является естественным компонентом, и это именно то, что мы хотим.
Сортировка списков
Под контролем системы тегов пришло время обратиться к некоторым другим спискам, которые разбросаны по всему разделу администратора. Многие из этих списков заполняются сервером в порядке их ввода в систему. Это хорошо для просмотра того, что нового, но плохо для поиска конкретных предметов. Наш клиент попросил нас встроить некоторые возможности сортировки во все списки в разделе администратора, чтобы он мог нажать кнопку и отсортировать списки в алфавитном порядке по возрастанию или убыванию.
Разметка, с которой мы будем иметь дело, представляет собой простой неупорядоченный список, состоящий из ссылок:
<ul class="sortable"> <li><a href="#">Beau Dandy</a></li> <li><a href="#">Glendatronix</a></li> <li><a href="#">BMX Spandex Corporation</a></li> <li><a href="#">Maxwell Zilog</a></li> <li><a href="#">Computadors</a> </ul>
В объектах jQuery отсутствуют какие-либо встроенные функции сортировки. В конце концов, это имеет смысл; выборка может включать элементы различного типа, расположенные в разных частях страницы, поэтому сортировка их согласованным образом будет невозможна. Поэтому, чтобы отсортировать выборки jQuery, нам нужно обратиться к некоторым методам массива JavaScript. Выборки jQuery на самом деле не являются массивами, но они «похожи на массивы» и позволяют нам использовать для них функцию sort
JavaScript.
Мы попытаемся создать многоразовый виджет для сортировки списка. Мы назовем его SORTER
и SORTER.sort(list)
для сортировки списка в порядке возрастания, а SORTER.sort(list, 'desc')
для сортировки в порядке убывания. Предположим, что переданный селектор будет соответствовать упорядоченным или неупорядоченным спискам, но давайте посмотрим, сможем ли мы это сделать:
var SORTER = {}; SORTER.sort = function(which, dir) { SORTER.dir = (dir == "desc") ? -1 : 1; $(which).each(function() { // Find the list items and sort them var sorted = $(this).find("> li").sort(function(a, b) { return $(a).text().toLowerCase() > $(b).text().toLowerCase() ? SORTER.dir : -SORTER.dir; }); $(this).append(sorted); }); };
Этот код обманчиво короткий, потому что он делает много! Сначала мы проверяем, передан ли desc
в качестве параметра dir
, и соответственно устанавливаем переменную SORTER.dir
. Все, что нам нужно сделать, это взять все элементы списка дочерних элементов первого уровня и дать им сортировку. Мы хотим только предметы первого уровня; если бы мы захватили дополнительные уровни, они были бы отсортированы и перенесены на родительский уровень. Так как вызов sort
наши выборки до необработанного JavaScript, нам нужно перевернуть их в $()
чтобы иметь возможность вызывать text
метод jQuery и сравнивать их значения. Мы также конвертируем значения в нижний регистр, что делает сортировку без учета регистра.
Функция sort
Функция sort
— это простой старый JavaScript: она сортирует массив на основе результатов функции, которую вы передаете ему. sort
перебирает содержимое массива и передает их вашей функции попарно. Если ваша функция возвращает 1
, sort
поменяет местами элементы и поместит вторую на первую. Если ваша функция возвращает -1
, JavaScript поместит первый элемент первым. Наконец, если ваша функция возвращает 0
, sort
будет считать, что оба элемента равны и сортировка не будет выполняться.
Мы делаем немного волшебства, чтобы позволить нам использовать одну и ту же функцию для сортировки по возрастанию и убыванию: мы установили нашу переменную SORTER.dir
в -1
или 1
, в зависимости от направления. Затем в функции сравнения sort
мы выполняем дальнейшие вычисления: если a
меньше b
, мы возвращаем -SORTER.dir
. Если направление приходит как -1
, мы обрабатываем его как -(-1)
, что равно 1
поэтому, если мы пытаемся отсортировать по убыванию, возвращаемые значения меняются местами.
После того, как мы отсортировали элементы, мы можем снова вставить их в список в правильном порядке. Помните, что функция append
сначала удаляет элемент — поэтому он удаляет элемент и добавляет его в правильное положение.
Чтобы проверить это, мы добавим несколько кнопок в наш HTML и вызовем SORTER.sort
из их обработчиков событий click
:
$('#ascending').click(function() { SORTER.sort('.sortable'); }); $('#descending').click(function() { SORTER.sort('.sortable', 'desc'); });
Управление списками выбора ящиков
Здесь мы собираемся изучить элементы select
, особенно те, которые имеют multiple="multiple"
(то есть, поля выбора, которые отображаются в виде выбираемых списков элементов).
Замена элементов списка
StarTrackr! Клиент попросил нас улучшить функциональность администратора для назначения знаменитостей в А-список. Текущая функциональность состоит из двух выбранных элементов: один содержит знаменитости A-list, а другой — все остальные знаменитости в системе. Но мир популярности крайне непостоянен, а завтрашний выпускник сегодня может стать никем. Таким образом, клиент хочет иметь возможность легко обмениваться знаменитостями между каждым списком. Мы добавим несколько элементов управления к интерфейсу, чтобы позволить ему сделать это, как показано ниже:
Это тот HTML-код, с которым мы имеем дело, состоящий из двух элементов select
и нескольких кнопок для выполнения различных операций:
<select id="candidates" multiple="multiple" size="8"> <option value="142">Beau Dandy</option> ⋮ </select> <select multiple="multiple" size="8"> <option value="232">Johnny Stardust</option> ⋮ </select> <div> <input type="button" value=">" /> <input type="button" value="<" /> ⋮ </div>
Как уже говорилось, клиент хочет иметь возможность поменять выбранные элементы из одного списка в другой. Мы SWAPLIST
объект SWAPLIST
который будет содержать все функциональные возможности, которые мы SWAPLIST
. Затем его можно использовать в любое время, когда нам нужно поиграть с select
элементами:
var SWAPLIST = {}; SWAPLIST.swap = function(from, to) { $(from) .find(':selected') .appendTo(to); }
Мы определили функцию swap
которая принимает строки селектора, нацеленные на два списка: список источника и список назначения. Первая задача, которую мы хотим сделать, это захватить любые элементы, которые в данный момент выбраны. Мы можем сделать это, используя действие find
с :selected
фильтром формы. Этот фильтр вернет все элементы формы, у которых установлен selected
атрибут. Затем мы можем переместить выделение в список адресатов с помощью appendTo
. Легко! И как только мы определили эту функциональность, мы можем применить ее к любым двум спискам, вызвав наш метод подкачки из соответствующих обработчиков кликов:
$('#swapLeft').click(function() { SWAPLIST.swap('#candidates', '#a-listers'); }); $('#swapRight').click(function() { SWAPLIST.swap('#a-listers', '#candidates'); });
Теперь выбранные предметы можно менять местами по желанию! Давайте добавим больше функциональности в наш объект SWAPLIST
. Как насчет замены всех элементов? Это даже проще:
SWAPLIST.swapAll = function(from,to) { $(from) .children() .appendTo(to); }
Мы просто берем все дочерние элементы (а не только выбранные элементы) и добавляем их в конец пункта назначения — весь список перемещается из списка источников в список назначения.
Инвертирование выбора
Следующий запрос клиента — добавить кнопку, которая инвертирует текущий выбор, чтобы его сотрудникам было легче работать с большими выборами. При щелчке по этой ссылке все выбранные в данный момент элементы в целевом списке отменяются, и наоборот. Давайте SWAPLIST
функцию внутри объекта SWAPLIST
которая делает это:
SWAPLIST.invert = function(list) { $(list) .children() .attr('selected', function(i, selected) { return !selected; }); }
Все, что нам нужно сделать, это извлечь каждый элемент списка и поменять его selected
атрибут. Мы используем функцию attr
чтобы установить для наших элементов списка значение !$(this).attr('selected')
. Оператор JavaScript NOT ( !
) (Восклицательный знак) инвертирует логическое значение, поэтому, если значение было true
оно становится false
, а если оно false
оно становится true
!
Вызов attr
с параметром функции
Это отличный трюк: мы использовали действие attr
но это не та версия, к которой мы привыкли. Ранее мы использовали действие attr(key, value)
для установки атрибутов в статическое значение, но attr
также позволяет нам передавать функцию для определения значения. В функцию будут переданы два параметра: индекс элемента и его текущее значение. Возвращаемое значение функции становится новым значением атрибута. Для нашего метода invert
мы возвращаем значение, противоположное текущему значению выбора элемента, поэтому каждый элемент переключается. Мы можем выполнять такую динамическую обработку с помощью стеков команд: text
, html
, val
, addClass
, wrap
… и многих других!
Поиск по спискам
После того, как клиент постоянно слушает, как тяжело найти знаменитостей, которых он пытается выбрать, вы решили добавить немного халявы: функция быстрого поиска, которая позволяет ему печатать некоторые символы и автоматически выбирать любые подходящие элементы. :
SWAPLIST.search = function(list, search) { $(list) .children() .attr('selected', '') .filter(function() { if (search == '') { return false; } return $(this) .text() .toLowerCase() .indexOf(search) > - 1 }) .attr('selected', 'selected'); }
Что тут происходит? Сначала мы берем все элементы списка, а затем очищаем все предыдущие выборы (устанавливая selected
пустую строку). Далее мы используем действие filter
чтобы найти любые элементы, которые мы ищем.
Действие filter
принимает функцию в качестве параметра и запускает эту функцию для каждого объекта jQuery в выделении. Если функция возвращает true
, элемент остается в выделении. Но если функция возвращает false
— она ушла … из выбора и не подвергается влиянию дальнейшей обработки.
Чтобы найти интересующие нас элементы, мы проверяем, содержит ли текст, который они содержат, искомый текст. Для этого мы используем текстовое действие, которое дает нам строку. Мы конвертируем его в нижний регистр (поэтому поиск будет нечувствителен к регистру) и проверяем, находится ли наш исходный текст в строке элемента. Метод JavaScript indexOf
найдет положение строки внутри другой строки; например, "hello".indexOf('ll');
вернет 2
(индекс начинается с 0
, как обычно). Если подстрока не найдена, indexOf
вернет -1
, что мы и проверяем здесь.
Какие бы элементы ни оставались в выборе jQuery после filter
функции filter
должны содержать ключевое слово, которое мы ищем, поэтому еще раз мы используем метод attr
для их выбора.
Чтобы использовать метод поиска, который мы создали, мы могли бы прикрепить его к обработчику click
чтобы пользователь вводил слово, а затем нажимал кнопку поиска. Еще лучше прикрепить обработчик keyup к самому input
, поэтому он выбирается при input
:
$('#search').keyup(function() { SWAPLIST.search("#a-listers, #candidates", $(this).val()); });
Проверьте книгу и купите ее онлайн на Вы также можете скачать архив кода книги, проверить наличие обновлений и ошибок или проконсультироваться с процветающим сообществом разработчиков JavaScript и jQuery на форумах SitePoint .