Is there a simple ‘filter all other dropdowns according to the one that triggered the ‘change’ event?
My goal: Build an excel-style ‘Autofilter’ dropdown for each column in a table. Filter each column’s dropdown options by those selected in the other columns.
The table has 3 columns: Category, Subcategory, Product.
I’m struggling to find a simple way to script ‘Filter all other dropdowns by this dropdown’, other than specifically mapping Column 1’s filter conditions to Columns 2 and 3, and likewise mapping Column 2’s filters to Columns 1 and 3 etc. This would have clear scaling issues.
The current approach overview is below (Note how it currently cascades from category => subcategory => product, but I aim to have all dropdowns being able to filter each other)
-
Webpage loads =>
-
populatedropdown() adds event listeners to all checkboxes =>
-
EVENT: a checkbox ‘check’ value is changed =>
4a) call filtersubcategories() to modify available subcategory dropdown options according to category dropdown selections
AND
4b) call filterproducts() to do the same with available product dropdown options
(Disclaimer: yes, I’ve googled around and whilst there’s loads of advice about dropdown filters in JS (using react, for example), 1) it tends to be ‘directional’ (i.e., ‘cascade filters so column 1 filters column 2 but not vice versa). I can’t find anything relating to ensuring that the filtering in one dropdown filters all other available options in the table-if there’s any available materials/libraries to help, I’m all ears!)
I considered pasting the filterSubcategories() and filterProducts() (plus relevant event listeners) as snippets, but I figured there the rest of the script could be useful for
context.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Data Table with Filtering</title>
<style>
/* Style for the table */
table {
border-collapse: collapse;
width: 100%;
}
th, td {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
.filter-dropdown {
position: relative;
display: inline-block;
margin-bottom: 10px;
}
.filter-dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 150px;
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.2);
z-index: 1;
padding: 10px;
}
.filter-dropdown-content label {
display: block;
margin-bottom: 5px;
}
</style>
</head>
<body>
<h2>Data Table with Filtering</h2>
<div>
<input type="text" id="categoryInput" onkeyup="filterTable()" placeholder="Filter by Category..">
<input type="text" id="subcategoryInput" onkeyup="filterTable()" placeholder="Filter by Subcategory..">
<input type="text" id="productInput" onkeyup="filterTable()" placeholder="Filter by Product..">
<button onclick="resetFilters()">Reset Filters</button>
</div>
<table id="myTable">
<thead>
<tr>
<th>
<div class="filter-dropdown" onclick="toggleDropdown('categoryDropdown')">Category ▼</div>
<div class="filter-dropdown-content" id="categoryDropdown"></div>
</th>
<th>
<div class="filter-dropdown" onclick="toggleDropdown('subcategoryDropdown')">Subcategory ▼</div>
<div class="filter-dropdown-content" id="subcategoryDropdown"></div>
</th>
<th>
<div class="filter-dropdown" onclick="toggleDropdown('productDropdown')">Product ▼</div>
<div class="filter-dropdown-content" id="productDropdown"></div>
</th>
</tr>
</thead>
<tbody></tbody>
</table>
<script>
var data = [
{ Category: "Category 1", Subcategory: "Subcategory 1", Product: "Product 1"},
{ Category: "Category 1", Subcategory: "Subcategory 2", Product: "Product 2"},
{ Category: "Category 2", Subcategory: "Subcategory 3", Product: "Product 3"},
{ Category: "Category 2", Subcategory: "Subcategory 1", Product: "Product 4"},
{ Category: "Category 2", Subcategory: "Subcategory 3", Product: "Product 5"},
];
// JavaScript for filtering the table
function filterTable() {
var table, tbody, tr, td, i, txtValue;
var inputs = document.querySelectorAll('input[type="text"]');
var checkboxes = document.querySelectorAll('input[type="checkbox"]:checked');
var filters = Array.from(inputs).map(input => input.value.toUpperCase());
var filterValues = {
category: [],
subcategory: [],
product: []
};
checkboxes.forEach(function(checkbox) {
if (checkbox.classList.contains('categoryFilter')) {
filterValues.category.push(checkbox.value.toUpperCase());
} else if (checkbox.classList.contains('subcategoryFilter')) {
filterValues.subcategory.push(checkbox.value.toUpperCase());
} else if (checkbox.classList.contains('productFilter')) {
filterValues.product.push(checkbox.value.toUpperCase());
}
});
table = document.getElementById("myTable");
tbody = table.getElementsByTagName("tbody")[0];
tbody.innerHTML = ""; // Clear existing table body
data.forEach(function(item) {
if (
(filters[0] === "" || item.Category.toUpperCase().includes(filters[0])) &&
(filters[1] === "" || item.Subcategory.toUpperCase().includes(filters[1])) &&
(filters[2] === "" || item.Product.toUpperCase().includes(filters[2])) &&
(filterValues.category.length === 0 || filterValues.category.includes(item.Category.toUpperCase())) &&
(filterValues.subcategory.length === 0 || filterValues.subcategory.includes(item.Subcategory.toUpperCase())) &&
(filterValues.product.length === 0 || filterValues.product.includes(item.Product.toUpperCase()))
) {
var row = document.createElement("tr");
Object.values(item).forEach(function(value) {
var cell = document.createElement("td");
cell.appendChild(document.createTextNode(value));
row.appendChild(cell);
});
tbody.appendChild(row);
}
});
}
// JavaScript for resetting filters
function resetFilters() {
var inputs = document.querySelectorAll('input[type="text"]');
inputs.forEach(input => input.value = "");
var checkboxes = document.querySelectorAll('input[type="checkbox"]:checked');
checkboxes.forEach(checkbox => checkbox.checked = false);
filterTable(); // Reset the table to its initial state
}
// JavaScript for dropdown filters
function toggleDropdown(dropdownId) {
var dropdown = document.getElementById(dropdownId);
dropdown.style.display = (dropdown.style.display === 'block') ? 'none' : 'block';
}
// Function to get unique values from an array
function getUniqueValues(data, key) {
return Array.from(new Set(data.map(item => item[key])));
}
// Populate table on page load
window.onload = function() {
filterTable();
};
// Populate dropdown options
function populateDropdown(dropdownId, options) {
var dropdownContent = document.getElementById(dropdownId);
dropdownContent.innerHTML = "";
options.forEach(function(option) {
var label = document.createElement("label");
var checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.value = option;
checkbox.className = dropdownId === "categoryDropdown" ? "categoryFilter" :
dropdownId === "subcategoryDropdown" ? "subcategoryFilter" : "productFilter";
checkbox.addEventListener("change", function() {
filterTable();
if (dropdownId === "categoryDropdown") {
filterSubcategories();
filterProducts();
} else if (dropdownId === "subcategoryDropdown") {
filterProducts();
}
});
label.appendChild(checkbox);
label.appendChild(document.createTextNode(option));
dropdownContent.appendChild(label);
});
}
// Populate dropdowns on page load
window.onload = function() {
var categoryOptions = getUniqueValues(data, "Category").sort();
var subcategoryOptions = getUniqueValues(data, "Subcategory").sort();
var productOptions = getUniqueValues(data, "Product").sort();
populateDropdown("categoryDropdown", categoryOptions);
populateDropdown("subcategoryDropdown", subcategoryOptions);
populateDropdown("productDropdown", productOptions);
filterTable(); // Populate the table
};
// Filter subcategories based on selected categories
function filterSubcategories() {
var categoryCheckboxes = document.querySelectorAll('.categoryFilter:checked');
var subcategoryCheckboxes = document.querySelectorAll('.subcategoryFilter');
subcategoryCheckboxes.forEach(function(checkbox) {
// checkbox.disabled = true;
checkbox.parentNode.style.display = "none";
});
categoryCheckboxes.forEach(function(checkbox) {
var subcategoryOptions = data.filter(function(item) {
return item.Category === checkbox.value;
}).map(function(item) {
return item.Subcategory;
});
subcategoryCheckboxes.forEach(function(subCheckbox) {
if (subcategoryOptions.includes(subCheckbox.value)) {
subCheckbox.parentNode.style.display = "block";
} else {
subCheckbox.parentNode.display = "none";
subCheckbox.checked = false;
}
});
});
}
// Filter products based on selected subcategories
function filterProducts() {
var categoryCheckboxes = document.querySelectorAll('.categoryFilter:checked');
var subcategoryCheckboxes = document.querySelectorAll('.subcategoryFilter:checked');
var productCheckboxes = document.querySelectorAll('.productFilter');
productCheckboxes.forEach(function(checkbox) {
checkbox.parentNode.style.display = "none";
// checkbox.display = false
});
subcategoryCheckboxes.forEach(function(checkbox) {
var productOptions = data.filter(function(item) {
return item.Subcategory === checkbox.value;
}).map(function(item) {
return item.Product;
});
productCheckboxes.forEach(function(prodCheckbox) {
if (productOptions.includes(prodCheckbox.value)) {
prodCheckbox.parentNode.style.display = "block";
} else {
prodCheckbox.parentNode.style.display = "none";
prodCheckbox.checked = false;
}
});
});
}
</script>
</body>
</html>