2

I'm working in a project implementing a masonry gallery. It works pretty well, but i need that the amount of columns is dynamically adjusted in the JS contruct call... I've tried different approaches with resize event, while loops, conditional if's, and even from the CSS using mediaQueries to hack the columns number, falling into nothing, recursive calls, and infinite loops, and i couldn't find a solution yet...

const masonryLayout = (containerElem, itemsElems, columns) => {

    // Crea Container para las columnas
    containerElem.classList.add('masonry-layout', `columns-${columns}`)

    // Crea n-columnas de acuerdo a "columns"
    let columnsElements = []
    for (let i = 1; i <= columns; i++) {
      let column = document.createElement('div')
      column.classList.add('masonry-column', `column-${i}`)
      containerElem.appendChild(column)
      columnsElements.push(column)
    }

    // Ubica cada imagen en la columna correspondiente
    for (let m = 0; m < Math.ceil(itemsElems.length / columns); m++) {
      for (let n = 0; n < columns; n++) {
        let item = itemsElems[m * columns + n]
        columnsElements[n].appendChild(item)
        item.classList.add('masonry-item')
      }
    }
}

masonryLayout(document.getElementById("gallery"), document.querySelectorAll(".gallery-item"),5)
.gallery-item img {
  max-width: 100%;
  display: block;
}
.masonry-layout {
  --columns: 5;
  --gap: 0.6rem;
  display: grid;
  grid-template-columns: repeat(var(--columns), 1fr);
  grid-gap: var(--gap);
}
.masonry-layout .masonry-item {
  margin-bottom: var(--gap);
}

.masonry-layout.columns-1 {
  --columns: 1;
}
.masonry-layout.columns-2 {
  --columns: 2;
}
.masonry-layout.columns-3 {
  --columns: 3;
}
.masonry-layout.columns-4 {
  --columns: 4;
}
.masonry-layout.columns-5 {
  --columns: 5;
}
<div class="gallery" id="gallery">
  <div class="columnNbrClass"></div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/325?image=100" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/450?image=200" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/280?image=300" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/540?image=400" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/380?image=500" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/300?image=600" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/400?image=700" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/300?image=800" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/280?image=900" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/480?image=925" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/550?image=950" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/600?image=1000" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/325?image=25" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/450?image=50" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/280?image=75" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/540?image=100" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/380?image=125" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/300?image=161" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/400?image=175" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/300?image=200" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/280?image=225" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/480?image=250" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/550?image=275" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/600?image=300" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/325?image=13" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/450?image=26" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/280?image=39" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/540?image=52" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/380?image=65" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/300?image=78" alt="" class="">
  </div>
</div>

Basically, i need that the column number in the masonryLayout constructor change dinamically on resize, but i couldn't find a way, i even tried to save the original html document but without any luck... if anyone has any ideas, it would be great to hear and learn.

Thank youvery much comm!

Rmaxx
  • 1,105
  • 11
  • 22
Rodo
  • 23
  • 3
  • 1
    not sure if i understand, what you want, but dows this solves your problem: https://jsfiddle.net/w1drbzeL/ (just rezise the output panel and the what happens) – Jarlik Stepsto May 22 '20 at 07:08
  • 1
    fixed ohne bug, here the updated fiddle: https://jsfiddle.net/w1drbzeL/1/ – Jarlik Stepsto May 22 '20 at 07:24
  • @JarlikStepsto, could you put your code in a code snippet as an answer? A comment can not be marked as an answer, so this question would remain open forever. – Titulum May 22 '20 at 07:25
  • answer added hope it is, what TO was asking for – Jarlik Stepsto May 22 '20 at 07:30
  • Hi @JarlikStepsto, thanks a lot for your quick answer!! indeed your solution works, but there are some things that i don't want to: *** I need the images to be ordered, i mean, if there will be 4 colums, first 4 images should be in first row, second 4 images in the second and so on... in your solutions they are get stacked one over each other by column. *** And when a youtube iframe (that i've added) ends in any of the internal columns it is not displayed (that doesn't happen with the JS version In any event i would preffer to use my version, and learn how to fix the issue i'm facing – Rodo May 22 '20 at 13:28
  • have a look at this, i will add it to my answer, is this what you wantet? – Jarlik Stepsto May 22 '20 at 13:58
  • forgot to add the link to new fiddle: https://jsfiddle.net/koLs7n4t/ – Jarlik Stepsto May 22 '20 at 14:01

1 Answers1

1

Not sure if this is what you want, but if you just want a resizeable masonry gallery, you could approach it just by using js. It would also works if you add the items dynamically by using js.

For testing the resize behavior just resize the output panel in this fiddle: JsFiddle Example

Explanation:

Using media-query with min-width will increase the number of columns with growing window size and the grid layout will do the rest. Adding content with js will force the browse to rerender so it should not be a problem.

here is a working example without js only with css:

.gallery-item img {
  max-width: 100%;
  display: block;
}
.masonry-layout {
  --columns: 1;
  --gap: 0.6rem;
  columns: var(--columns);
  grid-gap: var(--gap);
}
.masonry-layout .masonry-item {
  margin-bottom: var(--gap);
  display: inline-block;
}

@media only screen and (min-width: 300px) {
  .masonry-layout {
    --columns: 2;
  }
}

@media only screen and (min-width: 500px) {
  .masonry-layout {
    --columns: 3;
  }
}

@media only screen and (min-width: 800px) {
  .masonry-layout {
    --columns: 4;
  }
}

@media only screen and (min-width: 1000px) {
  .masonry-layout {
    --columns: 5;
  }
}
<div class="gallery masonry-layout" id="gallery">
  <div class="gallery-item">
    <img src="https://picsum.photos/450/325?image=100" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/450?image=200" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/280?image=300" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/540?image=400" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/380?image=500" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/300?image=600" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/400?image=700" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/300?image=800" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/280?image=900" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/480?image=925" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/550?image=950" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/600?image=1000" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/325?image=25" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/450?image=50" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/280?image=75" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/540?image=100" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/380?image=125" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/300?image=161" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/400?image=175" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/300?image=200" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/280?image=225" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/480?image=250" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/550?image=275" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/600?image=300" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/325?image=13" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/450?image=26" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/280?image=39" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/540?image=52" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/380?image=65" alt="" class="">
  </div>
  <div class="gallery-item">
    <img src="https://picsum.photos/450/300?image=78" alt="" class="">
  </div>
</div>

[EDIT]

Also have a look at this fiddle I used your code and modified it to clear the container before filling it again and added a onresize, it seems to work. One of your errors was to not clear allready settted classes, so in column.classList.add('masonry-column',column-${i}); you added two classes, but the one from last add are still there, so when you call the methode 2 times with 1 and 2 as the column parameters, you will have following class declaration:

1:

class="masonry-column column-1"

2:

class="masonry-column column-1 column-2"

now, when you downsize the window, column-2 still is there and you will have two columns displayed but only one filled. To solve this exception I added containerElem.className = '' to clear the class before setting it.

Your second problem was: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'. In your case, you have 30 items and you try to add same amount of items to each columnt, that works for 1, 2 and 3 columns, becouase 30 can be divided by each of that numbers, but for 4 it is not possible and there are not enough items to splitt them even between all columns. To prevent the error, you can just check, if there is an item left:

if(m*columns + n >= itemsElems.length){
  continue;
}

Here is an updated fiddle with both errors fixed.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Jarlik Stepsto
  • 1,667
  • 13
  • 18
  • Hey, it would be something alike, but i've already try it and it is not working... if you look closely, you will see that when you resize and get back the layout breaks like it is in some bad recursive call... and i assume it is that way because it tries to construct from the already constructed html, which is not correct... i've tried duplicating the document as well, but i didn't went too far... i'm still trying – Rodo May 22 '20 at 19:33
  • @Rodo what do you mean, by its broken, for me resizing in the fiddle works perfectly, it does not break and creates as much columngs as i calculate on resize. which browser are you using and can you show an image of what happens? – Jarlik Stepsto May 22 '20 at 20:01
  • What i mean is that everything's ok when the resize increases the width, but it doesn't work the same when it reduces the width... [Here](https://drive.google.com/file/d/1fuNIgwvXVWKpVo3FdUyWDBurmHr2x59C/view?usp=sharing) you can check the fiddle working on my up-to-date chrome. Let me know if this only happens to me... – Rodo May 22 '20 at 21:04
  • 1
    @Rodo ah, sorry, forgot to fix this, see my updated fiddle https://jsfiddle.net/gecmvw6y/ (link in post updated). The problem was, that you dont remove the ald classes so the column variable in css was not set properly on downsize – Jarlik Stepsto May 22 '20 at 21:17
  • Heyy! thats awesome... i didn't understand the rootcause though... Now (and i think pretty much always) i can see an error occurring when resizing after 934px and up... 935, 936 and so on..."Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'." in line 22... that is causing some images not loading correctly under others... do you know why?? – Rodo May 22 '20 at 21:31
  • that error is because you try to splitt the images so that you have equal amaount in each column, that is not allwasy possible. Add a check in the inner loop if there is an element left for that position `if(m*columns + n >= itemsElems.length){ continue; }` – Jarlik Stepsto May 22 '20 at 22:22
  • 1
    @Rodo have a look at my updated answer, i explained there, what coused your errors and added a link to fixed fiddle. Please mark the answer as accepted :) – Jarlik Stepsto May 22 '20 at 22:32
  • man, this is more of what i've asked for! thanks very much for your explanation and commitment to solve my issues! Now is working flawlessly and with no errors. I'm doing a portfolio for an artist so i had to have this working with no fail... i've learned more than a few things here...thanks again! – Rodo May 22 '20 at 23:08