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 525 Bricks code tutorials with BricksLabs Pro

1 comment

Leave your comment

 

Related Tutorials..

Pro
Related Services grouped by Service Categories on Single Branch Posts in Bricks

Related Services grouped by Service Categories on Single Branch Posts in Bricks

How to query ACF relationship fields on single CPT templates.
Categories:
Pro
ACF Post Object Query Loop inside a Posts Query Loop in Bricks

ACF Post Object Query Loop inside a Posts Query Loop in Bricks

How to loop through a CPT and within each post, output a list of related posts.
Categories:
Pro
Post-specific ACF Repeater Galleries as Sliders in Bricks

Post-specific ACF Repeater Galleries as Sliders in Bricks

How to output ACF Repeater field rows with the images of the Gallery-type sub field as a slider.
Pro
Query Loop in Bricks

Query Loop in Bricks

This Pro tutorial walks you through the steps to use the Query Loop feature in Bricks to display a responsive grid of posts using CSS…
Categories:
How to populate a map with dynamic markers from a CPT using ACF in Bricks (PART 1)

How to populate a map with dynamic markers from a CPT using ACF in Bricks (PART 1)

This tutorial provides the PHP & JS codes that can be pasted in order to create a dynamic map with markers populated from a custom…
Categories:
Pro
Posts older than today in Bricks

Posts older than today in Bricks

How to display only posts that were published before today in a query loop.
Categories:
Pro
Bricks Query Loop for Top-level Parent Pages with Children

Bricks Query Loop for Top-level Parent Pages with Children

In the Bricks Facebook group, a user asked: Looking for a custom query to output a list of posts in the same hierarchy that the…
Categories:
Pro
ACPT Gallery Field Query Loop in Bricks

ACPT Gallery Field Query Loop in Bricks

How we can output images from ACPT‘s Gallery field for posts as a grid using a query loop.
Categories: