Gallery with bigger previews "in place"

Issue

My aim is to do a thumbnail gallery which will enlarge thumbnail in a new row after the thumbnail has been clicked [Edited for clarity]:

enter image description here

I am not sure how to do it properly (if there is a proper way). How would you conceptually approach this task?

Flexbox seems to be nice for the first part, but then add cell on calculated position by JS OR split the flexbox into two? And in every case redo everything completely in there was a resize?

A help would be also to know how this kind of gallery is called, because I was unable to find any info on Google. Thank you!

Solution

This is an example showing how a container could use display:flex; to show a number of div.item as pictures in a gallery.

Each of such div.item gets attached an handler for the click event, that will clone, populate and attach to dom the template defined to represent a preview.

The preview will show a bigger picture of the thumbnail clicked and some further details that the demo just limits to the item index number. Such bigger picture must be defined in the attribute data-original-image-url of the img thumbnail.

I was inspired to this question Gallery with bigger previews "in place" to craft the trick needed to determine the last item of a given row in a flex container.

Consider that the pictures url are fake here so the demo won’t show any picture but just broken urls. I faked the size of items setting a arbitrary different width for each of them and setting the height fixed in a css class.

//Add click event handler to every div.item in any .gallery
let items = document.querySelectorAll('.gallery > div.item');
for(let item of items){  
  item.addEventListener('click', onItemClick);
}

/*
 When an item gets clicked,
 It shows a preview, bound to the item clicked, on the next line
*/
function onItemClick(event){      
    
   //the clicked item
   //(currentTarget retrieves the item that had the listener not the actual element clicked)
   //so in this case it will be always the div.item (never the picture itself)
   clickedItem = event.currentTarget;
    
  //determines the index (1 based) of the clicked item relative to its container
  const itemIndex
  = Array.from(clickedItem.parentElement.querySelectorAll('.item')).indexOf(clickedItem) + 1;  
  
  //removes all .preview elements inside the parent of the clicked item
  clickedItem.parentElement.querySelectorAll('.preview').forEach( (o, i) => {
    o.remove();
  });
  
  //retrieves the url for the bigger picture of the item clicked
  let urlBiggerImage = clickedItem.querySelector('img').getAttribute('data-original-image-url');
  //retrieves the last item in the row where the item clicked lays
  let lastItemOfRow = getLastItemOfRow(clickedItem);
  
  //retrieves the html template for the preview
  const template = document.getElementById("template_preview");
  //clones the template to prepare the node as a concrete preview for the current item
  const preview = template.content.cloneNode(true);  
  //sets the img src of the preview using the value of this item.data[original-image-url]
  preview.querySelector('img').src = urlBiggerImage;  
  //sets the description of the preview
  preview.querySelector('.col:nth-child(2) p').innerText = `You selected the item n.${itemIndex}`;  
  //appends the preview item after the last item in the row where item clicked lays
  lastItemOfRow.after( preview );  
}

/*
Retrieves the last item in the row (of the flexbox container)
given an item determining the row to examine
Inspired by:
https://stackoverflow.com/questions/72096795/gallery-with-bigger-previews-in-place/72096982
*/
const getLastItemOfRow = (item) => {    
  
  const grid = item.parentElement;      
  const gridItems = Array.from(grid.querySelectorAll('.item'));  
  const itemIndex = gridItems.indexOf(item);      
  const baseOffset = item.offsetTop;
  
  let breakIndex = gridItems.findIndex(item => item.offsetTop > baseOffset) -1;        
  breakIndex = (breakIndex < 0) ? itemIndex : breakIndex;
  
  return gridItems[breakIndex];
}
.gallery{
  display: flex;
  flex-wrap: wrap;
  gap: 10px 10px;  
  padding: 10px;  
  border: solid 1px gray;      
  /*Here you can define an arbitrary width for the container*/
  width: 430px;
}

.item{
  display: block;  
  height: 50px;
  background: red;  
  color: white;
  font-weight: 700;  
  cursor: pointer;  
}

.item > img{
  height: 50px;
}

.preview{
  border: solid 1px black;
  flex: 0 0 100%;
}

.preview > .col{
  display: inline-block;
  padding: 5px;
  vertical-align: middle;
}

.bigger {
  width: 200px;
  height: 100px;
}
<template id="template_preview">
  <div class="preview">
    <div class="col">
      <img class="bigger" src=""/>
    </div>
    <div class="col">
      <p>You selected the picture n:</p>
    </div>
  </div>  
</template>

<!-- You can have many .gallery container -->
<div class="gallery">

  <!-- Each .item will count as an item with preview -->
  <div class="item">
    <img src="/img/1.jpg" title="1" style="width: 100px;" data-original-image-url="/img/bigger/1.jpg"/>
  </div>
  
  <div class="item">
    <img src="/img/2.jpg" title="2" style="width: 70px;" data-original-image-url="/img/bigger/2.jpg"/>
  </div>
  
  <div class="item">
    <img src="/img/3.jpg" title="3" style="width: 180px;" data-original-image-url="/img/bigger/3.jpg"/>
  </div>

  <div class="item">
    <img src="/img/4.jpg" title="4" style="width: 220px;" data-original-image-url="/img/bigger/4.jpg"/>
  </div>

  <div class="item">
    <img src="/img/5.jpg" title="5" style="width: 40px;" data-original-image-url="/img/bigger/5.jpg"/>
  </div>

  <div class="item">
    <img src="/img/6.jpg" title="6" style="width: 80px;" data-original-image-url="/img/bigger/6.jpg"/>
  </div>

  <div class="item">
    <img src="/img/7.jpg" title="7"  style="width: 50px;" data-original-image-url="/img/bigger/7.jpg"/>
  </div>

  <div class="item">
    <img src="/img/8.jpg" title="8" style="width: 50px;" data-original-image-url="/img/bigger/8.jpg"/>
  </div>

  <div class="item">
    <img src="/img/9.jpg" title="9" style="width: 50px;" data-original-image-url="/img/bigger/9.jpg"/>
  </div>
  
  <div class="item">
    <img src="/img/10.jpg" title="10" style="width: 50px;" data-original-image-url="/img/bigger/10.jpg"/>
  </div>

  <div class="item">
    <img src="/img/11.jpg" title="11" style="width: 50px;" data-original-image-url="/img/bigger/11.jpg"/>
  </div>

  <div class="item">
    <img src="/img/12.jpg" title="12" style="width: 50px;" data-original-image-url="/img/bigger/12.jpg"/>
  </div>
  
  <div class="item">
    <img src="/img/13.jpg" title="13" style="width: 80px;" data-original-image-url="/img/bigger/13.jpg"/>
  </div>
  
  <div class="item">
    <img src="/img/14.jpg" title="14" style="width: 150px;" data-original-image-url="/img/bigger/14.jpg"/>
  </div>
      
</div>

Answered By – Diego De Vita

Answer Checked By – Robin (AngularFixing Admin)

Leave a Reply

Your email address will not be published.