This tutorial series will explore the IsotopeJS library‘s features in Bricks.
Table of Contents
- Requirements
- Enqueue the files
- The JavaScript
- The CSS
- The Bricks Structure
- Control the column number
- Conclusion
Requirements
- Bricks Theme (any version)
- Bricks Child theme activated
Enqueue the files
IsotopeJS is part of the core library that Bricks natively uses and doesn’t require to be uploaded to your server, we’ll just enqueue it the same way Bricks does.
Though we’ll also need two extra files added to our servers: one .css and one init.js file to set all the options to be triggered by our Bricks page.
Here is the code to be pasted in the functions.php file of your child theme:
/**
* Register/enqueue custom scripts and styles
*/
add_action( 'wp_enqueue_scripts', function() {
// Enqueue your files on the canvas & frontend, not the builder panel. Otherwise custom CSS might affect builder)
if ( ! bricks_is_builder_main() ) {
// Isotope JS (library scripts)
if ( get_field( 'activate_isotopejs' ) ) {
wp_enqueue_script( 'bricks-isotope' );
// Isotope JS (init scripts)
if ( get_field( 'activate_isotope_init' ) ) {
wp_enqueue_script( 'isotope_init', get_stylesheet_directory_uri() . '/js/isotope_init.js', array( 'bricks-isotope' ), filemtime( get_stylesheet_directory() . '/js/isotope_init.js' ) );
wp_enqueue_style( 'isotope', get_stylesheet_directory_uri() . '/css/isotope/isotope.css', array(), filemtime( get_stylesheet_directory() . '/css/isotope/isotope.css' ) );
};
};
};
});
Note that I like to use the conditional enqueue method to load my scripts as described in this previous tutorial. In this case, I created 2 different ACF fields: one for the library itself (activate_isotopejs) and one for the init files (activate_isotope_init). The reason why is there are scenarios where you just want to load the library and not the init.js, just like we did in our previous leaflet.js series.
The JavaScript
I’ve created for you a full init file with error handlers, lots of comments, and some really handy data-attribute helpers so you don’t need to code anything in Bricks to make it work. Just copy/paste the following script inside your init.js file we enqueued earlier:
window.addEventListener('DOMContentLoaded', () => {
const isotopeWrappers = document.querySelectorAll('.isotope-wrapper');
// Stop the function if there is no isotope wrapper detected
if (isotopeWrappers.length < 1) {
return console.log('No isotope wrapper found. Make sure to add the ".isotope-wrapper" class to the main isotope wrapper');
};
var filterSelector = "*";
isotopeWrappers.forEach(wrapper => {
// Set variable and Error Handling
var isotopeContainer = wrapper.querySelector('.isotope-container');
if (!isotopeContainer) {
return console.log('No isotope container found. Make sure to add the ".isotope-container" class to the container of your selectors');
}
var isotopeSelector = isotopeContainer.querySelectorAll('.isotope-container .isotope-selector');
if (isotopeSelector.length < 0) {
return console.log('No isotope selector found. Make sure to add the ".isotope-selector" class to all your selector');
}
var filtersElem = wrapper.querySelectorAll(".filterbtn-container .filterbtn");
if (filtersElem.length < 0) {
return console.log('No filter wrapper or filter buttons found. Make sure your filter wrapper has the class ".filterbtn-wrapper" and all your filter buttons have the class ".filterbtn"');
}
// Gutter Settings through data-gutter
if (wrapper.dataset.gutter) {
var isotopeGutter = parseInt(wrapper.dataset.gutter);
wrapper.style.setProperty('--gutter', isotopeGutter + 'px');
isotopeSelector.forEach(elm => elm.style.paddingBottom = isotopeGutter + 'px');
} else {
// Default option
var isotopeGutter = 0;
console.log('No data-gutter attribute has been found on your isotope container. Default set to 0.');
};
// Layout Settings through data-filter-layout
if (wrapper.dataset.filterLayout) {
var isotopeLayoutHelper = wrapper.dataset.filterLayout;
} else {
// Default option
var isotopeLayoutHelper = 'fitRows';
console.log('No data-filter-layout attribute has been found on your isotope container. Default set to "fitRows".');
};
// init Isotope
var isotopeOptions = {
itemSelector: '.isotope-selector',
layoutMode: isotopeLayoutHelper,
};
// Set the correct layout
switch (isotopeLayoutHelper) {
case 'fitRows':
isotopeOptions.fitRows = {
gutter: isotopeGutter
};
break;
case 'masonry':
isotopeOptions.masonry = {
gutter: isotopeGutter
};
break;
}
var iso = new Isotope(isotopeContainer, isotopeOptions);
// bind filter button click
if (filtersElem.length > 0) {
filtersElem.forEach(elem => elem.addEventListener("click", function (event) {
event.preventDefault();
// get the data-filter attribute from the filter button
var filterValue = event.target.getAttribute("data-filter");
// use matching filter function
filterValue == '*' ? filterValue : filterValue = '[data-filter*="' + filterValue + '"]';
// apply the filter
iso.arrange({
filter: filterValue
});
}));
};
// change is-checked class on buttons
for (var i = 0, len = filtersElem.length; i < len; i++) {
var buttonGroup = filtersElem[i];
radioButtonGroup(buttonGroup);
};
function radioButtonGroup(buttonGroup) {
buttonGroup.addEventListener("click", function (event) {
filtersElem.forEach(btn => btn.classList.remove("filterbtn--active"));
event.target.classList.add("filterbtn--active");
});
};
setTimeout(() => iso.arrange({filter: '*'}), 300)
});
});
The CSS
It’s a really tiny one, but believe me, it will make your life easier. Copy/paste the following code inside the .css file you enqueued earlier:
.isotope-selector {
width: calc(calc(100% / var(--col)) - calc(calc(var(--gutter)* calc(var(--col) - 1))/var(--col)));
}
.isotope-wrapper {
--col: 1;
--gutter: 0px;
}
@media screen and (min-width: 768px){
.isotope-wrapper {
--col:2;
}
}
@media screen and (min-width: 992px){
.isotope-wrapper {
--col:3;
}
}
Now we’re all set with our scripts, let’s jump back inside the Bricks Builder!
The Bricks Structure
This part is crucial to respect – don’t skip anything here otherwise the function won’t work correctly.
The DOM Tree
We’ll basically need 3 containers:
- a container for the filter buttons. Inside this container, we’ll set all the different filter conditions for each filter button.
- another one that will contain the items that we intend to filter.
- the last one will wrap everything, trigger our script, and take care of the layout style and gutter options.
Here is what it looks like in Bricks:

Let’s create a really simple example to illustrate each step. Inside each .isotope-selector, I added different color squares. Our goal will be to filter them by colors:

But don’t worry, we’ll explore all the elements one by one. The important thing is to respect the markup and the hierarchy.
.isotope-wrapper
This is our big fat container that includes everything. First of all, let’s add the isotope-wrapper class to it (otherwise, our script won’t be triggered).

No style is necessary here, but here is the place where we want to add our useful data-attributes to manage the layout style and the gutter width.
data-filter-layout
Go to the attribute tab and choose between the fitRows layout or the masonry layout:

fitRows
fitRows is the default isotope style and work great with items with equal height (like our colored squares). All the rows will be horizontally aligned correctly, tho if our items have different heights, it could create an undesired result such as this:

If this is your case, you probably want to consider the masonry layout described below.
masonry
As you may already know, the masonry layout sorts the items by columns (instead of rows) and is a perfect fit for items that have different heights. Here is the result of our previous example with the masonry layout applied:

looks great, isn’t it?
data-gutter
The gutter is the white space between each item. By default, it’s set to 0. But with our data-gutter attribute, it’s a baby-proof task to adjust it as you need:

This is the result if we add a value of 40 in there:

The value is intended as pixels since this is the metric used by Isotopejs to calculate the position of each item dynamically.
.filterbtn-container
As the name suggests, this is the container of all the filter buttons. Let’s add the .filterbtn-container class to it:

In our example, we set it horizontally using the flexbox options and added some gaps, but feel free to style it as you want.
.filterbtn and .filterbtn–active
Let’s jump now on the buttons. Each one of them needs the .filterbtn class added:

And also a data-filter attribute with the filter condition (in our example, the color):

Usually you’ll also want an ALL button that will reset all the filters and show all the items. In our case, we want to show all the items by default and set the ALL button active, so let’s add another class to the ALL button called .filterbtn--active and set the data-filter to *:


And that’s it for the filter buttons.
.isotope-container
So this is the container that will contain all the .isotope-selectors (the items we want to filter). Let’s add the isotope-container class:

Since at the time of writing the article, the bricks builder does not output the data-attributes markup inside the builder view, the script isn’t running correctly (only in the backend, the frontend should still work as expected)- thus it creates quite a messy vertical layout:

A quick hack is to change the flex-direction of our container to row and set the flex-wrap to wrap:

It has no impact on the frontend, but it just temporally fixes the backend layout:

.isotope-selector
The last one is our selectors. They all stand inside the .isotope-container. Add the isotope-selector class to all of them:

And give them the data-filter attribute (read “category”) that you want to assign:

And that’s it! Save your work and check the front end:
Control the column number
By default, our isotope container will have 3 columns on desktop, 2 columns on tablet, and 1 on mobile, but you can easily override this setting and add your own breakpoints by adding a few lines of CSS inside the .isotope-wrapper. The number of columns is managed by a CSS variable called --col and can be modified inside standard media queries. For example, let’s add a 4th column on devices larger than 1200px, we will add the following code:
@media screen and (min-width: 1200px) {
root{
--col:4;
}
}
and this is the result on screens larger than 1200px:

Conclusion
Despite the length of this article, we just scratched the surface of the possibilities that IsotopeJS offers to us. In the next tutorials, we’ll create some more complex filters such as blog post filters, dynamic data, image galleries, etc. But before jumping onto more complex shiny stuff, make sure to understand the basics and memorize the structure. Happy filtering!