Edit: Ok let's have another try.
The main issue is to size your main image which should stay in its ratio and be wrapped in its parent. After trial and error there seems to be no easy method to archive this. I also tried object-fit
property, but event this is not working as you want to. So here are my two solutions.
Solution 1:
The most easy way is to use a div
with the background-image
property instead of an img
. As you see in the solution below, the main-image
is a div
now. You could also use the element .image-wrapper
as the background image. But I wrapped a main-image
into it to get some padding between these two elements.
Solution 2: Instead of using the background-image
property you can still use a img
, but you'll need some javascript and calculations. The calculation is done by the function calculateMainImage
. It is not too complex, but it needs some lines. The strategy is this:
- Get the dimensions of the main image and its parent element
- Assume that the image fits into its parent (in css:
width: 100%
). So calculate the image dimensions for this assumption.
- If the calculated height is greater than the height of the parent, the image won't fit into the parent. So now set the image's height to the height of its parent and recalculate the width.
This function is also called when the document is ready (initialization) and when the window resizes (window.onresize
).

let mainImage = document.querySelector('.main-image');
let thumbnails = document.querySelectorAll('.thumbnail');
thumbnails.forEach(thumbnail => {
thumbnail.addEventListener('click', () => {
let backgroundImage = 'url(' + thumbnail.src +')';
mainImage.style.backgroundImage = backgroundImage;
});
});
.gallery{
position: relative;
width: 100%;
height: 400px;
background: #dedede;
display: grid;
grid-template-columns: 70% 30%;
}
.image-wrapper{
position: relative;
width: 100%;
height: 100%;
background: #aaa;
padding: 12px;
box-sizing: border-box;
}
.main-image{
position: relative;
width: 100%;
height: 100%;
background-image: url("https://via.placeholder.com/350x150");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.thumbnail-wrapper{
position: relative;
width: 100%;
height: 100%;
overflow-y: auto;
padding: 12px;
box-sizing: border-box;
}
.thumbnail-wrapper > .thumbnail:not(:last-child){
margin-bottom: 12px;
}
.thumbnail{
position: relative;
display: block;
width: 100%;
height: auto;
margin-left: auto;
margin-right: auto;
cursor: pointer;
}
<section class="gallery">
<!-- Main image -->
<div class="image-wrapper">
<div class="main-image">
</div>
</div>
<!-- Thumbnails -->
<div class="thumbnail-wrapper">
<img class="thumbnail" src="https://via.placeholder.com/350x150">
<img class="thumbnail" src="https://via.placeholder.com/200x100">
<img class="thumbnail" src="https://via.placeholder.com/140x100">
<img class="thumbnail" src="https://via.placeholder.com/350x65">
<img class="thumbnail" src="https://via.placeholder.com/350x150">
<img class="thumbnail" src="https://via.placeholder.com/200x100">
<img class="thumbnail" src="https://via.placeholder.com/140x100">
</div>
</section>
let gallery = document.querySelector('.gallery');
let container = document.querySelector('.container');
let mainImage = document.querySelector('.main-image');
let thumbnails = document.querySelectorAll('.thumbnail');
(function(){
// Document ready
calculateMainImage();
// When window resizes
window.addEventListener('resize', () => {
calculateMainImage();
});
})();
thumbnails.forEach(thumbnail => {
thumbnail.addEventListener('click', () => {
mainImage.src = thumbnail.src;
// Fit image to container
calculateMainImage();
});
});
function calculateMainImage(){
// Reset current dimensions
mainImage.style.width = 'initial';
mainImage.style.height = 'initial';
// Container dimensions
let containerWidth = container.getBoundingClientRect().width;
let containerHeight = container.getBoundingClientRect().height;
// Image dimensions
let width = mainImage.getBoundingClientRect().width;
let height = mainImage.getBoundingClientRect().height;
let ratio = width / height;
// Calculate image dimensions when width: 100%
let maxWidth = containerWidth;
let maxHeight = maxWidth / ratio;
// Check if image fits in parent
if(maxHeight > containerHeight){
// Scale image down. Recalculate image's width
let newHeight = containerHeight;
let newWidth = newHeight * ratio;
setMainImageSize(newWidth, newHeight);
}else{
setMainImageSize(maxWidth, maxHeight);
}
}
function setMainImageSize(width, height){
mainImage.style.width = width + 'px';
mainImage.style.height = height + 'px';
}
.gallery{
position: relative;
width: 100%;
height: 400px;
background: #dedede;
display: grid;
grid-template-columns: 70% 30%;
}
.image-wrapper{
position: relative;
width: 100%;
height: 100%;
background: #aaa;
padding: 12px;
box-sizing: border-box;
}
.container{
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.main-image{
position: relative;
flex-grow: 0;
flex-shrink: 0;
}
.thumbnail-wrapper{
position: relative;
width: 100%;
height: 100%;
overflow-y: auto;
padding: 12px;
box-sizing: border-box;
}
.thumbnail-wrapper > .thumbnail:not(:last-child){
margin-bottom: 12px;
}
.thumbnail{
position: relative;
display: block;
width: 100%;
height: auto;
margin-left: auto;
margin-right: auto;
cursor: pointer;
}
<section class="gallery">
<!-- Main image -->
<div class="image-wrapper">
<div class="container">
<img class="main-image" src="https://via.placeholder.com/200x100">
</div>
</div>
<!-- Thumbnails -->
<div class="thumbnail-wrapper">
<img class="thumbnail" src="https://via.placeholder.com/350x150">
<img class="thumbnail" src="https://via.placeholder.com/200x100">
<img class="thumbnail" src="https://via.placeholder.com/140x100">
<img class="thumbnail" src="https://via.placeholder.com/350x65">
<img class="thumbnail" src="https://via.placeholder.com/350x150">
</div>
</section>
Update
When you have several gallerys on your page and use use solution #1, then you need to add this JS snipped.
let gallerys = document.querySelectorAll('.gallery');
gallerys.forEach(gallery => {
updateGalleryPictures(gallery)
});
function updateGalleryPictures(gallery) {
// Get gallery's main image
let mainImage = gallery.querySelector('.main-image');
if (mainImage === null) return;
// Get gallery's thumbnail images
let thumbnails = gallery.querySelectorAll('.thumbnail');
// Change the background-image property on click
thumbnails.forEach(thumbnail => {
thumbnail.addEventListener('click', () => {
let backgroundImage = 'url(' + thumbnail.src + ')';
mainImage.style.backgroundImage = backgroundImage;
});
});
// Initialize background-image property using the 1st thumbnail image
let firstThumbnail = thumbnails[0];
if (firstThumbnail === null || firstThumbnail === undefined) return;
let initialBackgroundImage = 'url(' + firstThumbnail.src + ')';
mainImage.style.backgroundImage = initialBackgroundImage;
}
Update 2: Usage of baguetteBox
Step 1: Add the property overflow: hidden;
to the class .image-wrapper
and create a new class
.baguettebox-image{
opacity: 0 !important;
}
Step 2: Change the structure of the main-image
setup.
<div class="image-wrapper">
<div class="main-image"> <!-- THis is the baguette box -->
<a class="baguettebox-link" href="">
<img class="baguettebox-image" src=""></a>
</a>
</div>
</div>
Step 3: Change the JS snipped of the 1st update to:
Note that the baguette boxes are also initialized in this script. According to the documentation querySelectorAll
is used, so all boxes (.main-image
) should be initialized.
let gallerys = document.querySelectorAll('.gallery');
gallerys.forEach(gallery => {
updateGalleryPictures(gallery);
});
// Initialize all baguette boxes
baguetteBox.run('.main-image');
function updateGalleryPictures(gallery) {
// Get gallery's thumbnail images
let thumbnails = gallery.querySelectorAll('.thumbnail');
// Change the background-image property on click
thumbnails.forEach(thumbnail => {
thumbnail.addEventListener('click', () => {
updateMainImage(gallery, thumbnail.src)
});
});
// Initialize background-image property using the 1st thumbnail image
let firstThumbnail = thumbnails[0];
if (firstThumbnail === null || firstThumbnail === undefined) return;
updateMainImage(gallery, firstThumbnail.src)
// Initialize baguette
}
function updateMainImage(gallery, src) {
// Get main image and check if it exists
let mainImage = gallery.querySelector('.main-image');
if (mainImage === null) return;
mainImage.style.backgroundImage = 'url(' + src + ')';
// Get baguette elements
let boxLink = gallery.querySelector('.baguettebox-link');
let boxImage = gallery.querySelector('.baguettebox-image');
// Update baguette elements href and src
if (boxLink !== null && boxLink !== undefined) boxLink.href = src;
if (boxImage !== null && boxImage !== undefined) boxImage.src = src;
}