Custom Hugo Content Filter

Hi guys!

I want to migrate from wordpress to Hugo. But i have a filter functionality that is really important, using this plugin:

https://facetwp.com/demo/cars/

Have you guys done some similiar?

Thanks,
Ed!

Yes it’s possible to create a content filter in Hugo with minimum or no JS at all!

I just made a color filter for a product catalogue, using Hugo’s tags on individual posts, a special nested color parameter in the _index.md of several sections and nested sections and I got it up and running using the checkbox CSS hack.

It works GREAT. It is less complicated than it sounds. And you should be able to get this up and running in no time.

There are a couple of minor caveats, though.

  1. In my use case I had to turn off pagination because i was getting some problems with nested sections. I was unable to render the first page on the mainSection. There is an open issue about this over here: https://github.com/gohugoio/hugo/issues/3297
  2. Since everything is in one page lazy loading images on scroll and loading images when a filter option is clicked is an absolute MUST.

But I’m pretty happy. And best of all I don’t have to use any of the paid content filtering plugins that are out there. And since this is CSS based it’s pretty damn fast.

Do you mind sharing the page? :slight_smile:

It’s not online yet.

But I can write a detailed tutorial for you (and others).

However I cannot do it right now. I’ll post the tutorial as a separate thread sometime on Monday, and let you know.

4 Likes

I wanted to add that while “filtering” can be done in Hugo with proper site structure and creative thinking, it doesn’t emulate the strength and functionality of FacetWP.

So consider what you are using it for. The number of facets available are not going to be replicated in Hugo, because they are two very different things.

I really enjoy FacetWP, and if I were needing that functionality, I would probably run a WordPress site just for it, and would feed the database with content from the site. Then just link to it or send search form traffic to something like https://search.example.com.

Well, also if you don’t want to build the interface in Javascript and CSS. :stuck_out_tongue:

I was thinking of the theme showcase, which granted only uses a taxonomy to do a simple filter, but understanding that will go a long way to help peeps figure out how to build their own filters.

@alexandros make sure to publish that elsewhere, so you can keep it up to date on not-forums, and can be linked to, etc. :slight_smile:

1 Like

Sorry to bring this thread back from the dead, but I’m wondering if you ever got around to writing this tutorial? Sounds like this is exactly what I’m looking for.

Hello,

I wrote that tutorial sometime ago and then deleted it because it was a cumbersome technique and I have moved away from it.

In any case here it is since you asked:


I’m going to post my tutorial in this thread after all. For reference’s sake I edited the original topic title.

So here is how to make A Poor Man’s Content Filter in Hugo.

Obviously this is not something as robust as the FacetWP that was discussed above but it gets the job done. I guess that there is room for improvement. So if anyone has suggestions or a better way to create a content filter in a Hugo project please share.

OK. First things first. This is a tutorial for a content filter based on color and I am using this in a product catalogue.

I am using the excellent B-lazy script to lazy load images. For instructions to set it up see: GitHub - dinbror/blazy: Hey, be lazy! bLazy.JS is a lightweight pure JavaScript script for lazy loading and multi-serving images. It's working in all modern browsers including IE7+.

Then under /layouts/_default/list.html my template code to render individual products looks like this

<div class="post-item {{ delimit .Params.tags " " }}">
<a href="{{ .Permalink }}" class="post-link">
<img class="b-lazy" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="{{ with .Params.image }}{{ . | absURL }}{{ end }}" alt="{{ .Title }}"/>
<noscript>
<img src="{{ with .Params.image }}{{ . }}{{ end }}" alt="{{ .Title }}"/>
</noscript>

In individual product markdown files I include the following front matter parameters

+++
title = "Product"
description = "Lorem Ipsum Dolor Si Amet"
image = "/images/some-thumb.jpg"
tags = [ "fuchsia" ]
+++

As you can see I am assigning the color of a product with a tag combined with my list.html template this will be rendered as

<div class="post-item fuchsia">

Then in _index.md of my products Section I include the following frontmatter

+++
[color]
  fuchsia = "fuchsia"
+++

Now to render my color filter I am using the Checkbox CSS hack

The template under /layouts/_default/list.html looks like this

{{- with $.Params.color -}}
<div class="colors">
<div class="label-section"><span>COLORS</span></div>
  <label class="filtro selected" for="reset" onclick="show()"><span onclick="show();modal.close();">All</span></label>
{{- range $key, $value := $.Params.color -}}<label for="{{ $key }}"><span>{{ $value | title }}</span></label>{{- end -}}</div>{{- end -}}
</div>
<div class="list">
<input type="radio" id="reset" name="color"/>
{{- range $key, $value := $.Params.color -}}<input type="radio" id="{{ $key }}" name="color" />{{- end -}}

Here is the CSS to make the content filter work.
This hides the radio buttons:

input[type="radio"] {
display:none;
  font-size: 0 !important;
}

And this is the magic part (you will need to repeat it for each color):

 input[type="radio"][id="fuchsia"]:checked ~ .post-item:not(.fuchsia) {
  width:0;
  height:0;
  padding:0;
  margin:0;
 opacity:0;
transform: scale(0);

}

If you don’t want to use CSS transitions just change the above to

 input[type="radio"][id="fuchsia"]:checked ~ .post-item:not(.fuchsia) {
  display:none;
} 

Then to include a transition for .post-item use the following:

.post-item {
    display: inline-block;
    transition:opacity 0.5s ease-out;
    transform: scale(1);
}

Now as I said in my first reply in this thread I do not use pagination while we wait for .mainSections to be used with the range function.

So a script is needed to load the lazy images that are not in the viewport when a color option is clicked. Include the following just before </body> or wherever you think it’s best.

<script type="text/javascript">
   function show() {
var images = document.querySelectorAll("img:not(.b-loaded)");
  for (var i = 0, len = images.length; i < len; i++)
  {
  	images[i].setAttribute("style", "padding-top:0;");
    images[i].src = images[i].getAttribute('data-src');
  }
}
</script>

The above loads images that were not loaded by b-lazy (as the above function will cause already loaded images to disappear) and also I am setting padding-top to zero as I am using padding ratio to make sure that the images take up their space even before they are loaded (you can find how to calculate padding-ratio for your images easily on Google :wink:).

And then back to my color filter list under /layouts/_default/list.html I simply call the above function onclick like this

{{- range $key, $value := $.Params.color -}}<label for="{{ $key }}"><span onclick="show();">{{ $value | title }}</span></label>{{- end -}}</div>{{- end -}}

Room for improvement.
I would like to load the color list for my filter from data files. Because Go Templates randomizes the color list order. Also as you will probably see if you use this technique you need to manually enter the color list in several _index.md for the main section as well as the nested sections, that can be a bit of a pain but still it’s doable for smallish projects.

And that’s pretty much how I made a custom content filter for a Hugo project. No need to pay for fancy plugins unless of course you have a huge site and you really need a lot of filtering options.

2 Likes

This topic was automatically closed after 8 days. New replies are no longer allowed.