11th Oct '22
/
1 comment

How to add a layout toggle on query loops in Bricks

How to add a layout toggle on query loops in Bricks

In this tutorial, we’ll show you how to change the layout of your query loop container based on the click of an icon toggle sitting on top of it.

Table of Contents

Introduction

Are you willing to let your users choose from different layout options for your query loop grid?

This tutorial will try to explain how without using external plugins, js libraries, or duplicating the content (you really don’t want to hurt your SEO content optimization, right?), but just by adding a bit of custom code to your page.

DOM Structure

Recreate the following structure and apply the classes as shown below. Note that the post container style/tree is totally up to you – it has no impact on the script.

IMPORTANT: make sure to add the .toggle-layout--active class to the default active toggle. Feel free to style this class as you wish.

On each toggle layout, add the following attributes:

For the data-layout attribute, choose a unique name that describes the layout as a value. In our example, we added list, list-two-col, and grid. These names will be used as class names and will be added to the query container in order to style our inner elements based on the specific layout.

For the data-layout-col attribute, insert the number of columns that will be applied to the query container:

The JavaScript

Insert the following code inside your page. My personal recommendation is to use the conditional enqueue method described here:

// Run the script once the DOM has been fully loaded - avoid render blocking
window.addEventListener('DOMContentLoaded', () => {

   // Query all the containers with layout toggles
   const containers = document.querySelectorAll('.loop-ctn-w-layout-toggles');

   // Check if there are containers with layout toggles on the page
   if (containers.length < 1) return console.log('There is no container with layout toggles on this page. Double check you correctly added the .loop-ctn-w-layout-toggles class on the main container.');

   // Loop into all of them
   containers.forEach(container => {

      // Query the toggle container
      const toggleContainer = container.querySelector('.toggle-container');

      // Check if there is a toggle container
      if (!toggleContainer) return console.log('There is no toggle container. Double check you correctly added the .toggle-container class on the toggle container.');

      // Query the query container
      const queryContainer = container.querySelector('.query-container');

      // Check if there is a query container
      if (!queryContainer) return console.log('There is no query container. Double check you correctly added the .query-container class on the query container.');

      // Query all the toggles
      const toggles = toggleContainer.querySelectorAll('.toggle-layout');

      // Query all the active toggle
      const activeToggle = toggleContainer.querySelector('.toggle-layout--active');

      // Set the active layout on load
      queryContainer.classList.add(activeToggle.dataset.layout);

      // Check if there are toggles
      if (toggles.length < 1) return console.log('There are no layout toggles. Double check you correctly added the .toggle-layout class on each toggle.');

      // Create an empty array for all the different layout classes
      let layouts = [];

      // Populate the array with all the different layout classes
      toggles.forEach(toggle => {

         // Check if the data-layout attribute has been corretly set
         if (!toggle.dataset.layout) return console.log('The attribute data-layout has not been correctly set on the toggles.');
         layouts.push(toggle.dataset.layout);
      });

      // Loop into each toggle
      toggles.forEach(toggle => {

         // Create an Event listener when a toggle get clicked
         toggle.addEventListener('click', (e) => {

            // prevent default actions
            e.preventDefault();

            // get the layout data attribute
            const layout = toggle.dataset.layout;

            // Avoid running the same animation if the toggle is already set correctly           
            if (queryContainer.classList.contains(layout)) return;

            // get the number of col from the attribute 
            const col = toggle.dataset.layoutCol;

            // Check if the data-layout-col attribute has been corretly set
            if (!toggle.dataset.layoutCol) return console.log('The attribute data-layout-col has not been correctly set on the toggles.');

            // remove existing actuve classes
            toggles.forEach(el => el.classList.remove('toggle-layout--active'));

            // add the current the active class to the clicked toggle
            toggle.classList.add('toggle-layout--active');

            // Remove existing classes from the query container
            queryContainer.classList.remove(...layouts);

            setTimeout(() => {
               // Add the custom class to the query container for further custom design on inner elements
               queryContainer.classList.add(layout);

               // Change the number of col of the grid
               queryContainer.style.setProperty('--grid-column-count', col);
            }, 100);
         })
      })
   })
})

The CSS

insert the following CSS on your page:

.loop-ctn-w-layout-toggles .query-container {
   /* Set the following variables as you need */
   --grid-layout-gap: 3rem;
   --grid-column-count: 4;
   --grid-item--min-width: 280px;
   /* End of variables - Don't modify the following values */

   display: grid;
   grid-gap: var(--grid-layout-gap);
}

@media screen and (min-width: 768px) {
   .loop-ctn-w-layout-toggles .query-container {
      --gap-count: calc(var(--grid-column-count) - 1);
      --total-gap-width: calc(var(--gap-count) * var(--grid-layout-gap));
      --grid-item--max-width: calc((100% - var(--total-gap-width)) / var(--grid-column-count));
      grid-template-columns: repeat(auto-fill, minmax(max(var(--grid-item--min-width), var(--grid-item--max-width)), 1fr));
   }

   /* Optional Animation */
   body.bricks-is-frontend .loop-ctn-w-layout-toggles .query-container .post-container {
      animation: none;
      opacity: 0;
   }

   body.bricks-is-frontend .loop-ctn-w-layout-toggles .query-container.list .post-container,
   body.bricks-is-frontend .loop-ctn-w-layout-toggles .query-container.list-two-col .post-container,
   body.bricks-is-frontend .loop-ctn-w-layout-toggles .query-container.grid .post-container {
      animation: 300ms slide-up ease-in-out forwards;
   }

   /* Optional inner elements style customisation based on the query container layout */
   .loop-ctn-w-layout-toggles .query-container.list .post-container,
   .loop-ctn-w-layout-toggles .query-container.list-two-col .post-container {
      flex-direction: row;
   }

   .loop-ctn-w-layout-toggles .query-container.list .post-container img {
      width: 30%;
      max-height: 100%;
   }

   .loop-ctn-w-layout-toggles .query-container.list-two-col .post-container img {
      width: 45%;
      max-height: 100%;
   }
}

@keyframes slide-up {
   0% {
      opacity: 0;
      transform: translateY(40px);
   }

   100% {
      opacity: 1;
      transform: translateY(0);
   }
}

Important notes:

  • The CSS contains the following code for optional animations – make sure to change it with your own layout names:
  • In this example we decided to change the flex-direction for the list and list-two-col layouts. To do that, we have to add some custom css lines. You first need to attach the layout class to the query container as shown in the red rectangles below, and then target the CSS selector you want to change – in our case both the post container and the featured image:

Final Result

If everything is correctly set, you should see the following result on frontend:

Get access to all 632 Bricks code tutorials with BricksLabs Pro

1 comment

  • Is this now possible by only using interactions on bricks?

Leave your comment

 

Related Tutorials..

Filtering Bricks Posts Query Loop by Meta Box Group’s Radio Subfield Value

Filtering Bricks Posts Query Loop by Meta Box Group’s Radio Subfield Value

In the past we showed how a Bricks posts query loop can be filtered by Meta Box group's subfield of types text/number and Datepicker here.…
Categories:
Pro
Adding active class to the current term/post in a Bricks query loop on Term archives/single posts

Adding active class to the current term/post in a Bricks query loop on Term archives/single posts

Updated on 29 Feb 2024 In the Sibling Terms Bricks Query Loop tutorial, a member asked: How can I set the class "active" to the…
Categories:
Pro
Nested Queries in Bricks – Posts Grouped by Published Years and Categories

Nested Queries in Bricks – Posts Grouped by Published Years and Categories

In the past we showed how posts could be grouped by years in Bricks. This Pro tutorial takes it further by grouping posts by categories…
Categories:
Pro
Accordion Grid Query Loop in Bricks

Accordion Grid Query Loop in Bricks

How to set up a accordion in Bricks such that the "titles" are arranged in a grid with the full width content opening below.
Categories:
Pro
Parent Query Loop Object – Traversing Nested Query Loops Up in Bricks

Parent Query Loop Object – Traversing Nested Query Loops Up in Bricks

Bricks enables us to build nested query levels of unlimited levels. Here are a couple of examples: Sometimes, you might want to obtain the parent…
Categories:
Pro
Related Episodes Grouped by Episode Chapters on Single Podcasts in Bricks when using JetEngine

Related Episodes Grouped by Episode Chapters on Single Podcasts in Bricks when using JetEngine

A user asks: How to Display Related Episodes Grouped by Chapters on a Podcast Page in Bricks? Hi everyone, I have two post types created…