I'm working on a product category page where the product images have inconsistent heights so I've been asked to improve the page appearance without changing the images.
The page contains x number of products in a 3 column grid.
I decided to set the height of the image wrapper to the height of the image with the maximum height in each row, then use flex to vertically center the images.
How could I improve my JavaScript?
/**
* Ensure all rows appear the same height even if they contain products with
* small images.
*/
const imgWrappers = Array.from(document.querySelectorAll('.summary-image-wrapper'));
const rows = [];
// 3 Products per row - First group the image wrappers into groups of 3
while (imgWrappers.length !== 0) {
rows.push({
elements: imgWrappers.splice(0, 3),
});
}
// Then establish which element is the tallest in it's group
rows.forEach((row) => {
let height = 0;
row.elements.forEach((el) => {
const currHeight = el.firstElementChild.getAttribute('height');
height = currHeight > height ? currHeight : height;
});
row.height = height;
// Set the height of each element to that of the tallest element in the row
row.elements.forEach((el) => {
el.style.height = `${row.height}px`;
});
});
<div class="container">
<div class="row">
<div class="col-6 col-md-4">
<div class="summary-product">
<a href="http://examplelink.com/" class="woocommerce-LoopProduct-link woocommerce-loop-product__link">
<div class="summary-image-wrapper" style="height: 413px;">
<img width="300" height="413" src="http://via.placeholder.com/300x413" class="attachment-woocommerce_thumbnail size-woocommerce_thumbnail wp-post-image" alt="alt ttext" srcset="http://via.placeholder.com/300x413 300w, http://via.placeholder.com/218x300 218w, http://via.placeholder.com/350x482 350w, http://via.placeholder.com/581x800 581w" sizes="(max-width: 300px) 100vw, 300px">
</div>
<h2 class="woocommerce-loop-product__title summary-product-title">Example title</h2>
<span class="price summary-product-price">POA</span>
</a>
</div>
<!-- Further .summary-products have been omitted for brevity-->
</div>
</div>
</div>
EDIT: I discovered an error within my original code due to string comparison. Take the following row of 3 images and their height:
Image a: 365
Image b: 91
Image c: 401
Given the above images using the code originally given 'image b' will be considered the largest. This is because el.firstElementChild.getAttribute('height')
returns a string, when comparing strings javascript does it "alphabetically". This means that js looks at the first character of the string then the second and so on. When comparing two numberic strings if a string starts with a 9 it will always be the largest!
'10000000000' > '9' //false
'89999999999' > '9' //false
'91' > '9' //true
To ensure the original code works the value stored in currHeight needs to be converted to a number e.g currHeight = Number.parseInt(el.firstElementChild.getAttribute('height'));
before comparison.