Using buttons to filter post within same page

Hi all. I am working on a very basic site and have been seeking a way to filter posts based on user input. Currently I am generating a list of tags for all of my posts via this code:

<div>
    {{ range .Site.Taxonomies.tags }}
    <button><a href="{{ .Page.Permalink }}">{{
            .Page.Title
            }}</a><sup> {{ .Count }}</sup></button>
    {{ end }}
</div>

To get this output:

Now, I want a user to be able to click the button and dynamically filter the posts they want to see. Presently, I’m just linking to the list page that contains the posts with that tag but rather I want to change the visible posts based on user selection and not link to another list page. This is a pretty basic feature on many sites that allow user select but I cannot for the life of me figure it out in hugo.

Can anyone give me guidance on 1) if this is possible 2) if so, a way to get started on it?

Thanks so much!

Hi there,

this is a feature I searched for a while ago. Here are some code examples I collected:

Hope that helps, cheers

1 Like

Your html is invalid. You can’t have a link inside a button! :scream:

Buttons do things, links go somewhere; decide what you want.

This is nothing to do with Hugo. If you don’'t want a user to go to another page, use a button. Then you’ll need Javascript to show/hide results, again not a Hugo thing. This is functionality that you will need to implement yourself.

1 Like

Thank you for the help, these are exactly what I was looking for.

1 Like

In case anyone ends up on this thread looking for the same functionality, here is the solution I ended up with based on the examples provided by @iaeiou

  1. First I have a div that generates a button for each tag in any of my posts
<div id="tag-buttons">
    <button>All</button>
    {{ range .Site.Taxonomies.tags }}
    <button>{{ .Page.Title }}</button>
    {{ end }}
</div>

  1. You need to display those posts somehow, in my case I’m generating a grid to place the posts in with this code (note, I’m using tailwind css so the grid is generated with the classes):
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
    {{ range where .Site.RegularPages ".Params.unlisted" "!=" "true" }}
    <div class="p-4 post {{ range .Params.tags }}{{ . }} {{ end }}">
        <a href="{{ .RelPermalink }}">
            <div class="w-full h-0 aspect-w-4 aspect-h-3">
                <img src="{{ .Params.previewImage }}" alt="{{ .Title }}" class="object-cover mb-2 rounded-lg">
            </div>
            <h2 class="text-lg font-semibold mb-2">{{ .Title }}</h2>
        </a>
        <p class="text-gray-600 mb-4">{{ .Description }}</p>
        <div class="flex flex-wrap tags">
            {{ range .Params.tags }}
            <a href="#" data-tag="{{ . }}" class="tag-link">
                <span class="text-sm mr-2 mb-2 underline">{{ . }}</span>
            </a>
            {{ end }}
        </div>
    </div>
    {{ end }}
</div>
  1. Finally you need to implement some javascript to hide and show your posts based on what button was clicked. I did so in an inline script tag:
<script>
    // Wait for the page to load
    window.addEventListener("DOMContentLoaded", function () {
        // Get all the buttons and posts
        const buttons = document.querySelectorAll("#tag-buttons button");
        const posts = document.querySelectorAll(".post");

        // Add click event listener to each button
        buttons.forEach(function (button) {
            button.addEventListener("click", function () {
                // Remove 'active' class from all buttons
                buttons.forEach(function (btn) {
                    btn.classList.remove("bg-blue-500", "text-white");
                    btn.classList.add("bg-gray-200", "text-gray-700");
                });

                // Add 'active' class to the clicked button
                button.classList.remove("bg-gray-200", "text-gray-700");
                button.classList.add("bg-blue-500", "text-white");

                if (button.textContent === "All") {
                    // Show all posts if 'All' button is clicked
                    posts.forEach(function (post) {
                        post.style.display = "block";
                    });
                } else {
                    const tag = button.textContent.trim();

                    // Hide all posts
                    posts.forEach(function (post) {
                        post.style.display = "none";
                    });

                    // Show only posts with the selected tag
                    const filteredPosts = document.querySelectorAll("." + tag);
                    filteredPosts.forEach(function (post) {
                        post.style.display = "block";
                    });
                }
            });
        });
    });
</script>

Hope this helps people!

2 Likes

I create a site with this exact needs.

I use buttons to look like there are “filters” but all is precompute.

And I do not use javascript just plain HTML.

HTH.

1 Like

@divinerites Is there a public demo running somewhere?

EDIT: ok I found it: https://cp-videos.netlify.app/

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.