Javascript фильтр таблиц
Наконец, я нашел время и хочу представить вашему вниманию свой новый скрипт, который позволяет осуществить фильтрацию данных в html-таблицах.
Скрипт поддерживает следующие типы фильтров:
- Текстовое поле
- Выпадающий список
- Радио-кнопки
- Чекбоксы
Скрипт имеет малый размер и достаточно прост в подключении, а так же неплохо комбинируется со скриптом сортировщиком таблиц скачать его вместе с примерами подключения и настройки можно в моем репозитории: bitbucket.org
Фильтр таблицы демо:
Символы | Текст | Цифры | Цифры | Текст |
---|---|---|---|---|
A B C | - 1 2 3 | |||
B | Арбуз | 2 | 3 | Фанат |
B | Стрелок | 1 | 2 | Арба |
C | Фанат | 3 | 1 | Стрелок |
C | Стрелок | 2 | 1 | Фантомас |
B | Стрелок | 1 | 2 | Арбуз |
C | Фанат | 3 | 3 | Стрелок |
A | Арбуз | 2 | 2 | Арбуз |
A | Фанат | 1 | 1 | Стрелочник |
C | Фанат | 3 | 3 | Арбуз |
B | Фанат | 2 | 3 | Фантик |
C | Стрелок | 1 | 1 | Арбуз |
C | Фанат | 3 | 2 | Стрелка |
Концептуально скрипт состоит из двух частей: объектов-фильтров filterTable.Filter и собственно из функции filterTable( ... ), которая привязывает эти объекты-фильтры к html-таблице.
Объект-фильтр имеет следующий конструктор:
/** * Объект фильтр. * @param HTMLInputElement | HTMLSelect HTMLElementRef | [] - Ссылка, или массив ссылок * на html-элементы, служащие фильтрами. * @param Function callback - ф-ция обратного вызова. Вызывается когда скрипт * производит валидацию содержимого ячейки. Ф-ция вызывается для каждой строки таблицы, для * каждой ячейки столбца, для которого назначен фильтр. * Функции будут переданы 3 параметра: callback(value, filters, i) где: * String value - значение ячейки таблицы, проверяемой на момент вызова ф-ции * HTMLElements[] filters - массив HTML-элементов назначенных фильтрами для проверяемого столбца. * Number i - индекс элемента фильтра в массиве filters который является * валидатором для текущего вызова. Т.е. filters[i] внутри ф-ции * обратного вызова будет содержать элемент, с которым провзаимодействовал * пользователь, в результате чего был запущен процесс валидации. * @param String eventName - название события привязанного к фильтру, по которому будет * запускаться валидация (onkeyup | onclick | onblur | onchange и т.п.) * @constructor */ filterTable.Filter = function (HTMLElementRef, callback, eventName)
- Первый аргумент: HTMLElement HTMLElementRef
-
- ссылка на html-элемент-фильтр, полученный, например при помощи document.getElementById, или массив таких ссылок.
- Второй аргумент: Function callback
-
- функция: callback(value, filters, i) где:
String value - значение ячейки таблицы, проверяемой на момент вызова ф-ции
HTMLElements[] filters - массив HTML-элементов назначенных фильтрами для проверяемого столбца.
Number i - индекс элемента фильтра в массиве filters который является валидатором для текущего вызова. Т.е. filters[i] внутри ф-ции обратного вызова будет содержать элемент, с которым провзаимодействовал пользователь, в результате чего был запущен процесс валидации. Функция должна возвращать true, или false в зависимости от того проходит фильтрацию пришедшее значение value при установленном значении фильтра filters[i] согласно вашей задумке, или нет. - Третий аргумент: String eventName
-
- название события привязанного к фильтру, по которому будет запускаться валидация (onkeyup | onclick | onblur | onchange и т.п.) onchange - значение по-умолчанию
Сама же функция filterTable имеет следующую сигнатуру:
/** * Привязать фильтры к таблице. * @param HTMLTableSectionElement HTMLTBodyRef - ссылка на элемент <tbody> таблицы * @param Object filters - объект-конфигурация фильтров: { N : FILTER[, N : FILTER] } * * Где: * NUM - это натуральное число - номер столбца таблицы, обслуживаемого * фильтром. Этот номер может принимать значения от 0 до кол-во * столбцов таблицы - 1. Номера можно задавать не по порядку. * * FILTER - это ссылка на HTML-элемент представляющий собой элемент * HTML-формы и имеющий атрибут value (select в том числе), либо * объект типа tableKit.Filter */ filterTable(HTMLTBodyRef, aFilters)
Выглядит достаточно запутанно, но давайте разберем на примере. Для начала нам необходим html - каркас таблицы. Заметьте, что фильтры - это просто элементы html-формы они кстати имеют уникальные атрибуты id по которым мы их будем выбирать для передачи в конструктор filterTable.Filter
<table> <thead> <tr> <th>Символы</th> <th>Текст</th> <th>Цифры</th> <th>Цифры</th> <th>Текст</th> </tr> <!-- Это строка содержит фильтры //--> <tr> <td> <input type="checkbox" id="charA" value="A" />A <input type="checkbox" id="charB" value="B" />B <input type="checkbox" id="charC" value="C" />C </td> <td> <input id="text" /> </td> <td> <select id="digits"> <option value="">---</option> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> </td> <td> <input type="radio" name="digit" id="radioZ" value="" checked="true" />- <input type="radio" name="digit" id="radioA" value="1" />1 <input type="radio" name="digit" id="radioB" value="2" />2 <input type="radio" name="digit" id="radioC" value="3" />3 </td> <td> <input id="regexp" /> </td> </tr> </thead> <!-- А вот на этот элемент нам нужно получить ссылку для передачи в filterTable //--> <tbody id="target"> <tr> <td>B</td><td>Арбуз</td><td>2</td><td>3</td><td>Фанат</td> </tr> <tr> <td>B</td><td>Стрелок</td><td>1</td><td>2</td><td>Арба</td> </tr> <tr> <td>C</td><td>Фанат</td><td>3</td><td>1</td><td>Стрелок</td> </tr> <tr> <td>C</td><td>Стрелок</td><td>2</td><td>1</td><td>Фантомас</td> </tr> <tr> <td>B</td><td>Стрелок</td><td>1</td><td>2</td><td>Арбуз</td> </tr> <tr> <td>C</td><td>Фанат</td><td>3</td><td>3</td><td>Стрелок</td> </tr> <tr> <td>A</td><td>Арбуз</td><td>2</td><td>2</td><td>Арбуз</td> </tr> <tr> <td>A</td><td>Фанат</td><td>1</td><td>1</td><td>Стрелочник</td> </tr> <tr> <td>C</td><td>Фанат</td><td>3</td><td>3</td><td>Арбуз</td> </tr> <tr> <td>B</td><td>Фанат</td><td>2</td><td>3</td><td>Фантик</td> </tr> <tr> <td>C</td><td>Стрелок</td><td>1</td><td>1</td><td>Арбуз</td> </tr> <tr> <td>C</td><td>Фанат</td><td>3</td><td>2</td><td>Стрелка</td> </tr> </tbody> </table>
Итак, каркас у нас есть. Фильтры в нём есть. Осталось все это связать воедино. Но давайте для начала рассмотрим простейший вариант подключения, и разберем лишь фильтры для 2-го и 3-го столбцов, потому, как там используются текстовое поле и выпадающий список значений, которые скрипт-фильтр понимает "нативно" без нужды создания filterTable.Filter Я нарочно закомментировал элементы 0, 3, 4 и пока обозначил их реализацию как "..." что бы преждевременно не отпугнуть слабонервных :)
Обратите внимание, что второй аргумент ф-ции filterTable( ..., {...} ) - это объект-конфигурация фильтров, у которого свойства имеют имена-цифры, начиная от 0 и до КОЛ-ВО_СТОЛБЦОВ_ТАБЛИЦЫ-1 Значением каждого такого свойства должен стать фильтр:
filterTable( /* Ссылка на элемент <tbody> таблицы */ document.getElementById("target"), /* Объект-конфигурация фильтров: */ { /* Фильтр для первого столбца чекбоксы: 0: ..., */ /* Фильтр для второго столбца текстовое поле - только точное совпадение: */ 1: document.getElementById("text"), /* Фильтр для третьего столбца выпадающий список: */ 2: document.getElementById("digits"), /* Фильтр для четвертого столбца радио кнопки: 3: ... , /* Фильтр для пятого столбца Постепенный ввод слова: 4: ... */ } );
Если кому то понадобится весь функционал, как показанный в демо примере, например фильтры с радио-кнопками, или фильтры с чекбоксами, то подключение становится немного сложнее потому, как по сути это фильтры, состоящие из набора html-элементов, и при валидации нужно проверять значения всего набора такого фильтра. Тут в действие вступают callback-функции. Ниже в листинге я постарался подробно прокомментировать этот момент.
Опять же обратите внимание, что для текстового поля и выпадающего списка достаточно просто передать ссылку на html-элемент. А так же обратите внимание на подключение последнего фильтра. Вы спросите почему - ведь в последнем столбце фильтр - это текстовое поле! Да это так, но если вы внимательно поработали с демо примером, то заметили, что первый фильтр - тестовое поле срабатывает только после того как вы ввели значение полностью и нажали кнопку "Enter", или кликнули в любое другое место страницы т.е. фильтр срабатывает по дефолтному событию "onchange"! А вот фильтр-текстовое- поле последнего столбца - срабатывает моментально как только вы вводите какой-либо символ. Вот для реализации этого поведения пришлось создавать фильтр по всем правилам с filterTable.Filter а так же понадобилось задавать callback функцию и плюс к этому необходимо было передать имя события "onkeyup" по которому будет инициироваться процесс фильтрации. Вот так. Надеюсь я вас не запутал окончательно.
filterTable( /* Ссылка на элемент <tbody> таблицы */ document.getElementById("target"), /* Объект-конфигурация фильтров: */ { /* Фильтр для первого столбца чекбоксы: */ 0: new filterTable.Filter([ /* Элементы фильтра */ document.getElementById("charA"), document.getElementById("charB"), document.getElementById("charC") ], /* Коллбэк ф-ция валидации */ function (value, filters, i) { /* Если чекбокс не отмечен - его значение не учавствует в валидации поэтому мы обязаны вернуть true */ if (false === filters[i].checked) return true; /* Далее, при проверке, мы должны одновременно проверять значения всех элементов набора при условии чекбокс отмечен */ return filters[0].checked && filters[0].value === value || filters[1].checked && filters[1].value === value || filters[2].checked && filters[2].value === value; } ), /* Фильтр для второго столбца текстовое поле - только точное совпадение: */ 1: document.getElementById("text"), /* Фильтр для третьего столбца выпадающий список: */ 2: document.getElementById("digits"), /* Фильтр для четвертого столбца радио кнопки: */ 3: new filterTable.Filter([/* Элеменеты фильтра */ document.getElementById("radioZ"), document.getElementById("radioA"), document.getElementById("radioB"), document.getElementById("radioC") ], /* Коллбэк ф-ция валидации */ function (value, filters, i) { /* В filters[0] - у нас радио кнопка "Не выбрано", если она установлена фильтр не участвует в валидации и мы обязаны вернуть true */ if (true === filters[0].checked) return true; /* Если какая то радио-кнопка отмечена и содержимое проверяемой ячейки совпало то вернем true */ return filters[1].checked && filters[1].value === value || filters[2].checked && filters[2].value === value || filters[3].checked && filters[3].value === value; } ), /* Фильтр для пятого столбца Постепенный ввод слова: */ 4: new filterTable.Filter(document.getElementById("regexp"), /* Коллбэк ф-ция валидации */ function (value, filters, i) { return value.indexOf(filters[i].value) === 0; }, /* Будем вызывать валидацию по событию onkeyup фильтра */ "onkeyup" ) } );
Собственно все, жду от вас отзывов и предложений и надеюсь вам пригодится мой труд. Ну, и напоследок привожу полный листинг скрипта.
/** * Привязать фильтры к таблице. * @param HTMLTableSectionElement HTMLTBodyRef - ссылка на элемент <tbody> таблицы * @param Object filters - объект-конфигурация фильтров: { N : FILTER[, N : FILTER] } * * Где: * NUM - это натуральное число - номер столбца таблицы, обслуживаемого * фильтром. Этот номер может принимать значения от 0 до кол-во * столбцов таблицы - 1. Номера можно задавать не по порядку. * * FILTER - это ссылка на HTML-элемент представляющий собой элемент * HTML-формы и имеющий атрибут value (select в том числе), либо * объект типа tableKit.Filter */ var filterTable = function (HTMLTBodyRef, aFilters) { var rows = HTMLTBodyRef.getElementsByTagName("TR"), filters = {}, n, walkThrough = function (rows) { var tr, i, f; for (i = 0; i < rows.length; i += 1) { tr = rows.item(i); for(f in filters) { if (filters.hasOwnProperty(f)) { if (false === filters[f].validate(tr.children[f].innerText) ) { tr.style.display = "none"; break; } else { tr.style.display = ""; } } } } }; for(n in aFilters) { if (aFilters.hasOwnProperty(n)) { if (aFilters[n] instanceof filterTable.Filter) { filters[n] = aFilters[n]; } else { filters[n] = new filterTable.Filter(aFilters[n]); } filters[n]._setAction("onchange", function () {walkThrough(rows);}); } } } /** * Объект фильтр. * @param HTMLInputElement | HTMLSelect HTMLElementRef | [] - Ссылка, или массив ссылок * на html-элементы, служащие фильтрами. * @param Function callback - ф-ция обратного вызова. Вызывается когда скрипт * производит валидацию содержимого ячейки. Ф-ция вызывается для каждой строки таблицы, для * каждой ячейки столбца, для которого назначен фильтр. * Функции будут переданы 3 параметра: callback(value, filters, i) где: * String value - значение ячейки таблицы, проверяемой на момент вызова ф-ции * HTMLElements[] filters - массив HTML-элементов назначенных фильтрами для проверяемого столбца. * Number i - индекс элемента фильтра в массиве filters который является * валидатором для текущего вызова. Т.е. filters[i] внутри ф-ции * обратного вызова будет содержать элемент, с которым провзаимодействовал * пользователь, в результате чего был запущен процесс валидации. * @param String eventName - название события привязанного к фильтру, по которому будет * запускаться валидация (onkeyup | onclick | onblur | onchange и т.п.) * @constructor */ filterTable.Filter = function (HTMLElementRef, callback, eventName) { /* Если ф-цию вызвали не как конструктор фиксим этот момент: */ if (!(this instanceof arguments.callee)) { return new arguments.callee(HTMLElementRef, callback, eventName); } /* Выравниваем пришедший аргумент к массиву */ this.filters = {}.toString.call(HTMLElementRef) == "[object Array]" ? HTMLElementRef : [HTMLElementRef]; /** * Шаблонный метод вызывается для каждой строки таблицы, для соответствующей * ячейки. Номер ячейки задается в объекте-конфигурации фильтров ф-ции * filterTable (См. параметр 2 ф-ции tableFilter ) * @param String cellValue - строковое значение ячейки * @returns {boolean} */ this.validate = function (cellValue) { for (var i = 0; i < this.filters.length; i += 1) { if ( false === this.__validate(cellValue, this.filters[i], i) ) { return false; } } } this.__validate = function (cellValue, filter, i) { /* Если фильтр был создан явно и явно указана функция валидации: */ if (typeof callback !== "undefined") { return callback(cellValue, this.filters, i); } /* Если в фильтр напихали пробелов, или другой непечатной фигни - удаляем: */ filter.value = filter.value.replace(/^\s+$/g, ""); /* "Фильтр содержит значение и оно совпало со значением ячейки" */ return !filter.value || filter.value == cellValue; } this._setAction = function (anEventName, callback) { for (var i = 0; i < this.filters.length; i += 1) { this.filters[i][eventName||anEventName] = callback; } } };
Фильтр диапазона значений "ОТ" и "ДО"
По просьбе трудящихся, показываю, как можно реализовать фильтр, который позволит выбирать диапазоны значений цифр "от" и "до". В заголовке столбца, которому требуется этот фильтр пропишем код, который являет собой два выпадающих списка. Соответственно: первый - это значение "от", а второй это значение "до":
<table> <thead> <tr> ... <td> ОТ <select id="digits-from"> <option value="">---</option> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> ДО <select id="digits-to"> <option value="">---</option> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> </td> ... </tr> </thead> <tbody id="target"> ... </tbody> </table>
А в вызове скрипта прописываем фильтр (для третьего столбца) для краткости я сократил листинг оставив только определение фильтра "от" и "до":
filterTable( /* Ссылка на элемент <tbody> таблицы */ document.getElementById("target"), /* Объект-конфигурация фильтров: */ { /* Фильтр для первого столбца чекбоксы: */ 0: ... , /* Фильтр для второго столбца текстовое поле - только точное совпадение: */ 1: ... , /* ФИЛЬТР диапазон значений ОТ и ДО */ 2: new filterTable.Filter([ document.getElementById("digits-from"), document.getElementById("digits-to") ], function (value, filters, i) { var accept = true; value = parseInt(value,10) if (filters[0].value) { accept = (value >= parseInt(filters[0].value,10)); } if (accept && filters[1].value) { accept = (value <= parseInt(filters[1].value,10)); } return accept; } ), /* Фильтр для четвертого столбца радио кнопки: */ 3: ... , /* Фильтр для пятого столбца Постепенный ввод слова: */ 4: ... } );
Примеры фильтров таблиц без учета регистра
Фильтр не чувствительный к регистру для точного совпадения (можно заменять им стандартный)
new filterTable.Filter( document.getElementById("text"), function (value, filters, i) { var empty_filter = filters[i].value == '', match_value = value.toLowerCase() == filters[i].value.toLowerCase(); return empty_filter || match_value; } )
Фильтр не чувствительный к регистру для постепенного ввода слова
new filterTable.Filter(document.getElementById("regexp"), /* Коллбэк ф-ция валидации */ function (value, filters, i) { var c_value = value.toLowerCase(), f_value = filters[i].value.toLowerCase(); return c_value.indexOf(f_value) === 0; }, /* Будем вызывать валидацию по событию onkeyup фильтра */ "onkeyup" )
В принципе не чувствительность к регистру можно встроить и в сам скрипт фильтра, но пока нет времени думаю в будущем переписать этот скрипт с учетом всех ваших пожеланий тогда и сделаем эту фичу там. Как говорится: оставайтесь с нами ;)
Видеохостинг
Rucloud - онлайн видеохостинг для частного и коммерческого использования.
rucloud-streaming.com
Добавить комментарий
Информация копипастерам
Внимание! Копирование контента с сайта, возможно только с разрешения администратора. Т.е. Меня! Я скорее всего разрешу Вам это сделать, в обмен на живую ссылку, на статью оригинал.