Filter by posts within a single page

Hi there!
Clarify please, is it possible to filter posts by internal tag or maybe by other attributes? Like here — https://d.pr/free/v/voiENq.

I have a main tag — Shop. Different types of products are available within this tag. I would like to filter them to ensure the fastest possible access to the desired product. However, I’d like to implement everything within one page and don’t split all products into different tags.

Yes, this is certainly possible.

In the video you linked, and in the use case you specified, you will need to modify your theme (obviously) in order to accomplish this. There are various built-in handlebars helpers which you can use to get the desired effect. Generally, you will need to be comfortable with handlebars development, so I suggest you read through the documentation and reference it frequently while working.

So, how would you do this? On your filtering html element, you need to have a listener which runs a function that modifies your div class="post-feed" or your <div class="inner"> assuming that’s what you call them in your theme. Personally, my first attempt would be to write a separate div, <div class="post-feed-2"> but make it hidden: <style>.post-feed-2{display:none}</style>. Within post-feed-2, you could have some filtering, like:

{{#foreach posts}}
	{{#has tag="#someSpecialInternalTag"}}
		{{> "post-card"}}
	{{/has}}
{{/foreach}}

Then, your event listener would just have to hide and un-hide the different post feeds, which all reside within inner. That should work, but no promises. I work with a trial and error workflow :wink: .

There might be some better genius way of doing this, but I can’t come up with it at the moment.

2 Likes

I’ll try, huge thanks, Apellus!

Apellus, hi!

I got the following result. Initially loaded correctly, but after clicking on the first filter (any), the desired styles are disabled. I absolutely did something wrong, but I can’t figure out what it was.

Current version:

Webpage — https://www.edt.im/shop/, check it, please.
HBS — tag-shop-2.hbs — Yandex.Disk

Filter:

<ul class="nav">
        <li><a class="posters" onclick="show('post-feed-posters'); hide('post-feed','post-feed-icons');" href="#">Posters</a></li>
        <li><a class="icons" onclick="show('post-feed-icons'); hide('post-feed','post-feed-posters');" href="#">Icons</a></li>
        <li><a class="post-feed" onclick="show('post-feed'); hide('post-feed-icons','post-feed-posters');" href="#">All</a></li>
</ul>

JS for show/hide:

<script>
    function show(elementId) { 
        document.getElementById("post-feed-posters").style.display="none";
        document.getElementById("post-feed-icons").style.display="none";
        document.getElementById("post-feed").style.display="none";
        document.getElementById(elementId).style.display="block";
    }
</script>

Posts fetching:

For posters:

<div id="post-feed-posters" class="row loop">
{{#foreach posts}}
    {{#has tag="#shopPoster"}}
        <div class="col-md-6 col-lg-4 {{#if @first}}first{{/if}} item"></div>
        {{> "loop"}}
    {{/has}}
{{/foreach}}     
</div>

For icons:

<div id="post-feed-icons" class="row loop">
{{#foreach posts}}
    {{#has tag="#shopIcons"}}
        <div class="col-md-6 col-lg-4 {{#if @first}}first{{/if}} item"></div>
        {{> "loop"}}
    {{/has}}
{{/foreach}}     
</div>

All posts:

<div id="post-feed" class="row loop">
    {{#foreach posts}}
        <div class="col-md-6 col-lg-4 {{#if @first}}first{{/if}} item">
            {{> "loop"}}
        </div>
    {{/foreach}}
</div>

Hey @everydaytem :wave:
I think this can be approached in a more straight forward way.

If I understand correctly, you have a series of posts that are parts of different categories (posters and icons). You also don’t want them to be public tags, so internal tags are best for this. Internal tags can be exposed by using the visibility="all" attribute on the tags helper. Here’s an example:

<div class="posts" data-filter-container>
  {{#foreach posts}}

    <div class="{{#foreach tags visibility="all"}}{{slug}} {{/foreach}}">
      {{> "loop"}}
    </div>

  {{/foreach}}
</div>

In this example I’ve applied the tags to the class value so I can target the posts with CSS. Next, to achieve the filtering mechanism I’m going to employ some JavaScript and CSS. Here’s what I have for my filtering navigation:

<ul class="navigation">
  <li><button data-filter-control="posters">Posters</button></li>
  <li><button data-filter-control="icons">Icons</button></li>
  <li><button data-filter-control="all">All</button></li>
</ul>

I’ll use JavaScript to target these elements, as well as my container for all my posts:

<script type="text/javascript">
  const filterContainer = document.querySelector("[data-filter-container]");
  [...document.querySelectorAll("button[data-filter-control]")].map(button => {
    button.addEventListener("click", event => {
      filterContainer.dataset.filterContainer = event.target.dataset.filterControl;
    });
  });
</script>

Any time a button is clicked, the value of data-filter-control on the button will get applied to the value of data-filter-container on the post container. So now with CSS I can target certain elements to hide and show posts depending on those values:

<style>
  [data-filter-container="posters"] > div:not(.hash-posters) {
    display: none;
  }
  [data-filter-container="icons"] > div:not(.hash-icons) {
    display: none;
  }
</style>

Note that this CSS uses the :not selector, which means the default for the post container is to show all posts.

Hope this is clear enough! If you need any more guidance just let us know :blush:

4 Likes

Wohhou! It works! :slight_smile:

@DavidDarnes @Apellus my deepest thanks to both of you!

1 Like