Create a masonry layout that works today | CSS tricks

Create a masonry layout that works today | CSS tricks

7 minutes, 28 seconds Read

Many CSS experts weighed heavily last year on possible syntax for a new brickwork layout function. There were two main camps and one Third camp That is a balance between the two:

  1. Usage display: masonry
  2. Usage grid-template-rows: masonry
  3. Usage item-pack: collapse

I don’t think they came up with a resolution yet. But maybe you want to know that Firefox already supports the layout of the brickwork with the second syntax. And Chrome test it With the first syntax. Although it is cool to see native support for CSS brickwork evolving, we cannot really use it in production if other browsers do not support the same implementation …

So, instead of adding my voice to one of those camps, I went out What time the brickwork today work with other browsers. I am happy to report that I have found a way – and bonus! – that support can be offered With only 66 lines JavaScript.

In this article I will show you how it works. But first, here is a demo for you to play with, just to prove that I am not nonsense. Note that there will be a small delay, because we are waiting for an image to be loaded first. If you place a brickwork at the top of the upper fold, consider skipping it, including images!

Anyway, here is the demo:

What is this in the magic?!

Now there are a lot of things I have recorded this demo, even though there are only 66 lines of JavaScript:

  • You can define the brickwork with a random number of columns.
  • Each item can overstrain multiple columns.
  • We wait for the media to be loaded before the size of each item is calculated.
  • We made it responsive by listening to changes with the ResizeObserver.

These make my implementation incredibly robust and ready for production use, while they are also much more flexible than a lot of flexbox masonry knocks -offs on the interwebs.

Now, a hot tip. If you combine this with the responsive variants of the tail wind and random values, you can record Even more flexibility In this masonry schedule to write CSS.

Okay, before you are hyped, let’s come back to the main question: how the hell does this work?

Let’s start with a polyfill

Firefox is already supporting brickwork workstuffs via the syntax of the second camp. Here are the CSS that you need to create a CSS -Lay -Out Masonry schedule in Firefox.

.masonry {
  display: grid;
  grid-template-columns: repeat(
    auto-fit,
    minmax(min(var(--item-width, 200px), 100%), 1fr)
  );
  grid-template-rows: masonry;
  grid-auto-flow: dense; /* Optional, but recommended */
}

Because Firefox already has native brickwork support, we should of course not mess around with it. The best way to check whether the brickwork is supported by default is to check whether grid-template-rows Can the masonry value.

function isMasonrySupported(container) {
  return getComputedStyle(container).gridTemplateRows === 'masonry'
}

If the brickwork is supported, we will pass our implementation. Otherwise we do something about it.

const containers = document.querySelectorAll('.masonry')

containers.forEach(async container => {
  if (isMasonrySupported(container)) return
})

Masonry Lay -Out made easy

Now I want to precede this segment that I am not the one who invented this technique.

I discovered this technique when I was digging through the web, looking for possible ways to implement a brickwork schedule today. So Kudos goes to the unknown developer who first developed the idea – and perhaps I for understanding, converting and using it.

The technology is as follows:

  1. We have set grid-auto-rows Unpleasant 0px.
  2. Then we bet row-gap Unpleasant 1px.
  3. Then we get the length of the item through getBoundingClientRect.
  4. We were then attracted to the “driving allocation” of the item by the height the column-gap value together.

This is Real Not -Intuitive if you have used CSS -Grid in the standard way. But once you have this, you can also understand how this works!

Because this is not so intuitive, we will put things step by step, so that you see how this whole thing evolves to the final output.

Step by step

First, we set up grid-auto-rows Unpleasant 0px. This is strange because every grid item will effectively have “zero height”. Nevertheless, CSS Grid at the same time maintains the order of the columns and rows!

containers.forEach(async container => {
  // ...
  container.style.gridAutoRows="0px"
})

Second, we have set up row-gap Unpleasant 1px. As soon as we do this, you start to notice a first pile of the rows, each pixel under the previous one.

containers.forEach(async container => {
  // ...
  container.style.gridAutoRows="0px"
  container.style.setProperty('row-gap', '1px', 'important')
})
Tickets are still stacked and in three columns, but now they are more stacked immediately.

Thirdly, assuming that there are no images or other media elements in the schedules, we can easily get the height of each schedule item with getBoundingClientRect.

We can then restore the “height” of the schedule in CSS -Raster by replacing grow-row-end with the height value. This works because everyone row-gap is now 1px long.

If we do this, you will start to get the schedule. Each item is now (rather) back in their respective positions:

containers.forEach(async container => {
  // ...
  let items = container.children
  layout({ items })
})

function layout({ items }) {
  items.forEach(item => {
    const ib = item.getBoundingClientRect()
    item.style.gridRowEnd = `span ${Math.round(ib.height)}`
  })
}
A brickwork layout of cards that alternately alternates one and two columns.

We now have to restore the driving hole between items. Fortunately, because brickwork grilles usually have the same column-gap And row-gap Values, we can take the desired driving opening by reading column-gap Values.

As soon as we do that, we add it grid-row-end To expand the number of rows (the “height”) that is included in the grid by the item:

containers.forEach(async container => {
  // ...
  const items = container.children
  const colGap = parseFloat(getComputedStyle(container).columnGap)
  layout({ items, colGap })
})

function layout({ items, colGap }) {
  items.forEach(item => {
    const ib = item.getBoundingClientRect()
    item.style.gridRowEnd = `span ${Math.round(ib.height + colGap)}`
  })
}
A brickwork with three columns. Most items occupy one column, but two elements extend to take two columns. The order flows from left to right.

And so we made the brickwork schedule! Everything from here is just to prepare this for production.

Waiting for media to load

Try to add an image to each grid item and you will notice that the grid is breaking. That is because the height of the item will be “wrong”.

The first item in the masonry layout contains an image with text and is behind other items in the first two columns.

It is wrong because we are the height value before the image was correctly loaded. The Dom does not yet know the dimensions of the image. To solve this, we must wait for the media to be loaded before it layout function.

We can do this with the following code (which I will not explain, because this is not really a CSS trick 😅):

containers.forEach(async container => {
  // ...
  try {
    await Promise.all([areImagesLoaded(container), areVideosLoaded(container)])
  } catch(e) {}

  // Run the layout function after images are loaded
  layout({ items, colGap })
})

// Checks if images are loaded
async function areImagesLoaded(container) {
  const images = Array.from(container.querySelectorAll('img'))
  const promises = images.map(img => {
    return new Promise((resolve, reject) => {
      if (img.complete) return resolve()
      img.onload = resolve
      img.onerror = reject
    })
  })
  return Promise.all(promises)
}

// Checks if videos are loaded
function areVideosLoaded(container) {
  const videos = Array.from(container.querySelectorAll('video'))
  const promises = videos.map(video => {
    return new Promise((resolve, reject) => {
      if (video.readyState === 4) return resolve()
      video.onloadedmetadata = resolve
      video.onerror = reject
    })
  })
  return Promise.all(promises)
}

VoilàWe have a CSS Masnory grid that works with images and videos!

A complete masonry layout with six items. The first and third items occupy the first two columns and items 2, 4, 5 and 6 streams to the third column.

Responsive

This is a simple step. We only need the Resizeobserver API To listen to every change in dimensions of the brickwork schedule.

If there is a change, we walk the layout Function again:

containers.forEach(async container => {
// ...
const observer = new ResizeObserver(observerFn)
observer.observe(container)

function observerFn(entries) {
  for (const entry of entries) {
    layout({colGap, items})
  }
}
})

This demo uses the standard writers of observer API. But you can make it easier by using the refined resizeObserver Function that we recently built.

containers.forEach(async container => {
  // ...
  const observer = resizeObserver(container, {
    callback () {
      layout({colGap, items})
    }
  })
})

That’s it actually! You now have a robust masonry schedule that you can use in every working browser that supports CSS Grid!

Exciting, right? This implementation is so easy to use!

Masonry schedule with a beautiful Labz

If you are not disadvantageous for the use of code built by others, you may want to consider Grabbing the one I built for you In beautiful Labz.

To do that, install the helper library and add the required code:

# Installing the library
npm install @splendidlabz/styles
/* Import all layouts code */
@import '@splendidlabz/layouts';
// Use the masonry script
import { masonry } from '@splendidlabz/styles/scripts'
masonry()

One last thing: I have built a lot of tools to help Make web development much easier for you and me. I have them all under the Splendid Labz Brand – and one of these examples is this brickwork schedule that I showed you today.

If you like this, you might be interested in others Lay -out tools That makes the layout super easy to build.

Now I hope you enjoyed this article today. Go your new CSS brickwork grid if you want, and the very best!

#Create #masonry #layout #works #today #CSS #tricks

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *