#javascript #html
#javascript #HTML
Вопрос:
Для справки, полный код можно получить здесь
У меня есть три элемента выбора, которые должны действовать как фильтры на моей странице, вызывая myfilter
функцию. Затем в таблице я использую data-
атрибут в некоторых <td>
элементах, чтобы «классифицировать» их без необходимости устанавливать разные классы:
<select id="formats" onchange="myfilter('formats', 'mytable')">
<option value="nofilter">--ALL VALUES--</option>
<option value="apple">Apples</option>
<option value="orange">Oranges</option>
<option value="noattr">Unknown</option>
</select>
<table>
<tr>
<td data-format="apple" style="background-color:red;width:10px">amp;nbsp;</td>
<td data-model="film" style="background-color:#3ca971;width:10px">amp;nbsp;</td>
<td data-type="water" style="background-color:#458985;width:10px">amp;nbsp;</td>
<th>1</th>
<td>Michael</td>
<td>New York</td>
<td>M</td>
</tr>
</table>
myfilter
Функция, адаптированная из примера из W3Schools, извлекает значение выбранного списка, определяет, какой столбец он должен искать, и сравнивает пользовательский data-
атрибут со значением, чтобы сохранить строки видимыми или скрыть их:
function myfilter(myelement, reftable) {
var input, filter, table, tr, td, i, j, txtValue, rowstyle, checkvisible;
// Obtains the "value" element of the selected listbox
input = document.getElementById(myelement);
filter = input.value;
table = document.getElementById(reftable);
tr = table.getElementsByTagName("tr");
switch(myelement) {
case "formats":
j = 0;
break;
case "models":
j = 1;
break;
case "types":
j = 2;
}
for (i = 0; i < tr.length; i ) {
td = tr[i].getElementsByTagName("td")[j];
if (td) {
// Retrieves the respective "data-" attribute from <td>
switch(myelement) {
case "formats":
txtValue = td.dataset.format;
break;
case "models":
txtValue = td.dataset.model;
break;
case "types":
txtValue = td.dataset.type;
}
// If there's no style attribute, it's visibleby default
rowstyle = tr[i].getAttribute("style");
if (rowstyle == null) {
checkvisible = true;
}
// If it is hidden, getAttribute returns "display: none;"
else if (rowstyle.includes("none")) {
checkvisible = false;
}
else {
checkvisible = true;
}
// Maintains or hides the row
if (filter == "nofilter") {
tr[i].style.display = "";
}
else if ((txtValue == filter) amp; checkvisible == true) {
tr[i].style.display = "";
}
else {
tr[i].style.display = "none";
}
}
}
}
Проблема
Способ myfilter
работы допускает комбинированный фильтр, но с оговорками:
- Для одного фильтра необходимо выполнить сброс до
--ALL VALUES--
, прежде чем выбирать другой фильтр; - При объединении фильтров приведенное выше правило не позволяет мне динамически переключать значения, пока установлен другой фильтр.
Как я должен изменить функцию JavaScript, чтобы комбинированные фильтры работали должным образом (скажем, как фильтры Excel, где вы сохраняете один фильтр и меняете другой)? Я думал о разделении функции на три части, но в итоге получается та же ситуация.
Ответ №1:
Вы немного усложнили ситуацию, поместив данные в ячейки, а не в строки.
Несмотря на это, удаление onchange
атрибутов из <select>
элементов и использование этого, похоже, работает:
const table = document.getElementById('mytable');
const filters = [
['formats', 'format'],
['models', 'model'],
['types', 'type'],
];
filters.forEach(v => v[0] = document.getElementById(v[0]));
const rows = Array.from(table.querySelectorAll('tr:not(:first-child)')).map(row => {
return [row, filters.map(v => row.querySelector(`td[data-${v[1]}]`).dataset[v[1]])];
});
const changeHandler = event => {
const targets = filters.map(v => v[0].value);
rows.forEach(row => {
row[0].style.display = targets.every(
(v,i) => v == 'nofilter' || v == row[1][i]
) ? '' : 'none';
});
};
filters.forEach(v => {
v[0].addEventListener('change', changeHandler);
});
changeHandler();
filters
представляет собой массив сопоставлений между select
идентификатором и именем данных ячейки. Идентификатор преобразуется в <select>
ссылки на элементы.
rows
это сопоставление между строкой и ее данными. Это делает его более удобным для выполнения фильтрации.
changeHandler
обрабатывает <select>
change
события. Он получает текущие <select>
значения, а затем использует их для определения того, какие строки отображаются.
Затем мы подключаемся changeHandler
к каждому <select>
элементу и запускаем его один раз, чтобы синхронизировать все.
Комментарии:
1. Спасибо, я знаю, что моя реализация не будет оптимальной, я впервые работаю с JS. Просто из любопытства, как бы вы разместили этот фильтр, чтобы сделать его лучше? В итоге я выбрал
data-
атрибуты, потому что мой фильтр должен работать с ячейками без текстового содержимого. Если бы я реализовал это в<tr>
элементах, вы бы по-прежнему рекомендовали использоватьdata-
атрибут?2. Атрибуты данных в порядке. Это зависит от запланированного срока службы таблицы и ее варианта использования. Если данные статичны или будут отображаться только редкие, незначительные изменения, тогда я бы поместил данные в строки (поскольку это то, что фильтруется), и я бы переместил стиль в таблицу стилей, основываясь на данных: например,
#mytable tr[data-format="apple"] td:nth-child(1) {background-color: red;}
. Таким образом, фильтр проще реализовать, а цвета автоматически выводятся из данных, поэтому их легко обновлять. Как новичок, я бы не стал сильно беспокоиться об этом, это то, что придет с практикой и изучением.