Opens in a new tab
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.

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

1 comment

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

Leave your comment

 

Related Tutorials..

Pro
Bricks Query Loop – How to Insert Elements After Every Nth Post

Bricks Query Loop – How to Insert Elements After Every Nth Post

Update: Follow this tutorial instead. This Pro tutorial provides the steps to output element(s) in a Bricks' query loop after every nth post. Use case:…
Categories:
Pro
Upcoming Events Grouped by ‘Month Year’ in Bricks

Upcoming Events Grouped by ‘Month Year’ in Bricks

A custom query loop type for showing future events based on a date custom field using three nested query loops.
Pro
Events grouped by Event Date custom field in Bricks

Events grouped by Event Date custom field in Bricks

This Pro tutorial for Bricks users provides the steps to output posts of an event custom post type dynamically grouped by the value of their…
Categories:
Pro
Post ID Query Variable-based Post Output in Bricks

Post ID Query Variable-based Post Output in Bricks

How to query a single post of a custom post type using the post ID passed to a page via a URL parameter.
Pro
Posts Grouped by Taxonomy Terms in Bricks using BricksExtras’ Dynamic Table and Pro Tabs

Posts Grouped by Taxonomy Terms in Bricks using BricksExtras’ Dynamic Table and Pro Tabs

How to output any post type, with the taxonomy terms as Tab titles and posts inside tables with sortable columns.
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…
Pro
Events grouped by months within years based on event date custom field value

Events grouped by months within years based on event date custom field value

How to output events grouped by years and months using three nested query loops in Bricks.
Categories: