STEP 1
Making Search and Filter Work Together
Right now, search and filter are two separate listeners that each set card.style.display independently. That means if a user filters to "Food" and then types "wellness", the search listener will show the wellness card even though food is still the active filter.
The fix is to combine both conditions into a single function and call that function from both event listeners. One function, two triggers. The card is visible only when it matches the active category AND the search text.
STEP 2
Add the No-Results CSS
๐ Add to CSS:
.no-results {
display: none;
text-align: center;
padding: 60px 0;
color: #6b7280;
}
.no-results-icon { font-size: 2.5rem; display: block; margin-bottom: 12px; }
.no-results p { font-size: 1rem; }
STEP 3
Add the No-Results HTML
After the .biz-grid closing tag, add:
๐ Add after .biz-grid:
<div class="no-results" id="noResults">
<span class="no-results-icon">๐ฑ</span>
<p>No businesses match your search. Try a different term or category.</p>
</div>
STEP 4
Replace the JavaScript
Replace your existing <script> block with this combined version:
๐ Replace your <script> block:
<script>
const filterBtns = document.querySelectorAll('.filter-btn');
const cards = document.querySelectorAll('.biz-card');
const searchInput = document.getElementById('searchInput');
const noResults = document.getElementById('noResults');
let activeFilter = 'all';
function applyFilters() {
var query = searchInput.value.toLowerCase().trim();
var visibleCount = 0;
cards.forEach(function(card) {
var matchesCategory = activeFilter === 'all' || card.dataset.category === activeFilter;
var matchesSearch = query === '' || card.textContent.toLowerCase().includes(query);
if (matchesCategory && matchesSearch) {
card.style.display = '';
visibleCount++;
} else {
card.style.display = 'none';
}
});
noResults.style.display = visibleCount === 0 ? 'block' : 'none';
}
filterBtns.forEach(function(btn) {
btn.addEventListener('click', function() {
filterBtns.forEach(function(b) { b.classList.remove('active'); });
btn.classList.add('active');
activeFilter = btn.dataset.filter;
applyFilters();
});
});
searchInput.addEventListener('input', applyFilters);
</script>
The key change: activeFilter is a variable that stores the current filter state between events. Each time either the filter button or the search input changes, applyFilters() runs using the current value of both. The no-results block appears when visibleCount hits zero.
Unit 23 Complete
The Native Business Directory is done. You built a searchable, filterable directory from scratch: design system, business card grid, category filter, live search, featured spotlight, about section, and a combined search-filter function that handles edge cases with a no-results state.
The applyFilters pattern you wrote here powers every real search-and-filter UI on the web. It is one of the most reusable patterns in front-end development.