Issue with Masonry.js implementation causing layout to not display correctly

Currently, I am working on a project using Laravel, VueJS, and the Masonry.js library to develop a dynamic gallery. However, I have encountered a peculiar issue.

Here is a snippet of my VueJS template:

<template lang="html">
  <div id="uploads-grid">
    <div class="grid" ref="grid">

      <!-- GRID START -->

      <div class="grid-item white-outline" v-for="(bg, index) in bgs" :style="'width:' + wd + 'px;height:' + hg[index] + 'px;'">
        <img :src="'/storage/users/1/photos/1/' + bg.photo" :width="wd" :height="hg[index]">
      </div>

      <!-- GRID END -->

    </div>
  </div>
</template>

Here is how I retrieve the images:

<script>

import Masonry from 'masonry-layout';

export default {
  data() {
    return {
      bgs: [],
      wd: '300',
      hg: []
    }
  },
  methods: {
    getPhotos() {
      var url = '/photos/get'
      axios.get(url).then(r => {
        this.bgs = r.data
        for (var i = 0; i < r.data.length; i++) {
          var factor = r.data[i].width / 300
          var resizeHeight = Math.floor(r.data[i].height / factor)
          this.hg.push(resizeHeight)
        }
      });
    }
  },
  mounted() {
    let $masonry = new Masonry(this.$refs.grid, {
      itemSelector: '.grid-item',
      columnWidth: 300,
      isFitWidth: true
    });
  },
  created() {
    this.getPhotos()
  }
}
</script>

The issue at hand is that each image appears below the previous one.

In contrast, the following code snippet functions correctly:

<template lang="html">
  <div id="uploads-grid">
    <div class="grid" ref="grid">

      <!-- GRID START -->

      <div class="grid-item white-outline" v-for="(bg, index) in bgs" :style="'width:' + wd[index] + 'px;height:' + hg[index] + 'px;'">
        <img :src="bg">
      </div>

      <!-- GRID END -->

    </div>
  </div>
</template>

<script>

import Masonry from 'masonry-layout';

export default {
  data() {
    return {
      bgs: [],
      wd: [],
      hg: []
    }
  },
  methods: {
    rndBg() {
      for (var i = 0; i < 20; i++) {
        var wd = 300
        var hg = Math.floor(Math.random() * 350) + 150
        var bgsrc = 'https://placehold.it/' + wd + 'x' + hg
        this.bgs.push(bgsrc)
        this.wd.push(wd)
        this.hg.push(hg)
      }
    }
  },
  mounted() {
    let $masonry = new Masonry(this.$refs.grid, {
      itemSelector: '.grid-item',
      columnWidth: 300,
      isFitWidth: true
    });
  },
  created() {
    this.rndBg()
  }
}
</script>

However, as you can see, I am using placeholder dummy images instead of the ones I intended to use. Therefore, it does not suit my requirements. Despite using the same logic, it seems that I cannot resolve this issue.

Answer №1

Main point: Remember to retrigger Masonry after inserting a new image.

Masonry functions by fixing the position, width, and other attributes of elements. Therefore, every time an element is added or removed, you should retrigger Masonry to recalculate and reset these attributes again.

For example, check out this example in components/Hello.vue

<template>
    <div class="hello">
        <button @click="addImg"> add img </button>
        <br/><br/>
        <div class="grid">
            <div class="grid-item"  v-for="img in imgs">
                <img :src="img">
            </div>
        </div>
    </div>
</template>

<script>
    import Masonry from 'masonry-layout';

    export default {
  name: 'hello',
  data () {
    return {
            imgs:[
                `https://unsplash.it/200/300/?random=${Math.random()}`,
                `https://unsplash.it/200/100/?random=${Math.random()}`,
                `https://unsplash.it/200/400/?random=${Math.random()}`,
                `https://unsplash.it/200/250/?random=${Math.random()}`
            ]
    }
  },
    mounted() {
        this.triggerMasonry();
    },
    methods:{
        triggerMasonry() {
            console.log('triggerMasonry');
            var grid = document.querySelector('.grid');
            var msnry = new Masonry( grid, {
                itemSelector: '.grid-item',
                columnWidth: 200
            });
        },
        addImg(){
            this.imgs.push(`https://unsplash.it/200/300/?random=${Math.random()}`);
            this.$nextTick(()=>{
                this.triggerMasonry();
            });
        }
    }
}

</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.grid {
    width: 100%;
    img {
        width: 100%;
    }
}
</style>

Make sure to call triggerMasonry on mounted, as well as after adding a new image.

Additionally, use nextTick to ensure that triggerMasonry is applied only after the new image has been inserted into the DOM.

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

Can Jquery mobile detect drag gestures?

Is there an event in Jquery Mobile that allows you to hide/show a div by dragging it, similar to the effect seen on phones? I have searched on different platforms and found that using Jquery UI is the closest solution. Is it possible to achieve this only ...

Guide on successfully sending an object to axios.post for request handling in a Vue Component

I am attempting to send an object using axios.post, but it seems like I might be doing it incorrectly as I am encountering the following error: Error: "Request failed with status code 500" Interestingly, I do not face any issues when I send the data as a ...

Creating a unique seal, badge, or widget in Django is a fun and rewarding project

Here are some examples of what I want to accomplish: View the gif showcasing the function Visit https://sectigo.com/trust-seal Check out another gif demonstrating the function Explore https://www.carandtruckremotes.com/ Don't miss this gif showin ...

Include the script tags retrieved from an array

I'm exploring the idea of dynamically adding JavaScript to the DOM using an array and a loop. The goal is to load each script sequentially, starting with scripts[0], then scripts[1], and so on. var myScripts = [["external", "https://code.jquery.com/j ...

The style attribute is triggering an error stating that 'Every child in a list must possess a distinct "key" property.'

Can anyone explain why I'm encountering an error when storing JSX code in a variable like this? const centerStyle = {textAlign: 'center'}; viewState.myContent = ( <Fragment> <p style={centerStyle}>Some text</p> < ...

What are some methods for utilizing jQuery or Javascript to dynamically modify CSS styles depending on the current URL?

I'm working on integrating a separate navigation area file with my content using PHP includes. I have this idea where I want the links in the navigation area to change color depending on the active page (current URL). Essentially, I need jQuery or Jav ...

How to access event.target in Internet Explorer 8 with unobtrusive Javascript

Here is a function that retrieves the target element from a dropdown menu: function getTarget(evt){ var targetElement = null; //if it is a standard browser if (typeof evt.target != 'undefined'){ targetElement = evt.target; } //otherwise ...

What is the best way to showcase a JSON object in an attractive format on a webpage?

Similar Question: JavaScript data formatting/pretty printer var theobject_string = '{...}'; // I have a JSON object as a string. Is there a way to present this string in a visually appealing manner on an HTML webpage? I would like the ...

Angular Universal causing issues with updating the DOM component

@Component({ selector: 'mh-feature-popup', template: ` <div class="full"> <div> <div class="container-fluid" [@featurepop]="state"> <div class="row"> <div class="col-xs-12 col-md-4 col-md-offse ...

The attempt to test the AngularJS application using the Jasmine plugin was unsuccessful

I'm currently in the process of learning how to write test cases for my angularJS application. As a beginner, I'm searching for some sample examples of working demos. I came across an example online that uses the Jasmine plugin to test an angular ...

What is the best way to reveal and automatically scroll to a hidden div located at the bottom of a webpage?

Whenever the image is clicked, I want to display a hidden div with effects like automatically scrolling down to the bottom This is what my code looks like: $(document).ready(function() { $('#test').click(function() { $(&apos ...

What are the best practices for utilizing ESM only npm packages alongside traditional npm packages within a single JavaScript file?

Hey there, I'm fairly new to web development and I encountered a problem when trying to require two packages, franc and langs, in my index.js file. It turns out that franc is now an ESM only package, requiring me to import it and mention type:module i ...

Utilize AJAX to accurately capture and handle error codes

Here is a snippet of code that I want to send to my server: $.ajax({ type: 'POST', url: 'post.php', data: { token: '123456', title: 'some title', url: 'http://somedomain.com', data: & ...

Craft a hierarchical JSON structure out of a different nested JSON object [PAUSE]

Looking to transform an existing JSON object into a new object structure. Here is the current JSON object: { "name": "Parent", "children": [ { "name": "Child1", "children": [ { "name": "GrandC ...

Explore a JSON array within another JSON array

Here is a JSON format that I have: { "Project [id=1, dateDebut=2017-01-13, dateFin=2017-01-18, description=qsd, sponsor=qsd ]" : [ {"id":1,"title":"qsd ","description":"qsdqsd","dateFin":"2017-01-26"}, {"id":2,"title":"sss ","descriptio ...

The variable that contains a function is invoked, but the result is undefined

I'm having trouble with a function that should return John temp (2021) when called, but instead it's returning John undefined (undefined) const user = { username : 'John', type: 'temp', yearJoin: 2021, user ...

Save user information to initialize rootScope upon refreshing the page

I'm looking for a way to retain a User Json Object in such a manner that I can use it to initialize my $rootScope.user Json Object with the saved information even after refreshing the page. The storage should persist as long as the browser window rem ...

Tips for implementing daterangepicker js in an Angular 2 project

I'm currently working on an Angular 2 project and I'm looking to integrate the daterangepicker.js library for a date range picker. If you're not familiar with it, you can find more information about the library here. Here's the HTML co ...

Creating specialized paths for API - URL handlers to manage nested resources

When working with two resources, employees and employee groups, I aim to create a structured URL format as follows: GET /employees List employees. GET /employees/123 Get employee 123. GET /employees/groups List employee groups. GET /employees/groups/123 ...

Is there a way I can utilize canvas to overlay one image onto another?

Is there a way to merge one image into another by using a transparent box? It's not just about copying and pasting an image into another. I also want to know how to transform the image to fit perfectly within the other image. For example, similar to ...