Overview
Quick Search is an exclusive advanced component of Good that provides a flexible and powerful UI component to manage and handle layouts related to search. Quick Search also comes with multiple custom hook points to allow advanced users to bind custom JS or search logic to control and manipulate our Quick Search component.
The Quick Search component is a frontend only solution that manages element display orders and event triggers to show/hide certain layers. However, the search logic backend functionality is not included.
Usage
Quick Search hooks are globally included in our scripts bundle, however, initialization is manually controlled and is required to be included.
copy
<link href="assets/css/style.bundle.css" rel="stylesheet" type="text/css"/>
<script src="assets/js/scripts.bundle.js"></script>
Initialization
The Quick Search component is a highly robust and flexible frontend solution that comes with number of HTML elements to provide a rapid search layout. Quick Search starts with a simple
div
element that houses a number of HTML attributes to control and configure the Quick Search component.
See below for more info .
Quick Search structure comes in a few layers.
Main Wrapper
Toggle Button
Main Content Element
Main Content Inner Wrapper
Input Form
Search Suggestion
Search Preference
Search Advanced Options
Search Results
Empty Search
Each search UI HTML element within the main wrapper can be identified by the
data-kt-search-element
HTML attribute with a specific value. For example,
data-kt-search-element="toggle"
indicates that the current HTML element is a Toggle Button.
See below for more info .
Quick Search uses our extended Bootstrap's utility classes for styling.
Quick Search instances can also be controlled programmatically.
See below for more info .
Basic Layout
Here's a basic example of our Quick Search.
In this demo, we're simulating the search results by randomly showing and hiding the search results layer on keypress within the input field.
No users found
Try to search by username, full name or email...
copy
var processs = function(search) {
var timeout = setTimeout(function() {
var number = KTUtil.getRandomInt(1, 6);
// Hide recently viewed
suggestionsElement.classList.add("d-none");
if (number === 3) {
// Hide results
resultsElement.classList.add("d-none");
// Show empty message
emptyElement.classList.remove("d-none");
} else {
// Show results
resultsElement.classList.remove("d-none");
// Hide empty message
emptyElement.classList.add("d-none");
}
// Complete search
search.complete();
}, 1500);
}
var clear = function(search) {
// Show recently viewed
suggestionsElement.classList.remove("d-none");
// Hide results
resultsElement.classList.add("d-none");
// Hide empty message
emptyElement.classList.add("d-none");
}
// Input handler
const handleInput = () => {
// Select input field
const inputField = element.querySelector("[data-kt-search-element="input"]");
// Handle keyboard press event
inputField.addEventListener("keydown", e => {
// Only apply action to Enter key press
if(e.key === "Enter"){
e.preventDefault(); // Stop form from submitting
}
});
}
// Elements
element = document.querySelector('#kt_docs_search_handler_basic');
if (!element) {
return;
}
wrapperElement = element.querySelector("[data-kt-search-element="wrapper"]");
suggestionsElement = element.querySelector("[data-kt-search-element="suggestions"]");
resultsElement = element.querySelector("[data-kt-search-element="results"]");
emptyElement = element.querySelector("[data-kt-search-element="empty"]");
// Initialize search handler
searchObject = new KTSearch(element);
// Search handler
searchObject.on("kt.search.process", processs);
// Clear handler
searchObject.on("kt.search.clear", clear);
// Handle select
KTUtil.on(element, "[data-kt-search-element="customer"]", "click", function() {
//modal.hide();
});
// Handle input enter keypress
handleInput();
<!--begin::Main wrapper-->
<div
id="kt_docs_search_handler_basic"
data-kt-search-keypress="true"
data-kt-search-min-length="2"
data-kt-search-enter="true"
data-kt-search-layout="inline">
<!--begin::Input Form-->
<form data-kt-search-element="form" class="w-100 position-relative mb-5" autocomplete="off">
<!--begin::Hidden input(Added to disable form autocomplete)-->
<input type="hidden"/>
<!--end::Hidden input-->
<!--begin::Icon-->
<!--begin::Svg Icon | path: icons/duotune/general/gen021.svg-->
<!--end::Icon-->
<!--begin::Input-->
<input type="text" class="form-control form-control-lg form-control-solid px-15"
name="search"
value=""
placeholder="Search by username, full name or email..."
data-kt-search-element="input"/>
<!--end::Input-->
<!--begin::Spinner-->
<span class="position-absolute top-50 end-0 translate-middle-y lh-0 d-none me-5" data-kt-search-element="spinner">
<span class="spinner-border h-15px w-15px align-middle text-gray-400"></span>
</span>
<!--end::Spinner-->
<!--begin::Reset-->
<span class="btn btn-flush btn-active-color-primary position-absolute top-50 end-0 translate-middle-y lh-0 me-5 d-none"
data-kt-search-element="clear">
<!--begin::Svg Icon | path: icons/duotune/arrows/arr061.svg-->
</span>
<!--end::Reset-->
</form>
<!--end::Form-->
<!--begin::Wrapper-->
<div class="py-5">
<!--being::Search suggestion-->
<div data-kt-search-element="suggestions">
...
</div>
<!--end::Suggestion wrapper-->
<!--begin::Search results-->
<div data-kt-search-element="results" class="d-none">
...
</div>
<!--end::Search results-->
<!--begin::Empty search-->
<div data-kt-search-element="empty" class="text-center d-none">
...
</div>
<!--end::Empty search-->
</div>
<!--end::Wrapper-->
</div>
<!--end::Main wrapper-->
This demo combines the Quick Search component with our menu component for unique interactive dropdown search UI component.
In this demo, we're simulating the search results by randomly showing and hiding the search results layer on keypress within the input field.
copy
var processs = function(search) {
var timeout = setTimeout(function() {
var number = KTUtil.getRandomInt(1, 3);
// Hide recently viewed
mainElement.classList.add("d-none");
if (number === 3) {
// Hide results
resultsElement.classList.add("d-none");
// Show empty message
emptyElement.classList.remove("d-none");
} else {
// Show results
resultsElement.classList.remove("d-none");
// Hide empty message
emptyElement.classList.add("d-none");
}
// Complete search
search.complete();
}, 1500);
}
var clear = function(search) {
// Show recently viewed
mainElement.classList.remove("d-none");
// Hide results
resultsElement.classList.add("d-none");
// Hide empty message
emptyElement.classList.add("d-none");
}
var handlePreferences = function() {
// Preference show handler
preferencesShowElement.addEventListener("click", function() {
wrapperElement.classList.add("d-none");
preferencesElement.classList.remove("d-none");
});
// Preference dismiss handler
preferencesDismissElement.addEventListener("click", function() {
wrapperElement.classList.remove("d-none");
preferencesElement.classList.add("d-none");
});
}
var handleAdvancedOptionsForm = function() {
// Show
advancedOptionsFormShowElement.addEventListener("click", function() {
wrapperElement.classList.add("d-none");
advancedOptionsFormElement.classList.remove("d-none");
});
// Cancel
advancedOptionsFormCancelElement.addEventListener("click", function() {
wrapperElement.classList.remove("d-none");
advancedOptionsFormElement.classList.add("d-none");
});
// Search
advancedOptionsFormSearchElement.addEventListener("click", function() {
});
}
// Elements
element = document.querySelector("#kt_docs_search_handler_menu");
if (!element) {
return;
}
wrapperElement = element.querySelector("[data-kt-search-element="wrapper"]");
formElement = element.querySelector("[data-kt-search-element="form"]");
mainElement = element.querySelector("[data-kt-search-element="main"]");
resultsElement = element.querySelector("[data-kt-search-element="results"]");
emptyElement = element.querySelector("[data-kt-search-element="empty"]");
preferencesElement = element.querySelector("[data-kt-search-element="preferences"]");
preferencesShowElement = element.querySelector("[data-kt-search-element="preferences-show"]");
preferencesDismissElement = element.querySelector("[data-kt-search-element="preferences-dismiss"]");
advancedOptionsFormElement = element.querySelector("[data-kt-search-element="advanced-options-form"]");
advancedOptionsFormShowElement = element.querySelector("[data-kt-search-element="advanced-options-form-show"]");
advancedOptionsFormCancelElement = element.querySelector("[data-kt-search-element="advanced-options-form-cancel"]");
advancedOptionsFormSearchElement = element.querySelector("[data-kt-search-element="advanced-options-form-search"]");
// Initialize search handler
searchObject = new KTSearch(element);
// Search handler
searchObject.on("kt.search.process", processs);
// Clear handler
searchObject.on("kt.search.clear", clear);
// Custom handlers
handlePreferences();
handleAdvancedOptionsForm();
<!--begin::Main wrapper-->
<div id="kt_docs_search_handler_menu"
class="d-flex align-items-stretch"
data-kt-search-keypress="true"
data-kt-search-min-length="2"
data-kt-search-enter="true"
data-kt-search-layout="menu"
data-kt-menu-trigger="auto"
data-kt-menu-overflow="false"
data-kt-menu-permanent="true"
data-kt-menu-placement="bottom-start">
<!--begin::Search toggle-->
<div class="d-flex align-items-center" data-kt-search-element="toggle" id="kt_header_search_toggle">
<div class="btn btn-primary">
Toggle Quick Search
</div>
</div>
<!--end::Search toggle-->
<!--begin::Menu-->
<div data-kt-search-element="content" class="menu menu-sub menu-sub-dropdown p-7 w-325px w-md-375px">
<!--begin::Wrapper-->
<div data-kt-search-element="wrapper">
<!--begin::Form-->
<form data-kt-search-element="form" class="w-100 position-relative mb-3" autocomplete="off">
<!--begin::Icon-->
<!--begin::Svg Icon | path: icons/duotune/general/gen021.svg-->
<!--end::Icon-->
<!--begin::Input-->
<input type="text"
class="form-control form-control-flush ps-10"
name="search"
value=""
placeholder="Search..."
data-kt-search-element="input"/>
<!--end::Input-->
<!--begin::Spinner-->
<span class="position-absolute top-50 end-0 translate-middle-y lh-0 d-none me-1"
data-kt-search-element="spinner">
<span class="spinner-border h-15px w-15px align-middle text-gray-400"></span>
</span>
<!--end::Spinner-->
<!--begin::Reset-->
<span class="btn btn-flush btn-active-color-primary position-absolute top-50 end-0 translate-middle-y lh-0 d-none"
data-kt-search-element="clear">
<!--begin::Svg Icon | path: icons/duotune/arrows/arr061.svg-->
</span>
<!--end::Reset-->
<!--begin::Toolbar-->
<div class="position-absolute top-50 end-0 translate-middle-y" data-kt-search-element="toolbar">
<!--begin::Preferences toggle-->
<div data-kt-search-element="preferences-show"
class="btn btn-icon w-20px btn-sm btn-active-color-primary me-1"
data-bs-toggle="tooltip"
title="Show search preferences">
<!--begin::Svg Icon | path: icons/duotune/coding/cod001.svg-->
</div>
<!--end::Preferences toggle-->
<!--begin::Advanced search toggle-->
<div data-kt-search-element="advanced-options-form-show"
class="btn btn-icon w-20px btn-sm btn-active-color-primary"
data-bs-toggle="tooltip"
title="Show more search options">
<!--begin::Svg Icon | path: icons/duotune/arrows/arr072.svg-->
</div>
<!--end::Advanced search toggle-->
</div>
<!--end::Toolbar-->
</form>
<!--end::Form-->
<!--begin::Search results-->
<div data-kt-search-element="results" class="d-none">
...
</div>
<!--end::Search results-->
<!--begin::Main content element-->
<div data-kt-search-element="main">
...
</div>
<!--end::Main content element-->
<!--begin::Empty search-->
<div data-kt-search-element="empty" class="text-center d-none">
...
</div>
<!--end::Empty search-->
</div>
<!--end::Wrapper-->
<!--begin::Search Advanced Options-->
<div data-kt-search-element="advanced-options-form">
...
</div>
<!--end::Search Advanced Options-->
<!--begin::Search Preference-->
<div data-kt-search-element="preferences">
...
</div>
<!--end::Search Preference-->
</div>
<!--end::Menu-->
</div>
<!--end::Main wrapper-->
Responsive Layout
This demo showcases Search UI's responsive solution. It displays as a full inline input field on large screens, and converts into an icon toggle button on smaller screens.
In this demo, we're simulating the search results by randomly showing and hiding the search results layer on keypress within the input field.
copy
var processs = function(search) {
var timeout = setTimeout(function() {
var number = KTUtil.getRandomInt(1, 3);
// Hide recently viewed
recentlyViewedElement.classList.add("d-none");
if (number === 3) {
// Hide results
resultsElement.classList.add("d-none");
// Show empty message
emptyElement.classList.remove("d-none");
} else {
// Show results
resultsElement.classList.remove("d-none");
// Hide empty message
emptyElement.classList.add("d-none");
}
// Complete search
search.complete();
}, 1500);
}
var clear = function(search) {
// Show recently viewed
recentlyViewedElement.classList.remove("d-none");
// Hide results
resultsElement.classList.add("d-none");
// Hide empty message
emptyElement.classList.add("d-none");
}
// Elements
element = document.querySelector("#kt_docs_search_handler_responsive");
if (!element) {
return;
}
wrapperElement = element.querySelector("[data-kt-search-element="wrapper"]");
recentlyViewedElement = element.querySelector("[data-kt-search-element="recently-viewed"]");
resultsElement = element.querySelector("[data-kt-search-element="results"]");
emptyElement = element.querySelector("[data-kt-search-element="empty"]");
preferencesElement = element.querySelector("[data-kt-search-element="preferences"]");
preferencesShowElement = element.querySelector("[data-kt-search-element="preferences-show"]");
preferencesDismissElement = element.querySelector("[data-kt-search-element="preferences-dismiss"]");
// Initialize search handler
searchObject = new KTSearch(element);
// Search handler
searchObject.on("kt.search.process", processs);
// Clear handler
searchObject.on("kt.search.clear", clear);
// Preference show handler
preferencesShowElement.addEventListener("click", function() {
wrapperElement.classList.add("d-none");
preferencesElement.classList.remove("d-none");
});
// Preference dismiss handler
preferencesDismissElement.addEventListener("click", function() {
wrapperElement.classList.remove("d-none");
preferencesElement.classList.add("d-none");
});
}
<!--begin::Main wrapper-->
<div
id="kt_docs_search_handler_responsive"
class="d-flex align-items-center w-lg-400px"
data-kt-search-keypress="true"
data-kt-search-min-length="2"
data-kt-search-enter="enter"
data-kt-search-layout="menu"
data-kt-search-responsive="lg"
data-kt-menu-trigger="auto"
data-kt-menu-permanent="true"
data-kt-menu-placement="bottom-start">
<!--begin::Tablet and mobile search toggle-->
<div data-kt-search-element="toggle" class="d-flex d-lg-none align-items-center">
<div class="btn btn-icon btn-active-light-primary">
<?php echo Theme::getSvgIcon("icons/duotune/general/gen021.svg", "svg-icon-1")?>
</div>
</div>
<!--end::Tablet and mobile search toggle-->
<!--begin::Form-->
<form data-kt-search-element="form" class="d-none d-lg-block w-100 position-relative mb-5 mb-lg-0" autocomplete="off">
<!--begin::Hidden input(Added to disable form autocomplete)-->
<input type="hidden"/>
<!--end::Hidden input-->
<!--begin::Icon-->
<?php echo Theme::getSvgIcon("icons/duotune/general/gen021.svg", "svg-icon-2 svg-icon-lg-1 svg-icon-gray-500 position-absolute top-50 translate-middle-y ms-5")?>
<!--end::Icon-->
<!--begin::Input-->
<input type="text" class="form-control form-control-solid ps-14" name="search" value="" placeholder="Search..." data-kt-search-element="input"/>
<!--end::Input-->
<!--begin::Spinner-->
<span class="position-absolute top-50 end-0 translate-middle-y lh-0 d-none me-6" data-kt-search-element="spinner">
<span class="spinner-border h-15px w-15px align-middle text-gray-400"></span>
</span>
<!--end::Spinner-->
<!--begin::Reset-->
<span class="btn btn-flush btn-active-color-primary position-absolute top-50 end-0 translate-middle-y lh-0 me-5 d-none" data-kt-search-element="clear">
<?php echo Theme::getSvgIcon("icons/duotune/arrows/arr061.svg", "svg-icon-2 svg-icon-lg-1 me-0")?>
</span>
<!--end::Reset-->
</form>
<!--end::Form-->
<!--begin::Menu-->
<div data-kt-search-element="content" class="menu menu-sub menu-sub-dropdown w-300px w-md-400px py-7 px-7 overflow-hidden">
<!--begin::Wrapper-->
<div data-kt-search-element="wrapper">
<!--begin::Categories-->
<div data-kt-search-element="categories">
...
</div>
<!--end::Categories-->
<!--begin::Search results-->
<div data-kt-search-element="results" class="d-none">
...
</div>
<!--end::Search results-->
<!--begin::Recently viewed-->
<div data-kt-search-element="recently-viewed">
...
</div>
<!--end::Recently viewed-->
<!--begin::Empty search-->
<div data-kt-search-element="empty" class="text-center d-none">
...
</div>
<!--end::Empty search-->
</div>
<!--end::Wrapper-->
<!--begin::Preferences-->
<div data-kt-search-element="preferences"
...
</div>
<!--end::Preferences-->
</div>
<!--end::Menu-->
</div>
<!--end::Main wrapper-->
Markup Reference
Main wrapper HTML Attribute references
Quick Search uses HTML attributes within the Main Wrapper to configure the frontend logic. Here are the references for each below
Name
Type
Description
data-kt-search-keypress
optional
Enable search on any keyboard keypress. Accepts
true
or
false
values.
data-kt-search-enter
optional
Enable search on any keyboard
Enter
keypress. Accepts
true
or
false
values.
data-kt-search-responsive
optional
Defines the responsive mode for the input field's position. Useful for an inline input field to display itself within a dropdown menu on mobile to save space. Accepts
sm, md, lg, xl
and
null
values.
For example:
data-kt-search-responsive="lg"
indicates that the Quick Search component input field will be displayed inline with the parent container from
lg
screen sizes and above, and hidden (re-positioned into the dropdown menu) on smaller screens.
data-kt-search-min-length
optional
Defines the number of characters required before the search event is triggered. Accepts
numeric
values.
data-kt-search-layout
optional
Defines Quick Search's layout type. Accepts
inline
and
menu
values.
data-kt-search-show-on-focus
optional
Enables menu dropdown to show when the input field detects a focus event. Accepts
true
or
false
values.
HTML Attribute Identifiers references
Quick Search also use HTML attributes to identify the HTML elements that are used within the Main Wrapper. Here are the references for each below
Name
Type
Description
data-kt-search-element
mandatory
Identifies the HTML element as a specific Quick Search element. All Quick Search elements must be added within the Main Wrapper. The main wrapper does not have a specific attribute to identify it. It's simply a wrapping
div
element that wraps all Quick Search inner elements.
Here are the list of Quick Search elements available:
form
input
spinner
clear
toggle
content
toolbar
empty
results
suggestions
copy
<div>
<div data-kt-search-element="form">
...
<input data-kt-search-element="input" />
...
</div>
</div>
Methods
The following are Search UI's functionality methods for more control.
Name
Description
Static Methods
getInstance(DOMElement element)
Get the Search UI instance that has been created and returns the Search UI DOM element.
copy
var searchElement = document.querySelector("#kt_search_example_1");
var search = KTSearch.getInstance(searchElement);
Public Methods
new KTSearch(DOMElement element)
Initialize a new Quick Search control.
copy
var searchElement = document.querySelector("#kt_search_control");
var search = new KTSearch(searchElement);
show
Triggers the menu to show. Only applicable when
data-kt-search-layout
is set to
menu
hide
Triggers the menu to hide. Only applicable when
data-kt-search-layout
is set to
menu
update
Updates the menu responsiveness. Only applicable when
data-kt-search-layout
is set to
menu
search
Triggers the search event to begin searching.
complete
Triggers the completed search event.
clear
Clears the search input field and resets the Search UI.
isProcessing
Returns
true
or
false
to indicate if there's a search event still running.
getQuery
Returns the Search UI input string.
getMenu
Returns the Search UI menu element. Only applicable when
data-kt-search-layout
is set to
menu
getFormElement
Returns the Search UI form element.
getInputElement
Returns the Search UI input field element.
copy
search.getInputElement();
getContentElement
Returns the Search UI content element.
copy
search.getContentElement();
getElement
Returns the Search UI element.
Events
Below are few events for hooking into Quick Search's functionality.
Event Type
Description
kt.search.process
This event fires when search begins processing.
copy
var searchElement = document.querySelector("#kt_search_example_1");
var search = KTSearch.getInstance(searchElement);
search.on("kt.search.process", function() {
// console.log("kt.search.process event is fired");
});
kt.search.clear
This event fires when search input field is about to be cleared.
copy
var searchElement = document.querySelector("#kt_search_example_1");
var search = KTSearch.getInstance(searchElement);
search.on("kt.search.clear", function() {
// console.log("kt.search.clear event is fired");
});
kt.search.cleared
This event fires when search input field has been cleared.
copy
var searchElement = document.querySelector("#kt_search_example_1");
var search = KTSearch.getInstance(searchElement);
search.on("kt.search.cleared", function() {
// console.log("kt.search.cleared event is fired");
});