Solutions | LearnDash (2024)

`;} else {// Store the results in the html variableresults.forEach( ( { title, url } ) => {html += `

${ title }

`;} );// Got more than 10 results? Return paginationif ( total > 10 ) {html += getPrevNextButtons( page, totalPages );}}return html;};/** * Function to handle the population of search results HTML, in two parts * * 1) Display the search result links * 2) Display the results count number within the tabs (knowledgebase, dev docs, etc.) * * @param {Object} results - The search results data (post title, URL) * @param {Integer} total - The total number of results for the search * @param {String} tab - The tab (knowledgebase, dev docs, etc.) */const populateResults = ( results, totalString, totalPagesString, tab, page ) => {// Convert total to integerconst total = parseInt( totalString );// Convert totalPages to integerconst totalPages = parseInt( totalPagesString );// 1) Search Resultslet html = '';html += renderResults( results, page, total, totalPages );// Which tab are we populating?const resultsEl = searchSearched.querySelector( `[data-endpoint="${ tab }"]` );// Update page attributeresultsEl.setAttribute( 'data-page', page );// Populate the tab with resultsresultsEl.innerHTML = html;startPrevNextListeners();// Add event listener to "start new search" button if no results returnedif ( 0 === total ) {const noResultsButton = document.querySelector( `.${ classPrefix }__searched > [data-endpoint="${ tab }"] .${ noResultsClass } > button` );noResultsButton.addEventListener( 'click', noResultsButtonHandler );}// 2) Tab countconst countElement = document.querySelector( `.${ classPrefix }__tab[data-endpoint="${ tab }"] .${ classPrefix }__tab-count` );countElement.innerText = total;};/** * Function used to determine active tab on form submissions and recent search button clicks * Just those two events *because* they are not explicitly searching on a particular tab * * @param {String} blogTotal - Blog result count * @param {String} extensionsTotal - Extensions result count * @param {String} kbTotal - KB result count * */const tabPrioritizer = ( blogTotal, extensionsTotal, kbTotal ) => {// We have blog results, let's prioritize that and cease executionif ( parseInt( blogTotal ) > 0 ) {return 'blog';}// Do we have extensions content? Let's return that and cease executionif ( parseInt( extensionsTotal ) > 0 ) {return 'extensions';}// Do we have kb content? Okay, let's return that.if ( parseInt( kbTotal ) > 0 ) {return 'kb';}// At this point, we have 0 results across all tabs// I guess we can return 'blog' againreturn 'blog';};/** * Function that gathers search results and sets active tab accordingly. * * @param {String} term - The search term * @param {String} tab - The active tab */const startSearching = ( term, tab = false, page = 1 ) => {// If the term is an empty string, don't do anythingif ( isEmptyString( term ) ) {return;}// I guess we're searching now. Set loading state.setLoading();// Might as well add the term to recent searchesaddToRecentSearches( term );// Get ready to show recent searches should we return to that state latershowRecentsOrNot();// Retrieve blog resultsconst blogResults = getSearchResults( term, 'blog', page );// Retrive knowledgebase resultsconst extensionResults = getSearchResults( term, 'extensions', page );// Retrive kb resultsconst kbResults = getSearchResults( term, 'kb', page );// Set searchTerm statesearchSearched.setAttribute( 'data-searchTerm', term );// Promise we get back results of all endpointsPromise.all( [ blogResults, extensionResults, kbResults ] ).then( ( [ blog, extensions, kb ] ) => {// Populate results of knowledgebase tabpopulateResults( blog.results, blog.total, blog.totalPages, 'blog', 1 );// Populate results of extensions tabpopulateResults( extensions.results, extensions.total, extensions.totalPages, 'extensions', 1 );// Populate results of kb tabpopulateResults( kb.results, kb.total, kb.totalPages, 'kb', 1 );let activeTab;if ( false === tab ) {activeTab = tabPrioritizer( blog.total, extensions.total, kb.total );} else {activeTab = tab;}// Set the active tabsetActiveTab( activeTab );} );};/** * Function to handle when a "recent search item" button is clicked. * These buttons appear in the "not searching" state. For example: * * [Magnifying Glass Icon] Most recently searched term * [Magnifying Glass Icon] Second most recently searched term * * @param {Object} target - Destructured from the event */const recentSearchButtonHandler = ( { target } ) => {// Get the innerText property from the button, rename to "term"const { innerText: term } = target.closest( 'button' );// Let's update the searchInput value as if they actually typed it in.searchInput.value = term;// Start searchingstartSearching( term );};/** * Function to populate recent searches. */const populateRecentSearches = () => {// Get recent searchesconst recentSearches = getRecentSearches();// We don't have recent searches, let's chillif ( ! recentSearches ) {return;}// We do have recent searches, let's start writing HTMLlet html = '';// Build the HTMLrecentSearches.forEach( search => {// "Escape" the HTML// @link: https://stackoverflow.com/a/22706073const escapeStaging = document.createElement( 'p' );escapeStaging.appendChild( document.createTextNode( search ) );const { innerHTML: escapedSearch } = escapeStaging;escapeStaging.remove();// Build the HTMLhtml += `

`;} );// Select the HTML element we're going to fill up with new HTMLconst recentsEl = document.querySelector( `.${ classPrefix }__fresh-recents-searches` );// Fill up with new HTMLrecentsEl.innerHTML = html;// Select all newly-created recent search buttonsconst recentSearchButtons = recentsEl.querySelectorAll( `.${ recentSearchesButtonClass }` );// Add event listeners to each buttonrecentSearchButtons.forEach( button => button.addEventListener( 'click', recentSearchButtonHandler ) );};/** * Set the "notSearching" state. * This is when either recent searches show up. * Or, if no recent searches, a "No Recent Searches" view. */const setNotSearching = () => {// Make sure recent searches are up-to-datepopulateRecentSearches();// Set the stateapp.setAttribute( 'data-state', 'notSearching' );};/** * Function to handle when a "term button" is clicked. * These buttons appear in the "searching" state. For example: * * "Test in Blog" * "Test in Extensions" * "Test in KB" * * @param {Object} target - Destructured from the event * @since feature/TECCOM-1783-new-search-experience */const termButtonClickHandler = ( { target } ) => {// Destructure the data-endpoint value from the "searching" state buttonsconst { endpoint } = target.closest( `.${ classPrefix }__searching-item` ).dataset;// We're ready to start searching!startSearching( searchInput.value, endpoint );};/** * Function to set the "searching" state. * * This is before a search has actually taken place. * * As the user types, the user will see buttons like the following: * * "Test in Knowledgebase" * "Test in Developer Docs" * "Test in Blog Posts" * * @since feature/TECCOM-1783-new-search-experience */const setSearching = () => {app.setAttribute( 'data-state', 'searching' );};/** * This happens in the "searching" state. * * This is what takes the searchInput value and plops that into the each term button. * * For example: * * "Test in Blog" * "Test in Extensions" * "Test in Knowledgebase" * */const teleportTermToDivs = () => {blankTerms.forEach( term => {term.innerText = searchInput.value;} );};/** * Function to handle searchInput keyup events. * * @param {String} oldValue - This is the value *before* the keyup event occurs. * @param {String} key - This is the key (like "ArrowDown") that was pressed. */const searchInputKeyHandler = oldValue => {// The string hasn't changed, stop runningif ( oldValue === searchInput.value ) {return;}// If the search input is empty, we're not searching anymore. Sorry.if ( isEmptyString( searchInput.value ) ) {setNotSearching();return;}// Set searching statesetSearching();// Make sure "term buttons" have been teleported accordinglyteleportTermToDivs();};const activateModal = () => {// Let's make sure we grab recent searchespopulateRecentSearches();// Do we have recent searches? Let's make sure they show up as expectedshowRecentsOrNot();// Stateless? I guess we're notSearchingif ( ! app.dataset.state ) {app.setAttribute( 'data-state', 'notSearching' );}// Focus on the search inputsearchInput.focus();// Listen for keystrokes on search inputlet oldValue = '';searchInput.addEventListener( 'keydown', function() {oldValue = searchInput.value;} );searchInput.addEventListener( 'keyup', function( { key } ) {searchInputKeyHandler( oldValue, key );} );};/** * While in the "searched" state, this function handles click events on the tabs. * * For example: Blog, Extensions, KB **/const tabClickHandler = ( { target } ) => {// Bubble up to the button, if neededconst clickedTab = target.closest( 'button' );// Do nothing if tab is already activeif ( clickedTab.classList.contains( tabActiveClass ) ) {return;}// Destructure the data-endpoint valueconst { dataset: { endpoint } } = clickedTab;// Set the tab according to the endpoint (knowledgebase, tec.com, etc.)setActiveTab( endpoint );};/** * Function to handle search form submission events.*/const formHandler = event => {// Hijack default browser behavior because this isn't a normal HTML formevent.preventDefault();// Start searchingstartSearching( searchInput.value );};// Activate modal on loadactivateModal();// Listen to form submissionssearchForm.addEventListener( 'submit', formHandler );// Listen for clicks on those "Search for [term] in [Knowledgebase, Developer Docs, etc.] buttons"termButtons.forEach( button => button.addEventListener( 'click', termButtonClickHandler ) );// Listen for clicks on tabs like Knowledgebase or Developer Docs visible *after* a search has been runtabs.forEach( tab => tab.addEventListener( 'click', tabClickHandler ) );})();

Solutions | LearnDash (2024)
Top Articles
Latest Posts
Article information

Author: Dean Jakubowski Ret

Last Updated:

Views: 5732

Rating: 5 / 5 (70 voted)

Reviews: 93% of readers found this page helpful

Author information

Name: Dean Jakubowski Ret

Birthday: 1996-05-10

Address: Apt. 425 4346 Santiago Islands, Shariside, AK 38830-1874

Phone: +96313309894162

Job: Legacy Sales Designer

Hobby: Baseball, Wood carving, Candle making, Jigsaw puzzles, Lacemaking, Parkour, Drawing

Introduction: My name is Dean Jakubowski Ret, I am a enthusiastic, friendly, homely, handsome, zealous, brainy, elegant person who loves writing and wants to share my knowledge and understanding with you.