@@ -41,13 +41,36 @@ function fetchSearchResults(query) {
41
41
}
42
42
43
43
/**
44
- * Removes any current search results from the display.
45
- * @returns {void }
44
+ * Clears the search results from the display.
45
+ * If the removeEventListener flag is true, removes the click event listener from the document.
46
+ * @param {boolean } [removeEventListener=false] - Optional flag to indicate if the click event listener should be removed. Default is false.
47
+ * @returns {void } - This function doesn't return anything.
46
48
*/
47
- function clearSearchResults ( ) {
48
- while ( resultsElement . firstChild ) {
49
- resultsElement . removeChild ( resultsElement . firstChild ) ;
49
+ function clearSearchResults ( removeEventListener = false ) {
50
+ resultsElement . innerHTML = "" ;
51
+ if ( removeEventListener && document . clickEventAdded ) {
52
+ document . removeEventListener ( 'click' , handleDocumentClick ) ;
53
+ document . clickEventAdded = false ;
50
54
}
55
+ }
56
+
57
+ /**
58
+ * Displays a "No results found" message in both the live region and results display area.
59
+ * This is typically used when no matching results are found in the search.
60
+ * @returns {void } - This function doesn't return anything.
61
+ */
62
+ function showNoResults ( ) {
63
+ resultsLiveRegion . innerHTML = "No results found." ;
64
+ resultsElement . innerHTML = "No results found." ;
65
+ resultsElement . setAttribute ( 'data-results' , 'false' ) ;
66
+ }
67
+
68
+ /**
69
+ * Clears any "No results found" message from the live region and results display area.
70
+ * @returns {void } - This function doesn't return anything.
71
+ */
72
+ function clearNoResults ( ) {
73
+ resultsLiveRegion . innerHTML = "" ;
51
74
resultsElement . innerHTML = "" ;
52
75
}
53
76
@@ -81,9 +104,7 @@ function displaySearchResults(results) {
81
104
}
82
105
83
106
} else {
84
- resultsLiveRegion . innerHTML = "No results found." ;
85
- resultsElement . innerHTML = "No results found." ;
86
- resultsElement . setAttribute ( 'data-results' , 'false' ) ;
107
+ showNoResults ( ) ;
87
108
}
88
109
89
110
}
@@ -125,12 +146,33 @@ function debounce(callback, delay) {
125
146
}
126
147
}
127
148
149
+ /**
150
+ * Debounced function to fetch search results after 300ms of inactivity.
151
+ * Calls `fetchSearchResults` to retrieve data and `displaySearchResults` to show them.
152
+ * If an error occurs, clears the search results.
153
+ * @param {string } query - The search query.
154
+ * @returns {void } - No return value.
155
+ * @see debounce - Limits the number of requests during rapid typing.
156
+ */
128
157
const debouncedFetchSearchResults = debounce ( ( query ) => {
129
158
fetchSearchResults ( query )
130
159
. then ( displaySearchResults )
131
- . catch ( clearSearchResults ) ;
160
+ . catch ( ( ) => { clearSearchResults ( true ) } ) ;
132
161
} , 300 ) ;
133
162
163
+
164
+ /**
165
+ * Handles the document click event to clear search results if the user clicks outside of the search input or results element.
166
+ * @param {MouseEvent } e - The event object representing the click event.
167
+ * @returns {void } - This function does not return any value. It directly interacts with the UI by clearing search results.
168
+ */
169
+ const handleDocumentClick = ( e ) => {
170
+ if ( e . target !== resultsElement && e . target !== searchInput ) {
171
+ clearSearchResults ( true ) ;
172
+ }
173
+ }
174
+
175
+
134
176
//-----------------------------------------------------------------------------
135
177
// Event Handlers
136
178
//-----------------------------------------------------------------------------
@@ -146,14 +188,13 @@ if (searchInput)
146
188
else searchClearBtn . setAttribute ( 'hidden' , '' ) ;
147
189
148
190
if ( query . length > 2 ) {
149
-
150
191
debouncedFetchSearchResults ( query ) ;
151
-
152
- document . addEventListener ( 'click' , function ( e ) {
153
- if ( e . target !== resultsElement ) clearSearchResults ( ) ;
154
- } ) ;
192
+ if ( ! document . clickEventAdded ) {
193
+ document . addEventListener ( 'click' , handleDocumentClick ) ;
194
+ document . clickEventAdded = true ;
195
+ }
155
196
} else {
156
- clearSearchResults ( ) ;
197
+ clearSearchResults ( true ) ;
157
198
}
158
199
159
200
searchQuery = query
@@ -165,19 +206,24 @@ if (searchClearBtn)
165
206
searchClearBtn . addEventListener ( 'click' , function ( e ) {
166
207
searchInput . value = '' ;
167
208
searchInput . focus ( ) ;
168
- clearSearchResults ( ) ;
209
+ clearSearchResults ( true ) ;
169
210
searchClearBtn . setAttribute ( 'hidden' , '' ) ;
170
211
} ) ;
171
212
172
213
document . addEventListener ( 'keydown' , function ( e ) {
173
214
174
215
const searchResults = Array . from ( document . querySelectorAll ( '.search-results__item' ) ) ;
175
216
176
- if ( e . key === ' Escape' ) {
217
+ if ( e . key === " Escape" ) {
177
218
e . preventDefault ( ) ;
178
219
if ( searchResults . length ) {
179
- clearSearchResults ( ) ;
220
+ clearSearchResults ( true ) ;
180
221
searchInput . focus ( ) ;
222
+ } else if (
223
+ document . activeElement === searchInput
224
+ ) {
225
+ clearNoResults ( ) ;
226
+ searchInput . blur ( ) ;
181
227
}
182
228
}
183
229
0 commit comments