Filter Hubspot Database by More than One Filter

Hi All
Currently someone can filter by topic and type but it just adds both filtered types to the results list. Channel Marketing Resources | 360insights For instance if someone selects Funds Management (topic) and Case Studies (type) it shows all items relating Funds Management and all the case studies. What I would like to show is just case studies about Funds Management.
HTML:

<div class="resource-grid">

	{# Filters #}

 <div class="resource-filters">

		<form>

			<div class="resource-filters__search">
 <input type="text" placeholder="Search" onkeypress="return event.keyCode != 13;">
 </div>

			<div class="resource-filters__filter">
 <fieldset>
 <div class="resource-filters__filter-group mb-md">
 <legend>Browse by Topic</legend>
 <!--<h2 class="h4">Browse by Topic</h2>-->
 {% set resources = hubdb_table_rows(module.hubdb_id) %}
 {% set topics = hubdb_table_column(module.hubdb_id, "topic").options %}
 {% for topic in topics %}
 <label><input type="checkbox" value="topic-{{ topic.name|lower|replace(" ", "-")|replace("&-", "")|replace(",", "") }}" {% if request.query_dict.topic == topic.name|lower|replace(" ", "-")|replace("&-", "")|replace(",", "") %}checked{% endif %} />{{ topic.name }}</label>
 {% endfor %}
 </div>
 </fieldset>
 <fieldset>
 <div class="resource-filters__filter-group mb-md">
 <legend>Browse by Type</legend>
 <!--<h2 class="h4">Browse by Type</h2>-->
 {% set resources = hubdb_table_rows(module.hubdb_id) %}
 {% set types = hubdb_table_column(module.hubdb_id, "type").options %}
 {% for type in types %}
 <label><input type="checkbox" value="type-{{ type.name|lower|replace(" ", "-")|replace("&-", "")|replace(",", "") }}" {% if request.query_dict.type == type.name|lower|replace(" ", "-")|replace("&-", "")|replace(",", "") %}checked{% endif %} />{{ type.name }}</label>
 {% endfor %}
 </div>
 </fieldset>
 <fieldset>
 <div class="resource-filters__filter-group ">
 <legend>Browse by Featured Topic </legend>
 <!--<h2 class="h4">Browse by Featured Topic </h2>-->
 {% set resources = hubdb_table_rows(module.hubdb_id) %}
 {% set types = hubdb_table_column(module.hubdb_id, "featured_resource").options %}
 {% for type in types %}
 <label><input type="checkbox" value="featured-{{ type.name|lower|replace(" ", "-")|replace("&-", "")|replace(",", "") }}" {% if request.query_dict.featured == type.name|lower|replace(" ", "-")|replace("&-", "")|replace(",", "") %}checked{% endif %} />{{ type.name }}</label>
 {% endfor %}
 </div>
 </fieldset>
			</div>

		</form>

	</div>

	{# Results #}

 <div class="resource-items">

		{% set queryparam = "orderBy=order&inactive=false" %}

		{% set resources = hubdb_table_rows(module.hubdb_id, queryparam) %}

		{% for resource in resources %}
		<a href="{{ resource.link }}" {% if resource.open_link_in_new_tab %}target="_blank"{% endif %} class="resource-items__item {% for item in resource.featured_resource %}featured-{{ item.name|lower|replace(" ", "-")|replace("&-", "")|replace(",", "") }} {% endfor %} {% for item in resource.topic %}topic-{{ item.name|lower|replace(" ", "-")|replace("&-", "")|replace(",", "") }} {% endfor %} {% for item in resource.type %}type-{{ item.name|lower|replace(" ", "-")|replace("&-", "")|replace(",", "") }} {% endfor %}">
			{% if resource.image %}
			<img src="{{ resize_image_url(resource.image.url, 992, 620, 992) }}" loading="lazy" alt="{{ resource.name }}" class="resource-items__item-image">
			{% endif %}
			<div class="resource-items__item-details">

 {% for item in resource.type %}
 <p class="resource-items__type">{{ item.name }}</p>
 {% endfor %}

				<h3 class="resource-items__item-name">{{ resource.name }}</h3>
				{% if resource.description %}
				<p class="resource-items__item-description">{{ resource.description }}</p>
				{% endif %}

 <span class="button button--arrow button--notext">{{ resource.button_text if resource.button_text : 'Download' }}</span>
			</div>
		</a>
		{% endfor %}
	</div>

</div>

{{ require_js("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js", "head") }}

JS:

// quick search regex
var qsRegex;
var checkboxFilter;

// init Isotope
var $container = $('.resource-items');

// filter with checkboxes
var $checkboxes = $('.resource-filters__filter input');

var $quicksearch = $('.resource-filters__search input[type="text"]').keyup( function() {
 qsRegex = new RegExp( $quicksearch.val(), 'gi' );
});

// debounce so filtering doesn't happen every millisecond
function debounce(fn, threshold) {
 var timeout;
 threshold = threshold || 100;
 return function debounced() {
 clearTimeout(timeout);
 var args = arguments;
 var _this = this;

 function delayed() {
 fn.apply(_this, args);
 }
 timeout = setTimeout(delayed, threshold);
 };
}

// Match Height
function equalHeight() {
 let counter = 0;

 let tallestHeight = 0;
 $('.resource-items__item').css('height', 'auto');

 $('.resource-items__item:visible').each(function(i, el) {
 counter++;
 $(el).css('height', 'auto');

 if($(el).height() > tallestHeight) {
 tallestHeight = $(el).height();
 }

 if(counter % 3 == 0 && $(window).width() >= 992) {
 for(; counter > 0; counter--) {
 $('.resource-items__item:visible').eq(i-(counter-1)).css('height', tallestHeight);
 }
 tallestHeight = 0;
 counter = 0;
 } else if(counter % 2 == 0 && $(window).width() < 992 && $(window).width() >= 577) {
 for(; counter > 0; counter--) {
 $('.resource-items__item:visible').eq(i-(counter-1)).css('height', tallestHeight);
 }
 tallestHeight = 0;
 counter = 0;
 } else if($(window).width() < 577) {
 $('.resource-items__item').css('height', 'auto');
 } else if((i+1) == $('.resource-items__item:visible').length) {
 for(; counter > 0; counter--) {
 $('.resource-items__item:visible').eq(i-(counter-1)).css('height', tallestHeight);
 }
 }
 });

 $container.isotope('layout');
}

function Filter() {
 // map input values to an array
 var inclusives = [];
 // inclusive filters from checkboxes
 $checkboxes.each(function(i, elem) {
 // if checkbox, use value if checked
 if (elem.checked) {
 inclusives.push(elem.value);
 }
 });

 var text = $quicksearch.val()

 $container.isotope({
 itemSelector: '.resource-items__item',
 percentPosition: true,
 layoutMode: 'fitRows',
 masonry: {
 columnWidth: '.resource-items__sizer'
 },
 filter: function() {
 var $this = $(this)
 var searchResult = qsRegex ? $this.text().match( qsRegex ) : true;
 var checkClass = inclusives.length == 0 ? true : false;
 $.each(inclusives, function(index, c) {
 var _class = $this.hasClass(c)
 if (_class) {
 checkClass = true;
 return;
 }
 })
 return searchResult && checkClass
 }
 });

 setTimeout(function() {
 equalHeight();
 }, 500);
}

Filter();

$quicksearch.on('input', debounce(function() {
 Filter();
}));

$checkboxes.change(function() {
 Filter();
});

$(window).on('load', function(){
 $(window).resize(function() {
 equalHeight();
 });
});

Thanks in advance for any help.

Hi @Woodsy

The existing code piece showcases the current configuration for filtering HubSpot database entries based on topic and type.

Seems, it utilizes a logical OR (||) to combine all selected filters, leading to the behavior where all items related to any selected filter are displayed.

To achieve filtering with an AND logic, which shows items that match all selected criteria, you can modify the code as follows: update the filter function within the isotope call to implement an AND logic for filtering based on both checkboxes and text search. This updated approach will ensure that only items that meet all the selected filters are displayed.

Hope this helps - Happy to help further!!
Thank you very much and have a great one!
Warm regards

Hi, thanks for the reply. I’m learning JS as I go. From you reply I think I need to edit this area:

 filter: function() {
 var $this = $(this)
 var searchResult = qsRegex ? $this.text().match( qsRegex ) : true;
 var checkClass = inclusives.length == 0 ? true : false;
 $.each(inclusives, function(index, c) {
 var _class = $this.hasClass(c)
 if (_class) {
 checkClass = true;
 return;
 }
 })
 return searchResult && checkClass
 }

to include && please correct if I’m wrong?
Thanks

Hi @Woodsy

You’re absolutely right!

The filter function needs to be modified to implement AND logic using the && operator, ensuring all selected filters (both topic and type) are present on the item.

The logic needs to be updated to check if all filters are present using &&, so that only items matching all selected criteria are displayed

filter: function() {

var $this = $(this);

var searchResult = qsRegex ? $this.text().match(qsRegex) : true;

var topicMatch = topicFilters.length === 0 || topicFilters.every(function(topic) {

return $this.hasClass(topic);

});

var typeMatch = typeFilters.length === 0 || typeFilters.every(function(type) {

return $this.hasClass(type);

});

return searchResult && topicMatch && typeMatch;

}

We implemented topicFilters and typeFilters as discussed earlier.

The topicMatch and typeMatch variables utilize the ‘every’ method to verify if all elements in their respective filter arrays have a matching class on the item.

After all, the return statement combines the search text match, topic filter match, and type filter match using the && operator, achieving AND logic to ensure all conditions are met – give a try and see if this solves things for you.

Regards

Hi, before I change the code the view is this:

After I update the code this happens:

The code I have added is:

 filter: function() {
 var $this = $(this);
 var searchResult = qsRegex ? $this.text().match(qsRegex) : true;
 var topicMatch = topicFilters.length === 0 || topicFilters.every(function(topic) {
 return $this.hasClass(topic);
 });
 var typeMatch = typeFilters.length === 0 || typeFilters.every(function(type) {
 return $this.hasClass(type);
 });
 return searchResult && topicMatch && typeMatch;
 }
 });

Just wondering if I needed to adjust anything? Thanks

Hi @Woodsy

Thanks for sharing the outcome.

By looking the visual issue isn’t related to the code, Since the code seems to be working as intended for filtering, the visual clarity and wavering you’re experiencing are likely due to a separate issue.

Can you also list few pointers from your expectation in your own words:

What is your expected end result and

What is your current reach of point to address ?

To take it further

Regards

Hi, when I check the boxes on the left the shown cards do not change. Even if it issn’t being displayed correctly there should be some change. Is that correct? Thanks

Hi @Woodsy

That’s correct, even if there’s a visual issue or the website isn’t displaying perfectly, you should still see some change in the displayed cards when you check the boxes on the left, assuming the code is working as intended.

If you’re not seeing any changes, there might be a partial code issue at play. While the filtering logic might be working correctly, checking for topics and types, there could be a separate issue with how the filtered results are displayed visually, resulting in no apparent changes to the cards.

This suggests that the problem lies in the visualization aspect, rather than the filtering logic itself.

Hi, I’ve troed tweeking the code but without any luck. Before changing, clicking on Case Studies would pull these results:

When I add the code and select Case Studies nothing changes. I’m assuming that is the code was fireing correctly then we would expect to see one of the case studies images showing rather than what is showing which isn’t a cases study.

Just wanted to check with you in case this isn’t what would be expected.
Thanks

Hi @Woodsy

To troubleshoot the issue with the isotope filtering functionality, start by checking the browser’s developer console for any JavaScript errors related to the filtering code.

These errors can provide valuable clues about what’s going wrong. Next, verify that the user is selecting the correct filter option, in this case, the “Case Studies” checkbox, and that the class names on the items match the expected value (“type-case-studies”).

Then, inspect the code that builds the inclusives array, which should contain the selected filter values, and ensure that the logic for adding checkbox selections to the array is working correctly.

Also, to isolate the issue, try temporarily removing the search functionality and test the filtering with a single selection.

After all, consider other potential factors that might be affecting the filtering behavior, such as missing classes or conflicts with other JavaScript code.

Regards

Hi, I’ve inspected the code via the dev console and have found this error:

What would the best way be to drill down to find out why the topicFilters is not defined?
Thanks

Hi @Woodsy

The error message “Uncaught ReferenceError: topicFilters is not defined” shows the variable topicFilters is used before being assigned a value.

The code defines an anchor element with an onclick event that calls the filter function. Inside filter, it tries to use topicFilters which is undefined.

For fixing this, you can declare topicFilters and assign it a value before using it in filter

Below one is the sample for declaring:

var topicFilters; // Declare the variable

function filter() {

// Assign a value to topicFilters before using it

topicFilters = getTopicFilters(); // Hypothetical function to retrieve filters

// Rest of the filter function logic using topicFilters

}

To locate the root cause, explore other parts of your code where topicFilters might be defined or populated.

Use console logs to track its value and see how it’s defined. Sharing more context about how topicFilters is supposed to be populated will further assist in triaging the issue.

Regards

Hi, in the original code that worked there was checkClass referenced that is not used in the revised version that you suggested. Just wanted to check in case the removal of it was in part causing the error.

 filter: function() {
 var $this = $(this)
 var searchResult = qsRegex ? $this.text().match( qsRegex ) : true;
 var checkClass = inclusives.length == 0 ? true : false;
 $.each(inclusives, function(index, c) {
 var _class = $this.hasClass(c)
 if (_class) {
 checkClass = true;
 return;
 }
 })
 return searchResult && checkClass
 }
 });

Thanks

Hi @Woodsy

Let me get back to you on this.

Regards

Hi Humashankar, just checking to see if you had any further thoughts on including checkClass within the script might reslove the error?

Hi @Woodsy

Thats a great thught process, please include the checkClass and keep me posted with the outcome, will take it from there.

The only thing is I’m not sure how to include it within the reworked script. Would you be able to take a look when convenient? Thanks

Yes - i can help you on that, can you please provide the latest code layer you are trying to incorporate.

Thanks
The original code that was displaying correctly was:

filter: function() {
 var $this = $(this)
 var searchResult = qsRegex ? $this.text().match( qsRegex ) : true;
 var checkClass = inclusives.length == 0 ? true : false;
 $.each(inclusives, function(index, c) {
 var _class = $this.hasClass(c)
 if (_class) {
 checkClass = true;
 return;
 }
 })
 return searchResult && checkClass
 }

The code that has been updated to include filtering of both topic, type and search:

 filter: function() {
 var $this = $(this);
 var searchResult = qsRegex ? $this.text().match(qsRegex) : true;
 var topicMatch = topicFilters.length === 0 || topicFilters.every(function(topic) {
 return $this.hasClass(topic);
 });
 var typeMatch = typeFilters.length === 0 || typeFilters.every(function(type) {
 return $this.hasClass(type);
 });
 return searchResult && topicMatch && typeMatch;
 }
 });

Please try testing with different combinations of topics and types to see if any filters affect the displayed cards. If none of the filters seem to have any impact, it’s possible that there’s a more general issue with the filtering functionality.

In that case, try accessing the website with a different browser to troubleshoot whether the problem is specific to your current browser or a more extensive issue.