Hi I've got a table that I wanted to filter using a custom JavaScript dropdown filter function. Separately they work perfectly, but the issue comes in when I try to use both at the same time to filter the table contents. Here is my JavaScript code:
function searchUserName(input, tableName) {
dropDown(input, tableName, 0);
}
function searchSupervisorName(input, tableName) {
dropDown(input, tableName, 1);
}
var dropdown0 = false;
var dropdown1 = false;
function dropDown(input, tableName, colPos) {
var table = document.getElementById(tableName);
var tr = table.getElementsByTagName("tr");
var userDropdown;
var userNamesArray = [];
if (colPos == 0) {
if (!dropdown0) {
createDropdown();
dropdown0 = true;
}
}
else if (colPos == 1) {
if (!dropdown1) {
createDropdown();
dropdown1 = true;
}
}
function createDropdown() {
if (!userDropdown) {
userDropdown = document.createElement("div");
userDropdown.id = "userDropdown";
userDropdown.className = "dropdown";
userNamesArray = Array.from(tr).map(row => {
var td = row.getElementsByTagName("td")[colPos];
if (td) {
return td.textContent.trim();
}
}).filter(Boolean);
// Sort the user names alphabetically
userNamesArray.sort((a, b) => a.localeCompare(b));
var userNamesSet = new Set(userNamesArray);
// Add "Deselect All" option
var deselectAllContainer = createOptionContainer("Deselect All");
userDropdown.appendChild(deselectAllContainer);
// Add separator line after "Deselect All"
var separator = document.createElement("hr");
userDropdown.appendChild(separator);
userNamesSet.forEach(name => {
var optionContainer = createOptionContainer(name);
userDropdown.appendChild(optionContainer);
});
input.parentNode.insertBefore(userDropdown, input.nextSibling);
} else {
userDropdown.style.display = '';
}
removeOptionFromDropdown("null, null");
}
function removeOptionFromDropdown(optionText) {
if (userDropdown) {
Array.from(userDropdown.children).forEach(optionContainer => {
var optionContainerText = optionContainer.textContent.trim();
if (optionContainerText === optionText) {
userDropdown.removeChild(optionContainer);
}
});
}
}
function createOptionContainer(name) {
var optionContainer = document.createElement("label");
optionContainer.className = "option-container";
if (name === "Deselect All") {
var checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.value = name;
optionContainer.appendChild(checkbox);
optionContainer.classList.add("deselect-option");
} else {
var checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.value = name;
optionContainer.appendChild(checkbox);
}
var optionText = document.createTextNode(name);
optionContainer.appendChild(optionText);
return optionContainer;
}
//showing dropdown onclick event
function showDropdown() {
if (userDropdown) {
userDropdown.style.display = 'block';
}
}
function moveSelectedOptionsToTop() {
var deselectAllContainer = userDropdown.querySelector('input[value="Deselect All"]').parentNode;
var selectedOptions = Array.from(userDropdown.getElementsByClassName("option-container"))
.filter(optionContainer => {
var checkbox = optionContainer.querySelector('input[type="checkbox"]');
return checkbox.checked && checkbox.value !== "Deselect All";
});
if (selectedOptions.length > 0) {
// Sort selected options alphabetically
selectedOptions.sort((a, b) => {
var textA = a.textContent.trim();
var textB = b.textContent.trim();
return textB.localeCompare(textA);
});
// Move all selected options to the top
selectedOptions.forEach(optionContainer => {
userDropdown.insertBefore(optionContainer, userDropdown.firstChild.nextSibling); // Insert after "Deselect All"
});
} else {
// If no options are selected, move "Deselect All" to the top
userDropdown.insertBefore(deselectAllContainer, userDropdown.firstChild.nextSibling);
var allOptions = Array.from(userDropdown.getElementsByClassName("option-container"));
allOptions = allOptions.filter(optionContainer => {
var checkbox = optionContainer.querySelector('input[type="checkbox"]');
return checkbox.value !== "Deselect All";
});
allOptions.sort((a, b) => {
var textA = a.textContent.trim();
var textB = b.textContent.trim();
return textB.localeCompare(textA);
});
// Move all options below "Deselect All"
allOptions.forEach(optionContainer => {
userDropdown.insertBefore(optionContainer, deselectAllContainer.nextSibling);
});
}
}
function filterTable() {
var visibleRows = [];
var searchValue = input.value.trim().toUpperCase();
var selectedusers = Array.from(userDropdown.getElementsByClassName("option-container"))
.filter(optionContainer => {
var checkbox = optionContainer.querySelector('input[type="checkbox"]');
return checkbox.checked && checkbox.value !== "Deselect All";
})
.map(optionContainer => optionContainer.textContent.trim().toUpperCase());
// Include all options if "Deselect All" is checked
var deselectAllCheckbox = userDropdown.querySelector('input[value="Deselect All"]');
if (deselectAllCheckbox && deselectAllCheckbox.checked) {
// Uncheck all other checkboxes and remove selected options
Array.from(userDropdown.getElementsByClassName("option-container")).forEach(function (optionContainer) {
var checkbox = optionContainer.querySelector('input[type="checkbox"]');
checkbox.checked = false;
});
var deselectAllContainer = userDropdown.querySelector('input[value="Deselect All"]').parentNode;
userDropdown.removeChild(deselectAllContainer);
userDropdown.insertBefore(deselectAllContainer, userDropdown.firstChild.nextSibling); // Insert after the separator line
selectedusers = [];
}
// Move "Deselect All" to the top after checking selected options
moveSelectedOptionsToTop();
Array.from(tr).forEach(function (row) {
var td = row.getElementsByTagName("td")[colPos];
if (td) {
var userName = td.textContent.trim().toUpperCase();
var matchesSearch = userName.includes(searchValue);
var isInSelectedusers = selectedusers.length === 0 || selectedusers.includes(userName);
var displayRow = matchesSearch && isInSelectedusers;
row.style.display = displayRow ? "" : "none";
var hasVisibleRowClass = row.classList.contains('visible-row-dp1');
if (colPos == 0) {
if (displayRow && hasVisibleRowClass) {
row.classList.add('visible-row-dp0');
visibleRows.push(row);
} else {
row.classList.remove('visible-row-dp0');
}
}
else if (colPos == 1) {
if (displayRow) {
row.classList.add('visible-row-dp1');
visibleRows.push(row);
} else {
row.classList.remove('visible-row-dp1');
}
}
applyEvenOddStyling(visibleRows);
}
});
// Display "No matching records found" message if no visible rows
var dataTable = $('#maintable').DataTable();
if (visibleRows.length === 0) {
dataTable.rows().nodes().to$().hide(); // Hide all rows
dataTable.draw(); // Redraw the DataTable
$('#maintable tbody').append('<tr class="no-records"><td colspan="colspan">No matching records found</td></tr>'); // Display no records message
} else {
// If there are visible rows, redraw the DataTable and remove the no records message if present
dataTable.rows().nodes().to$().show(); // Show all rows
$('#maintable tbody .no-records').remove(); // Remove no records message if present
dataTable.draw();
}
Array.from(userDropdown.getElementsByClassName("option-container")).forEach(function (optionContainer) {
var optionText = optionContainer.textContent.trim().toUpperCase();
var displayOption = optionText.includes(searchValue);
optionContainer.style.display = displayOption ? "block" : "none";
});
if (colPos == 1) {
var SuperVisorSearchbar = document.getElementById("searchSupervisorInput");
if (selectedusers.length > 0) {
SuperVisorSearchbar.classList.add("selected-search-bar");
} else {
SuperVisorSearchbar.classList.remove("selected-search-bar");
}
} else if (colPos == 0) {
var UserSearchbar = document.getElementById("searchInput");
if (selectedusers.length > 0) {
UserSearchbar.classList.add("selected-search-bar");
} else {
UserSearchbar.classList.remove("selected-search-bar");
}
}
}
function applyEvenOddStyling(rows) {
rows.forEach(function (row, index) {
if (index % 2 === 0) {
row.classList.add('odd-row');
row.classList.remove('even-row');
} else {
row.classList.add('even-row');
row.classList.remove('odd-row');
}
});
}
userDropdown.addEventListener('change', function () {
filterTable();
moveSelectedOptionsToTop();
});
input.addEventListener("keyup", function () {
filterTable();
});
input.addEventListener("click", function () {
showDropdown();
});
document.addEventListener("click", function (event) {
if (userDropdown && !userDropdown.contains(event.target) && event.target !== input) {
userDropdown.style.display = 'none';
}
});
}
Since there can be multiple rows of a single supervisor, the supervisor dropdown will filter the table and the rows that are visible will be dynamically picked up by the username dropdown. Then, I can filter the username. The options in both dropdowns would also be multiselect and the table would be filtered accordingly. I also have an option open where the User can type out and filter either dropdown options and the separate columns they are in.
I will admit I used a lot of AI to help fix some bugs I had, so I might not be able to explain reasoning for everything but I'll try my best if you have any questions.
I haven't read through all the filtering logic. I also don't know what the table data is.
To filter an array, you could use the filter method on the array object.
Say you have an array of students;
To get only male students, you would filter the students array like so:
To filter the male students further say only male students less than the age of 26:
Or you could apply the sex and age filter at once on the students array:
You could then display the filtered array in your table.