HubSpot Product Library with Filter

Demo

Notes: The easier approach is to create a page using pre-built template called "Product Category".

Instructions:

  1. Go to Edit Page 
  2. Paste "HubSpot Product Library with Filter" into input

    4410ce18-fde4-44e8-95c4-1b3dbd522f68
  3. Drag highlighted option

    24d9248a-e160-47d6-8869-7f89139c0118
  4. 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 %


    bf48d4d5-dd44-4d72-9525-0f5db585533b
  5. Sample Output

    c6a11b55-5b44-48b4-b011-235b7f074fdc