Search code examples
htmlcssrebuildonresizepure-js

How to rebuild DOM after a resize event if it is being constructed from JS


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!


Solution

  • 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.