TableSorter + Multiple Select Filter

TableSorter + Multiple Select Filter

iSergium

Для начала краткая теория.

TableSorter - jQuery-плагин для работы с HTML-таблицами. Поддерживает дополнительные виджеты.

Filter Widget - один из этих виджетов. Позволяет фильтровать выводимые в таблице данные. Поддерживает немало способов фильтрации, пример в демо.

Multiple Select - jQuery-плагин, позволяющий создавать select из чекбоксов.

Задачка

При всей своей крутости TableSorter не может предложить фильтр, в котором можно было бы выбрать несколько значений. Использование диапазонов значений, фильтров "до" и "от" - это не устраивающий нас вариант. Выход из ситуации напрашивается сам - писать необходимый фильтр придётся самому.

Путь к решению

Простейший фильтр можно задать, прописав в заголовке столбца необходимый класс. Например выпадающий список можно получить так:

<th class="filter-select">User</th>

Идея 1. Фильтр задать выпадающим списком, затем для полученного select вызвать multipleSelect()
Грабли 1. Если в th задан class="filter-select", то фильтр в столбце всегда будет селектом. С другими типами аналогично.
Вывод 1. Классы в th не задавать.

Все незаданные классом фильтры можно задать в filter_formatter, например стандартный range из jQuery UI (требуется widget-filter-formatter-jui.js) прописывается так:

$table.tablesorter({
    widgetOptions : {
        filter_formatter: {
            0: function($cell, indx) {
                return $.tablesorter.filterFormatter.uiRange($cell, indx);
            }
        }
    }
});

Вывод 2. Необходимо написать нечто аналогичное и подключить так же:

1: function($cell, indx) {
    return multipleSelectFilter($cell, indx);
}

Пришло время творить функцию multipleSelectFilter.

var multipleSelectFilter = function($cell, indx) {}

Параметры:

  • $cell - ячейка в строке фильтров, которая содержит нужный фильтр
  • indx - порядковый номер столбца, в данном примере 1

По умолчанию фильтр задаётся простым полем ввода, которое поддерживает некоторые простые команды.

Идея 2. Добавить в $cell input[type=hidden], с помощью которого будет управляться фильтр, и select, который будет визуальной частью и к которому будет применён плагин MultipleSelect.

Добавить элементы в ячейку - несложно. Заставить кого-нибудь из них работать фильтром - описано в документации. Узнать все доступные для фильтра значения парой стандартных команд - вот это уже не получится. Благо, известен номер столбца indx, по которому есть возможность узнать содержимое ячеек необходимого столбца.

var $tds = $cell.closest('table').find('tbody').find('tr').find('td:nth(' + indx + ')'),
    values = $tds.map(function(){ return $(this).text(); }).get().sort();

Список значений почти готов, нужно лишь избавиться от повторов. Далее эти значения нужно прописать в select, к которому применить плагин MultipleSelect. При изменении значения в нём нужно изменять значение в $input, по которому и будет ориентироваться фильтр столбца.

Плагин MultipleSelect тоже неплохо расширяемый и тоже имеет хорошую документацию, поэтому найти нужное событие для колбэка не составляет труда.

В итоге получается нечто такое:

var multipleSelectFilter = function($cell, indx) {
 
    // те самые input и select
    var $input = $('<input type="hidden">').appendTo($cell),
        $select = $('<select />').attr('multiple', 'multiple');
 
    // получение значений для списка
    var $tds = $cell.closest('table').find('tbody').find('tr').find('td:nth(' + indx + ')'),
        values = $tds.map(function(){ return $(this).text(); }).get().sort(),
        options = [];
    // заполнение селекта с защитой от повторов
    for (var i in values) {
        if (values[i] && (options.indexOf(values[i]) == -1)) {
            options.push(values[i]);
            var $option = $('<option value="' + values[i] + '">').text(values[i]);
            $select.append($option);
        }
    }
    $select.appendTo($cell);
 
    // callback для multipleSelect
    $select.multipleSelect({
        selectAll: false,
        onClick: function() {
            var value = $select.val() ? $select.val().join('|') : '';
            $input.val( value );
            $input.trigger('search');
        }
    });
    return $input;
};

Изменение стилей выходит за рамки вопроса.

Для удобства выложено демо на jsfiddle. Внешние ресурсы там тоже заслуживают внимание (External Resources в меню слева). Задание на закрепление: сделать мультиселект-фильтр для столбца "User".

Дополнительно: