themes/docsy/static/js/offline-search.js (70 lines of code) (raw):

// Adapted from code by Matt Walters https://www.mattwalters.net/posts/hugo-and-lunr/ var idx = null; // Lunr index var resultDetails = []; // Will hold the data for the search results (titles and summaries) var $searchInput; // The search box element in the navbar var $searchResults; // Results shown in the navbar $(window).on('load', function() { // Set up for an Ajax call to request the JSON data file that is created by // Hugo's build process, with the template we added above var request = new XMLHttpRequest(); var query = ''; // Get dom objects for the elements we'll be interacting with $searchResults = document.getElementById('search-results'); $searchInput = document.getElementById('search-input'); request.overrideMimeType("application/json"); request.open("GET", "/index.json", true); // Request the JSON file created during build request.onload = function() { if (request.status >= 200 && request.status < 400) { // Success response received in requesting the search-index file var searchDocuments = JSON.parse(request.responseText); // Build the index so Lunr can search it. The `ref` field will hold the URL // to the page/post. title, excerpt, and body will be fields searched. idx = lunr(function lunrIndex() { this.ref('ref'); this.field('title'); this.field('body'); // Loop through all the items in the JSON file and add them to the index // so they can be searched. searchDocuments.forEach(function(doc) { this.add(doc); resultDetails[doc.ref] = { 'title': doc.title, 'excerpt': doc.excerpt, }; }, this); }); } else { $searchResults.innerHTML = '<ul><li>Error loading search results</li></ul>'; } }; request.onerror = function() { $searchResults.innerHTML = '<ul><li>Error loading search results</li></ul>'; }; // Send the request to load the JSON request.send(); // Register handler for the search input field registerSearchHandler(); }); function registerSearchHandler() { $searchInput.oninput = function(event) { var query = event.target.value; var results = search(query); // Perform the search // Render search results renderSearchResults(results); // Remove search results if the user empties the search phrase input field if ($searchInput.value == '') { $searchResults.innerHTML = ''; } } } function renderSearchResults(results) { // Create a list of results if (results.length > 0) { var ul = document.createElement('ul'); results.forEach(function(result) { // Create result item var li = document.createElement('li'); li.innerHTML = '<a href="' + result.ref + '">' + resultDetails[result.ref].title + '</a><br>' + resultDetails[result.ref].excerpt + '...'; ul.appendChild(li); }); // Remove any existing content so results aren't continually added as the user types while ($searchResults.hasChildNodes()) { $searchResults.removeChild( $searchResults.lastChild ); } } else { $searchResults.innerHTML = '<ul><li>No results found</li></ul>'; } // Render the list $searchResults.appendChild(ul); } function search(query) { return idx.search(query); } // Disables enter key on input fields except textarea $(document).on("keydown", ":input:not(textarea)", function(event) { return event.key != "Enter"; });