How to add a layout toggle on query loops in Bricks

Schermata 2022-10-11 alle 12.25.52

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:

Leave the first comment