Adding Live Search to Shop

I first added a live search, or instant search to my blog and now to my shop. This type of search modifies page contents based on the query, instead of directing you to a new page with the results. You can see it in action if you click the magnifier icon in my shop above the categories, and begin typing. The code filters the product listing based on the search query, and hides all the grid items (shop products) if the query does not match anything found in the title or the product tags. There is some additional code to handle the icon sliding left and right, but this is purely cosmetic. The key difference from the live search in my blog, is that I now not only look at the title of the product, which is housed in an attribute, but also at the product tags. These tags are listed as classes and preceded by the word β€œtag-”. You will find some functionality to extract this information below. I did not parse any JSON to achieve this, instead I handled everything inside the attributes as plain text.

HTML:

<input type="text" id="shopSearchInput" class="shop-search-input" placeholder="">
JAVASCRIPT:

document.addEventListener('DOMContentLoaded', function() {
const subString = 'shop';
const badString = 'shop/p/'; 
const getURL = window.location.href;
const shopSearchInput = document.querySelector('#shopSearchInput');
if (getURL.includes(subString) && !getURL.includes(badString)) {
var mobileMenuActive = false;
let isTouch = false;
const headerActions = document.querySelector('.header-actions');
function checkHeader() {
const styles = window.getComputedStyle(headerActions);
isTouch = styles.getPropertyValue('display') !== 'flex';
}
checkHeader();
//embed the shop search bar
var nestedCategories = document.querySelector('.nested-category-tree-wrapper');
if (isTouch == true) {
nestedCategories.insertBefore(shopSearchInput, nestedCategories.lastChild);
shopSearchInput.classList.add('mobile');
} else {
nestedCategories.insertBefore(shopSearchInput, nestedCategories.firstChild);
shopSearchInput.classList.remove('mobile');
}
//handle shop search
const debounceDelay = 700;
let debounceTimeout;
shopSearchInput.addEventListener('input', function() {
clearTimeout(debounceTimeout);
debounceTimeout = setTimeout(() => {
const query = this.value.toLowerCase();
//if input is not empty scroll to top
if (query.trim() !== '' && isTouch == false) {
setTimeout(() => {
window.scrollTo({ top: 0, behavior: 'smooth' });
}, 50); 
} 
const products = document.querySelectorAll('.grid-item');
products.forEach(product => {
const contextData = product.getAttribute('data-current-context');
let title = '';
if (contextData) {
const titleMatch = contextData.match(/"title":\s*"([^"]+)"/);
title = titleMatch ? titleMatch[1].toLowerCase() : '';
}
const tagClasses = Array.from(product.classList)
.filter(cls => cls.startsWith('tag-'))
.map(cls => cls.replace('tag-', '').toLowerCase());
const matchesTitle = title.includes(query);
const matchesTag = tagClasses.some(tag => tag.includes(query));
if (matchesTitle || matchesTag) {
product.style.display = ''; // Show the product
} else {
product.style.display = 'none'; // Hide the product
}
});
}, debounceDelay);
});
//is input empty for cosmetics
var hasText = false;
function checkInput() {
hasText = shopSearchInput.value.trim() !== '';
}
document.addEventListener('click', function(event) {
checkInput();
if (event.target !== shopSearchInput && hasText == false) {
shopSearchInput.classList.remove('texted');
} else {
shopSearchInput.classList.add('texted');
}
});
} else {
shopSearchInput.remove();
}
});
CSS:

#shopSearchInput {
  width: 200px;
  height: 50px !important;
  display: block;
  background: transparent;
  border: 0 !important;
  font-size: 1.3rem;
  letter-spacing: 1.8px;
  outline: none;
  padding: 0;
  margin: 0;
  margin-bottom: 28px;
  margin-left: -50px;
  text-align: left;
  z-index: 999;
  transition: all .7s ease !important;
  background-image: url(https://images.squarespace-cdn.com/content/v1/6654b2fee26f292de13f1a9d/2b60bbec-4d1c-4d73-9d6a-009b9bf1d26a/SearchCrosshairIcon.png);
  background-size: 50px;
  background-position: bottom 0px left 38px;
  background-repeat: no-repeat;
  padding-left: 50px !important; 
}
@media screen and (max-width:790px) {
#shopSearchInput {
  position: absolute;
  width: 70%;
  background-position: bottom 0px left 55%;
  margin-left: 0px;
  top: 429px;
  }
}
#shopSearchInput:focus {
  outline: none; 
  transition: all .7s ease;
  background-position: bottom 0px left -60px;
} 
#shopSearchInput.texted {
  outline: none; 
  transition: all .7s ease;
  background-position: bottom 0px left -6px;
}  
Previous
Previous

Display Latest Shop products in summary block

Next
Next

Adding Live Search to Blog