diff --git a/src/lib/components/SearchWrapper.js b/src/lib/components/SearchWrapper.js index 217c645..4e4378e 100644 --- a/src/lib/components/SearchWrapper.js +++ b/src/lib/components/SearchWrapper.js @@ -13,6 +13,8 @@ export class SearchWrapper extends BaseComponent { #arePropertiesSuggestionsEnabled = false; /** @type {"start"|"end"} */ #propertiesSuggestionsPosition = "start"; + /** @type {HTMLElement|null} */ + #cachedAutocompleteContainer = null; build() { this.#searchField = this.container.querySelector('input[name=q]'); @@ -113,6 +115,24 @@ export class SearchWrapper extends BaseComponent { return searchValue; } + /** + * Resolve the autocomplete container from the document. Once resolved, it can be safely reused without breaking + * anything. Assuming refactored autocomplete handler is still implemented the way it is. + * + * This means, that properties will only be suggested once actual autocomplete logic was activated. + * + * @return {HTMLElement|null} Resolved element or nothing. + */ + #resolveAutocompleteContainer() { + if (this.#cachedAutocompleteContainer) { + return this.#cachedAutocompleteContainer; + } + + this.#cachedAutocompleteContainer = document.querySelector('.autocomplete'); + + return this.#cachedAutocompleteContainer; + } + /** * Render the list of suggestions into the existing popup or create and populate a new one. * @param {string[]} suggestions List of suggestion to render the popup from. @@ -124,9 +144,21 @@ export class SearchWrapper extends BaseComponent { .map(suggestedTerm => SearchWrapper.#renderTermSuggestion(suggestedTerm)); requestAnimationFrame(() => { - const autocompleteContainer = document.querySelector('.autocomplete') ?? SearchWrapper.#renderAutocompleteContainer(); + const autocompleteContainer = this.#resolveAutocompleteContainer(); - for (let existingTerm of autocompleteContainer.querySelectorAll('.autocomplete__item--property')) { + if (!autocompleteContainer) { + return; + } + + // Since the autocomplete popup was refactored to re-use the same element over and over again, we need to remove + // the options from the popup manually when autocomplete was removed from the DOM, since site is not doing that. + const termsToRemove = autocompleteContainer.isConnected + // Only removing properties when element is still connected to the DOM (popup is used by the website) + ? autocompleteContainer.querySelectorAll('.autocomplete__item--property') + // Remove everything if popup was disconnected from the DOM. + : autocompleteContainer.querySelectorAll('.autocomplete__item') + + for (let existingTerm of termsToRemove) { existingTerm.remove(); } @@ -239,23 +271,6 @@ export class SearchWrapper extends BaseComponent { return suggestionsList; } - /** - * Render a new autocomplete container similar to the one generated by website. Might be sensitive to the updates - * made to the Philomena. - * @return {HTMLElement} - */ - static #renderAutocompleteContainer() { - const autocompleteContainer = document.createElement('div'); - autocompleteContainer.className = 'autocomplete'; - - const innerListContainer = document.createElement('ul'); - innerListContainer.className = 'autocomplete__list'; - - autocompleteContainer.append(innerListContainer); - - return autocompleteContainer; - } - /** * Render a single suggestion item and connect required events to interact with the user. * @param {string} suggestedTerm Term to use for suggestion item.