Real-time Search
Relevant source files
The following files were used as context for generating this wiki page:
The Real-time Search feature is a core component of the Norm Macdonald Archive redesign prototype, providing users with an enhanced, interactive way to browse content. It allows for instant filtering of jokes and videos as the user types a query or selects a filter option. This functionality is implemented entirely on the client-side using vanilla JavaScript, ensuring a fast and responsive user experience without requiring server-side requests for each search action.
The search is implemented across multiple sections of the site, including the "Jokes" and "Videos" pages. It supports text-based searching on various data fields and, in the case of jokes, filtering by guest. The system is designed to be efficient by loading all data once and performing filtering operations on the in-memory dataset.
Sources: redesign/README.md, redesign/script.js, redesign/videos.js
Architecture
The search architecture is a client-side pattern. On page load, the complete dataset (either jokes from jokes-data.json or videos from consolidated_youtube_data.json) is fetched and stored in a global state object. All subsequent search and filter operations are performed on this in-memory array. This approach minimizes network latency and provides instantaneous feedback to the user.
The general data flow is as follows:
- Initialization: The page loads, and a JavaScript module fetches the relevant JSON data.
- State Population: The fetched data is stored in a
stateobject, which also tracks the search term, filter selections, and pagination. - User Interaction: The user types into a search input or selects an option from a filter dropdown.
- Event Listening: An event listener (
inputorchange) captures the user's action. - State Update: The
stateobject is updated with the new search term or filter value. - Filtering: The
applyFiltersfunction iterates through the master list of data, creating a newfilteredarray based on the current state. - Rendering: The
renderfunction clears the current view and displays only the items from thefilteredarray, updating result counts and pagination controls.
Sources: redesign/script.js:51-62, redesign/videos.js:40-58, README.md
This diagram illustrates the client-side search and rendering flow.
State Management
A central state object is used in both script.js (for jokes) and videos.js (for videos) to manage the application's data and UI status. This object holds the master list of items, the filtered list, pagination details, and current search/filter values.
State Object Properties
| Property | Type | File | Description |
|---|---|---|---|
jokes / videos | Array | script.js, videos.js | The full, unfiltered array of all items loaded from the JSON file. |
filteredJokes / filteredVideos | Array | script.js, videos.js | The array of items that match the current search and filter criteria. |
currentPage | Number | script.js, videos.js | The current page number for pagination. Default is 1. |
jokesPerPage / videosPerPage | Number | script.js, videos.js | The number of items to display per page. |
searchTerm | String | script.js, videos.js | The current text value from the search input. |
selectedGuest | String | script.js | The value of the selected guest from the filter dropdown (Jokes page only). |
currentRandomJoke | Object | script.js | Stores the joke object currently displayed in the random joke modal. |
Sources: redesign/script.js:5-14, redesign/videos.js:5-12
Example State Object
Here is the initial structure of the state object for the jokes page.
// redesign/script.js:5-14
const state = {
jokes: [],
filteredJokes: [],
currentPage: 1,
jokesPerPage: 10,
searchTerm: '',
selectedGuest: '',
currentRandomJoke: null
};
Sources: redesign/script.js:5-14
UI Components
The search functionality relies on several key HTML elements defined in jokes.html and videos.html.
| Element ID / Class | File | Description |
|---|---|---|
#search-input | jokes.html, videos.html | The primary text input field where users type their search query. |
#clear-search | jokes.html, videos.html | A button that appears when the search input has text, allowing the user to clear the query. |
#guest-filter | jokes.html | A <select> dropdown populated with all available guests for filtering jokes. |
#jokes-container / #videos-container | jokes.html, videos.html | The main <div> element where the filtered result cards are dynamically rendered. |
#filtered-count | jokes.html, videos.html | A <strong> element that displays the number of currently visible results. |
#empty-state | jokes.html, videos.html | A <div> that is shown when a search yields no results. |
#pagination | jokes.html, videos.html | A <section> where pagination buttons are dynamically generated. |
Sources: redesign/jokes.html:92-149, redesign/videos.html:75-131
Event Handling and Data Flow
User interactions are captured by event listeners, which trigger the filtering and re-rendering process. The primary event is the input event on the search field, which ensures the search happens in real-time as the user types.
This sequence diagram shows the flow from user input to DOM update.
The core event listener for the search input is set up in initEventListeners.
// redesign/script.js:478-482
searchInput.addEventListener('input', (e) => {
state.searchTerm = e.target.value;
clearBtn.style.display = state.searchTerm ? 'block' : 'none';
applyFilters();
});
Sources: redesign/script.js:478-482
Filtering Logic
The applyFilters function is the heart of the search feature. It filters the master data array based on the searchTerm and any other selected filters.
Jokes Filtering
For jokes, the search is performed against the joke text, guest name, and episode title. The search is case-insensitive.
// redesign/script.js:90-97
function applyFilters() {
state.filteredJokes = state.jokes.filter(joke => {
const matchesSearch = joke.joke.toLowerCase().includes(state.searchTerm.toLowerCase()) ||
joke.guest?.toLowerCase().includes(state.searchTerm.toLowerCase()) ||
joke.episode?.toLowerCase().includes(state.searchTerm.toLowerCase());
const matchesGuest = !state.selectedGuest || joke.guest === state.selectedGuest;
return matchesSearch && matchesGuest;
});
// ...
}
Sources: redesign/script.js:90-97
Videos Filtering
For videos, the search is simpler, matching against the video title and description.
// redesign/videos.js:88-92
function applyFilters() {
state.filteredVideos = state.videos.filter(video => {
const matchesSearch = video.title.toLowerCase().includes(state.searchTerm.toLowerCase()) ||
video.description.toLowerCase().includes(state.searchTerm.toLowerCase());
return matchesSearch;
});
// ...
}
Sources: redesign/videos.js:88-92
After filtering, applyFilters resets the current page to 1 and calls the rendering functions to update the UI.
Rendering and Pagination
The renderJokes and renderVideos functions are responsible for updating the DOM. They calculate which subset of the filteredJokes/filteredVideos array to display based on the current page and items per page. If the filtered array is empty, an "empty state" message is displayed. Otherwise, it dynamically creates and appends content cards to the container.
Finally, the renderPagination function is called to generate the necessary pagination controls (Previous, Next, and page number buttons).
Sources: redesign/script.js:107-140, redesign/videos.js:102-135
Conclusion
The Real-time Search feature in the norm repository is a robust, client-side implementation that provides a fast and seamless user experience. By loading data upfront and performing all filtering in the browser, it avoids network latency and delivers instant results. The architecture is cleanly separated into state management, event handling, filtering logic, and rendering, making it a reusable and maintainable pattern that is effectively applied to both the jokes and videos sections of the archive.
Sources: README.md, redesign/README.md