Grid layout showcasing masonry gallery

I've spent some time looking for a Masonry gallery with a grid layout, but couldn't find one that suited my needs. So, I decided to create my own. I implemented a customElement with grid layout, but encountered an issue when trying to assign grid rows dynamically.
I would appreciate your feedback and assistance in improving my solution.

Some errors that I have identified are:

  • Need to run twice for it to work properly
  • Blank spaces appear when the image/container height is not a multiple of 100

HTML

<masonry-gallery></masonry-gallery>

JS

 class MasonryGallery extends HTMLElement {

    items = [
        { image:'https://unsplash.it/200/100/' },
        { image:'https://unsplash.it/200/200/' },
        { image:'https://unsplash.it/200/300/' },
        { ...}
    ]

    constructor() {
        super()
        this.attachShadow({ mode: 'open' })
        this.create()
        this.append()
    }

    create() {
        this.style.display = 'grid'
        this.style.gridTemplateColumns = 'repeat(auto-fill, 200px)'
        this.style.gridTemplateRows = 'repeat(auto-fill, 1fr)'
        this.style.gridGap = '1rem'
        this.style.gridAutoFlow = 'row dense'
    }

    append() {

        this.items.map(item => {

            const div = document.createElement('DIV');
            const image = document.createElement('IMG')

            image.src = item.image
            div.appendChild(image)

            this.shadowRoot.appendChild(div)

            div.style.gridRow = 'auto / span ' + [...String(image.height)][0]
        })

    }

}

customElements.define('masonry-gallery', MasonryGallery)

FIDDLE https://jsfiddle.net/znhybgb6/6/

Answer №1

Your code errors are as follows:

  1. You are attempting to determine the height of the image immediately after it is added to the component, but its height remains unknown until the image has finished loading.
  2. There are 1rem (16px) spaces between grid rows, causing each 100px tall image to increase the overall grid height by 116px.

To address this issue, you can modify your append method as shown below:

append() {

    let gap = parseInt(getComputedStyle(this).gridRowGap)

    this.items.map(item => {

        const div = document.createElement('DIV');
      const image = document.createElement('IMG')
      image.style.display = 'block';

      image.src = item.image
      div.appendChild(image)
      image.onload = function() {
          this.parentNode.style.gridRow = 'auto / span ' + ((this.height + gap)/(100 + gap));
      }

      this.shadowRoot.appendChild(div)

    })

  }

Additionally, consider updating gridRows with gridAutoRows = '100px' to maintain a consistent vertical rhythm and adjust image heights accordingly.

View the corrected result here in the revised Fiddle.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How to redefine TypeScript module export definitions

I recently installed a plugin that comes with type definitions. declare module 'autobind-decorator' { const autobind: ClassDecorator & MethodDecorator; export default autobind; } However, I realized that the type definition was incorrec ...

Customize RequireJS dependencies for flexible dependency injection

Currently, I am faced with integrating a component that would greatly benefit from Dependency Injection (DI) into an existing framework where DI was not considered during its initial design. The configuration defining dependencies is sourced from a backend ...

Incorporate React JS seamlessly into your current webpage

As I delve into learning React and considering migrating existing applications to React, my goal is to incorporate a React component within an established page that already contains its own HTML and JavaScript - similar to how KnockoutJS's `applyBindi ...

The Tailwind CSS Chrome extension is causing disruptions on the websites I view

Currently, I am in the process of creating a chrome extension using various tools like React, Typescript, TailwindCSS, and a custom Webpack configuration. To enhance user experience, I have modified the default action in manifest.json so that clicking on t ...

Using Google's Closure Compiler to Optimize JSON Files

When dealing with a json string that is parsed and properties of the object are accessed using dot notation, a warning may be triggered by the google closure compiler stating that the property is not defined. To overcome this issue, one workaround is to c ...

Assign a value to a hash object based on a specific location stored within an array

I'm struggling to figure out how to retrieve the original JSON data when its structure is variable. var jsonData = { "parent": { "child": "foo" } }; fu ...

Can I use more than one AudioContext at a time?

In my project, I am developing a React App using MeteorJS that utilizes the Web Audio API. This app consists of multiple pages, each of which utilizes web audio functionality. The issue I am facing is that I have hit the limit of 6 audio contexts allowed i ...

Encountering a vast expanse of empty white void

Attempting to create a scrollbar within my content div instead of the entire window seems to be causing a large white space in the content box, and I'm struggling to understand why. Can someone take a look and help me identify the issue? You can view ...

After deploying a Next.js project on an Ubuntu server, dynamic pages are returning a 404 error

I've been putting in a substantial amount of time on this project. I'm encountering some 404 errors on my dynamic pages, even though they work perfectly fine on localhost. I've tried using revalidate and playing around with fallback: true an ...

The onsubmit function encounters an issue with invocation when implemented with Node.js

When I submit the form, I want to perform some validations. However, it seems like the validation function is not working as expected. The alert part does not appear when triggered. Here is the code snippet: function validation(){ alert("somethi ...

The scroll function is executed only one time

I'm currently working on implementing a sticky sidebar snippet using jQuery. However, I am facing an issue where the function within .scroll() is only executing once instead of on every scroll event. Can anyone help me identify what I might be doing i ...

The Vuetify rating system seems to be having trouble displaying

I've integrated the Vuetify rating component into my app (https://vuetifyjs.com/en/components/ratings#ratings), but I'm facing an issue. Despite having Vuetify 1.5.5 installed and working with other components like buttons, the stars in the ratin ...

Label positioning for checkboxes

Is there a way to display the checkbox label before the actual checkbox without using the "for" attribute in the label tag? I need to maintain the specific combination of the checkbox and label elements and cannot use nested labels or place other HTML elem ...

Numerous Switches Similar to Tabs Using JavaScript and CSS

I'm looking to create tabs that operate as toggles, but with the twist that only one toggle can be active at any given time. Additionally, I need the tab content to appear above the actual tabs themselves. The challenge lies in the fact that I am usin ...

Tips on gathering information from an HTML for:

After encountering countless programming obstacles, I believe that the solution to my current issue is likely a simple fix related to syntax. However, despite numerous attempts, I have been unable to resolve it thus far. I recently created a contact form ...

execute a function for every regex match found within a string

When working with WordPress or PHP in general, I came across this interesting recommendation for email obfuscation, and I would like to: Convert email addresses to mailto links Encode the mailto links using str_13() Decode them client-side using JavaScri ...

The Fullpage.js embedded in an iframe is experiencing difficulty scrolling on iOS mobile devices

Trying to use Fullpage.js for a website with unique sections on different domains using full-screen iframes. However, encountering issues with scrolling on IOS mobile devices. The first solution rendered the page completely unscrollable: <iframe src=" ...

What is the best way to trigger an action in response to changes in state within Vuex

Are there more sophisticated methods to trigger actions in vuex when a state changes, rather than using a watcher on that state? I want to ensure the most efficient approach is being utilized. ...

AngularJS is used to vertically collapse three different div elements. This feature

I am facing an issue with the design of my page that contains three panels. Two of these panels have tables, and when viewing the page on a smaller display, it disrupts the layout. I am wondering if there is a way to add collapsible buttons to the panels s ...

Tips on accessing a browser cookie in a Next.js API endpoint

I've set a cookie in the layout.js component and it's visible in the browser. Now, I need to be able to retrieve that cookie value when a post request is made to my API and then perform some action based on that value. Despite trying different ...