Demo
Notes: The easier approach is to create a page using pre-built template called "Product Category".
Instructions:
- Go to Edit Page
- Paste "HubSpot Product Library with Filter" into input
- Drag highlighted option
- Paste the code
{% set products = crm_objects("product","limit=100"~product_count,"id,price,createdate,name,hs_images,description,hs_url,category,stock_level,hs_product_type,hs_sku") %}
<div class="product-library-filter">
<div id="products" class="{% if module.heading.text or module.lead.lead %}mt-10{% endif %}">
<div class="product-flex">
<div class="product-col product-col-sidebar">
<div class="sidebar bg-secondary-100">
<input class="search filter-space" type="search" placeholder="Search" />
<button class="sort filter-space" data-sort="product-name">
Sort by Product Name
</button>
<select id="product-category" class="filter-space">
<option selected="selected" value="">All Product Category</option>
{% for product in products.results|unique('category') %}
{% if product.category == null %}
{% else %}
<option value="{{ product.category|lower|replace(" ", "-") }}">{{ product.category }}</option>
{% endif %}
{% endfor %}
</select>
<div class="form-group product-type-wrap">
{% for product in products.results|unique('hs_product_type') %}
{% if product.hs_product_type == null or product.hs_product_type == "" %}
{% else %}
<label class="product-type-label">Product Type</label>
<div class="radio-inline">
<label>
<input class="filter" type="radio" value="{{ product.hs_product_type|lower|replace(" ", "-") }}" name="type" /> {{ product.hs_product_type }}
</label>
</div>
{% endif %}
{% endfor %}
</div>
</div>
</div>
<div class="product-col">
<div class="list grid grid-cols-1 gap-y-10 gap-x-6 sm:grid-cols-2 lg:grid-cols-3 lg:gap-x-8">
{% for product in products.results %}
<a data-category='{{ product.category|lower|replace(" ", "-") }}' class="product-item group text-sm" href="{% if product.hs_sku %}/product?SKU={{ product.hs_sku }}{% else %}javascript:void(0){% endif %}">
<div class="aspect-w-1 aspect-h-1 w-full overflow-hidden {{ module.style.card.image.border_radius }} bg-secondary-100 group-hover:opacity-75">
{% if product.hs_images %}
<img style="height:350px;" src="{{ product.hs_images }}" alt="{{ product.name }}" class="h-full w-full object-cover object-center">
{% else %}
<img style="height:350px;" src="https://dummyimage.com/600x400/fafafa/fafafa" alt="{{ product.name }}" class="h-full w-full object-cover object-center">
{% endif %}
</div>
{% if product.hs_product_type %}
<div class="type" style="display: none;">{{ product.hs_product_type|lower|replace(" ", "-") }}</div>
<div class="mt-2">{{ product.hs_product_type }}</div>
{% endif %}
{% if product.category %}
<p class="italic text-secondary-500 text-xs mt-2">Category: {{ product.category }}</p>
{% endif %}
<h3 class="product-name mt-4 {{ module.style.card.title_font_weight }} {{ module.style.card.title.text_color }} {{ module.style.card.title_font_size.breakpoints.xs }} {{ module.style.card.title_font_size.breakpoints.sm }} {{ module.style.card.title_font_size.breakpoints.md }} {{ module.style.card.title_font_size.breakpoints.lg }} {{ module.style.card.title_font_size.breakpoints.xl }} {{ module.style.card.title_font_size.breakpoints.xxl }}">{{ product.name }}</h3>
<p style="font-weight: bolder;" class="mt-2 {{ module.style.card.price_font_weight }} {{ module.style.card.price_color.text_color }}">{{ product.price }}</p>
{% if product.stock_level %}
<p style="font-weight: bolder;" class="mt-2 font-medium text-secondary-900">Stock Level: {{ product.stock_level }}</p>
{% endif %}
{% if product.description %}
<div class="js-read-smore mt-4" data-read-smore-words="30" data-read-smore-inline="true">
<p class="italic text-secondary-500">{{ product.description|striptags|truncatehtml(100) }}</p>
</div>
{% endif %}
</a>
{% endfor %}
</div>
<ul class="pagination"></ul>
</div>
</div>
</div>
</div>
{% require_js position="footer" %}
<script src="//cdnjs.cloudflare.com/ajax/libs/list.js/2.3.1/list.min.js"></script>
<script>
var options = {
valueNames: [
'product-name',
'type',
{ data: ['category']}
],
page: 12,
pagination: true
};
var productList = new List('products', options);
document.getElementById('product-category').addEventListener("change", function() {
// Reset Radio Button
var ele = document.querySelectorAll("input[type=radio]");
for(var i=0;i<ele.length;i++){
ele[i].checked = false;
}
const dropdown_value = this.value;
productList.filter();
if (dropdown_value) {
let url = window.history.pushState({}, "Filtering With URL Hash", "#"+dropdown_value);
productList.filter(function(item) {
return (item.values().category == dropdown_value);
});
} else {
productList.filter();
}
});
if (document.querySelector('input[name="type"]')) {
document.querySelectorAll('input[name="type"]').forEach((elem) => {
elem.addEventListener("change", function(event) {
// Reset Select Product Category
var dropDown = document.getElementById("product-category");
dropDown.selectedIndex = 0;
var radioselect = event.target.value;
productList.filter();
if (radioselect) {
let url = window.history.pushState({}, "Filtering With URL Hash", "#"+radioselect);
// console.log(radioselect);
productList.filter(function(event) {
var status = event.values().type;
return status == radioselect;
});
}
});
});
}
/* Start: Shareable URL Filter with Hash */
window.addEventListener('hashchange', fn, false);
window.onload = fn;
function fn() {
document.addEventListener('click', function (event) {
// If the clicked element doesn't have the right selector, bail
if (!event.target.matches('.page')) return;
// Don't follow the link
event.preventDefault();
var anchor = document.querySelector('.product-library-filter');
anchor.scrollIntoView({behavior: "smooth"});
}, false);
productList.filter();
document.getElementById('product-category').value = window.location.hash.replace('#','');
var product_type = window.location.hash.replace('#','');
let product_category = document.getElementById('product-category').value;
let url_hash = window.location.hash.replace('#','');
// console.log(product_type);
// radiobutton = document.getElementById("radio_1");
// radiobutton.checked = true;
var radios = document.querySelectorAll(".filter");
for (var radio of radios)
{
if (product_type == radio.value) {
radio.checked = true;
productList.filter(function(item) {
return (item.values().type == product_type);
});
var dropDown = document.getElementById("product-category");
dropDown.selectedIndex = 0;
}
}
if (product_category == url_hash && product_category != '') {
productList.filter(function(item) {
return (item.values().category == product_category);
});
}
}
/* END: Shareable URL Filter with Hash */
</script>
{% end_require_js % - Sample Output