-
Notifications
You must be signed in to change notification settings - Fork 0
/
hnl.wp-xmlsearch.mjs
198 lines (171 loc) · 6.59 KB
/
hnl.wp-xmlsearch.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/**
* Simple Fetch-based WP Ajax search
* (c) 2023 HN Leussink / hnldesign
*/
import {hnlLogger} from "./hnl.logger.min.mjs";
export const NAME = 'wpXMLSearch';
let controller, signal;
/**
* Handles search when typing in the input field
* @param {Event} e - The event object
*/
function searchWhenTyping(e) {
const input = e.target;
const search = input.value.trim();
const form = input.form;
if (!search || search.length < 2) {
form.xmlProps.DropdownHasData = false;
form.xmlProps.Dropdown.classList.remove('show');
} else if (!input.searched || input.searched !== search) {
hnlLogger.log(NAME, 'Searching for: ' + search);
clearDropdownButKeepLoader(form.xmlProps.Dropdown);
form.xmlProps.Dropdown.classList.add('show');
input.searched = form.xmlProps.Params.s = search;
// Cancel the previous request
if (controller !== undefined) {
controller.abort();
}
// Feature detect
if ("AbortController" in window) {
controller = new AbortController();
signal = controller.signal;
}
fetch(`${form.xmlProps.Uri}?${new URLSearchParams(form.xmlProps.Params)}`, { method: 'GET', signal })
.then(response => response.json())
.then((jsonData) => {
clearDropdownButKeepLoader(form.xmlProps.Dropdown);
form.xmlProps.Loader.classList.add('d-none');
if (jsonData.length) {
form.xmlProps.DropdownHasData = true;
for (const result of jsonData) {
const { post_title, post_excerpt, guid, post_type } = result;
form.xmlProps.Dropdown.appendChild(addListItem(post_title, post_excerpt, guid, post_type));
}
} else {
const message = `Geen resultaten ${form.xmlProps.CatType}. Probeer een andere zoekterm.`;
form.xmlProps.Dropdown.appendChild(addListItem(message));
form.xmlProps.DropdownHasData = false;
}
})
.catch(error => {
hnlLogger.warn(NAME, error);
clearDropdownButKeepLoader(form.xmlProps.Dropdown);
const message = `Geen resultaten ${form.xmlProps.CatType}. Probeer een andere zoekterm.`;
form.xmlProps.Dropdown.appendChild(addListItem(message));
form.xmlProps.DropdownHasData = false;
});
}
}
/**
* Removes all children from a dropdown except for the loader,
* and shows the loader.
* @param {HTMLElement} dropdownToClear - The dropdown to clear.
*/
function clearDropdownButKeepLoader(dropdownToClear) {
// Get all children of dropdownToClear and filter out the loader.
[...dropdownToClear.children]
.filter(child => !child.classList.contains('loader'))
// Remove each child.
.forEach(child => child.remove());
// Show the loader.
dropdownToClear.querySelector('.loader').classList.remove('d-none');
}
/**
* Creates a new list item element with title, description, link and category badge
* @param {string} title - The title of the list item
* @param {string} description - The description of the list item
* @param {string} link - The link of the list item
* @param {string} category - The category of the list item
* @returns {HTMLElement} - The created list item element
*/
function addListItem(title, description, link, category) {
const listItem = document.createElement('li');
const linkItem = link ? document.createElement('a') : document.createElement('span');
const titleElement = description ? document.createElement('h6') : document.createElement('span');
const descElement = document.createElement('small');
let catBadge = '';
if (category === 'faq_items') {
catBadge = '<span class="badge badge-outlined badge-primary fw-normal me-2">FAQ</span>';
}
titleElement.classList.add('mb-0');
titleElement.textContent = title;
descElement.classList.add('text-muted');
linkItem.appendChild(titleElement);
linkItem.appendChild(descElement);
if (description) {
descElement.innerHTML = catBadge + description;
}
if (link) {
linkItem.href = link.replace("&", "&");
linkItem.setAttribute('title', `${title} - Klik om verder te lezen`);
}
linkItem.classList.add('dropdown-item','overflow-hidden', 'text-truncate');
listItem.appendChild(linkItem);
return listItem;
}
/**
* Initializes the XML search input functionality for each element.
*
* @param {Array} elements - An array of DOM elements to initialize the search input for.
*/
export function init(elements){
elements.forEach(function(element){
const input = element.querySelector('.xml-search-input');
if (!input) {
hnlLogger.error(NAME, 'Form has no search input');
return;
}
//enable search input
input.disabled = false;
// Set initial properties for element
element.xmlProps = {
Dropdown : element.querySelector('.xml-search-autocomplete'),
Loader : element.querySelector('.loader'),
DropdownHasData : false,
Params : {},
Uri : element.dataset.xmlUri,
CatType : (typeof element.dataset.xmlType !== 'undefined') ? (element.dataset.xmlType === 'faq_items' ? ' gevonden in veelgestelde vragen' : '' ) : '',
}
// Extract additional XML parameters from data attributes of element
for (const attr in element.dataset) {
if (attr.includes('xml') && !attr.includes('xmlUri')) {
element.xmlProps.Params[attr.replace('xml' , '').toLowerCase()] = element.dataset[attr];
}
}
// Check if all required properties are set for element
if (Object.values(element.xmlProps).some(v => v === undefined)) {
hnlLogger.error(NAME, 'Not all required settings set');
return;
}
/**
* Hides the dropdown when input loses focus.
*
* @param {Event} e - The event object.
*/
function hideOnBlur(e){
//workaround for onfocusout having no relatedTarget (due to tabindex/focussable issues)
if(!element.contains(e.target)) {
input.removeEventListener('keyup', searchWhenTyping);
document.body.removeEventListener('click', hideOnBlur);
element.xmlProps.Dropdown.classList.remove('show');
}
}
/**
* Triggers a search when the user types in the input field.
*/
input.addEventListener('search', function(e){
const search = input.value.trim();
if (!search) {
//user clears searchbox
element.xmlProps.DropdownHasData = false;
element.xmlProps.Dropdown.classList.remove('show');
clearDropdownButKeepLoader(element.xmlProps.Dropdown);
}
});
input.addEventListener('focusin', function(e){
input.addEventListener('keyup', searchWhenTyping);
document.body.addEventListener('click', hideOnBlur);
element.xmlProps.Dropdown.classList.toggle('show', element.xmlProps.DropdownHasData);
});
})
}