Here's a Javascript you can use to implement a dynamic search dropdown in Flare. Just save the script below in a file and include it in your Flare project, and then include the script in your template page, or any page where you want the dropdown to appear. You might need to adjust the CSS selector of the search input field based on your project's structure.
NOTE Updated 2024-10-14: Added word highlighting. Updated 2024-08-12: Fixed the bug related to boolean operators. Updated 2024-05-20: Added a method to show the dropdown when the search input is focused.
class SearchManager {
constructor() {
this.debounce = this.debounce.bind(this);
this.handleSearch = this.handleSearch.bind(this);
this.displayResults = this.displayResults.bind(this);
this.clearResults = this.clearResults.bind(this);
this.showFullResults = this.showFullResults.bind(this);
this.hideDropDown = this.hideDropDown.bind(this);
this.showDropDown = this.showDropDown.bind(this); // Add this line
this.init();
}
debounce(func, delay) {
let debounceTimer;
return function () {
const context = this;
const args = arguments;
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => func.apply(context, args), delay);
};
}
handleSearch() {
let searchQuery = document.querySelector('.main-section .search-bar > input').value.trim();
const invalidPattern = /(\"|\($|\b(AND|OR|NOT|NEAR)\b)$/i;
const openParenthesesCount = (searchQuery.match(/\(/g) || []).length;
const closeParenthesesCount = (searchQuery.match(/\)/g) || []).length;
if (invalidPattern.test(searchQuery) || openParenthesesCount !== closeParenthesesCount) {
console.warn("Invalid search query. Please ensure proper use of operators and matching parentheses.");
return;
}
if (searchQuery.length > 0) {
MadCap.SearchHelper.SearchPane.Search(searchQuery, { searchContent: true })
.then(this.displayResults)
.catch(error => {
console.error("Search failed: ", error);
});
} else {
this.clearResults();
}
}
displayResults(results) {
const dropdown = document.getElementById('searchResultsDropdown');
dropdown.style.visibility = "visible"; // Ensure the dropdown is visible
dropdown.innerHTML = '';
const searchQuery = document.querySelector('.main-section .search-bar > input').value.trim(); // Get the current search query
results.content.slice(0, 5).forEach(item => {
const resultItem = document.createElement('li');
const title = document.createElement('div');
title.classList.add('title');
title.textContent = item.Title;
resultItem.appendChild(title);
const preview = document.createElement('div');
preview.classList.add('preview');
preview.textContent = item.AbstractText;
resultItem.appendChild(preview);
resultItem.addEventListener('click', () => {
// Append ?Highlight={searchword} to the URL
const urlWithHighlight = `${item.Link}?Highlight=${encodeURIComponent(searchQuery)}`;
window.location.href = urlWithHighlight;
});
dropdown.appendChild(resultItem);
});
if (results.content.length > 5) {
const showAll = document.createElement('li');
showAll.textContent = 'Showing 5 results. For all results, press ENTER';
showAll.addEventListener('click', this.showFullResults);
// Append this first so it's at the top:
dropdown.insertBefore(showAll, dropdown.firstChild);
}
}
displayAllResults(results) {
const dropdown = document.getElementById('searchResultsDropdown');
dropdown.innerHTML = '';
const searchQuery = document.querySelector('.main-section .search-bar > input').value.trim(); // Get the current search query
results.content.forEach(item => {
const resultItem = document.createElement('li');
const title = document.createElement('div');
title.classList.add('title');
title.textContent = item.Title;
resultItem.appendChild(title);
const preview = document.createElement('div');
preview.classList.add('preview');
preview.textContent = item.AbstractText;
resultItem.appendChild(preview);
resultItem.addEventListener('click', () => {
// Append ?Highlight={searchword} to the URL
const urlWithHighlight = `${item.Link}?Highlight=${encodeURIComponent(searchQuery)}`;
window.location.href = urlWithHighlight;
});
dropdown.appendChild(resultItem);
});
}
clearResults() {
const dropdown = document.getElementById('searchResultsDropdown');
dropdown.innerHTML = '';
}
showFullResults() {
// Implementation for showing full results
}
hideDropDown(event) {
const searchResultsDropdown = document.getElementById('searchResultsDropdown');
if (!event.target.closest(".search-field")) {
searchResultsDropdown.style.visibility = "hidden";
}
}
showDropDown() { // Add this method
const searchResultsDropdown = document.getElementById('searchResultsDropdown');
searchResultsDropdown.style.visibility = "visible";
}
init() {
$(document).ready(() => {
const searchInput = document.querySelector('.main-section .search-bar > input');
searchInput.addEventListener('input', this.debounce(this.handleSearch, 300));
searchInput.addEventListener('focus', this.showDropDown); // Add this line
const dropdown = document.createElement('ul');
dropdown.id = 'searchResultsDropdown';
document.querySelector('.main-section .search-bar').appendChild(dropdown);
});
document.addEventListener("click", this.hideDropDown);
}
}
new SearchManager();