On the web usually all images within a group of images are set to the same width or the same height. CSS doesn’t allow us to apply relative heights to images according to their actual sizes.
In most cases that’s fine, but there are situations, in which we really need our images to appear with realistic dimensions, as in the case of books on a shelf.
In my book shelf markup each book spine image is linked to the book’s individual page (e.g. a product page):
<div class="bub-bookspines"> <ul class="bub-bookspines__list"> <li class="bub-bookspines__item"> <a href="<?php echo $book_url ?>"> <img src="<?php echo $spine_url ?>" title="<?php echo $spine_alt ?>" data-height="<?php echo $spine_height ?>"> </a> </li> </ul> </div>
Now here’s the JavaScript / jQuery function, that applies relative heights to our images:
/** * Set Relative Image Heights * * Sets authentic heights to all images within the passed container: The * highest of all contained images will be set to 100% (of the container's * height). All other images will receive a relative height based on their * natural dimensions. The function was first created in order to properly * display books on a shelf. * * @param string. The container selector (e.g. '.my-container'). * @uses imagesLoaded.js */ bubjs.setRelativeImageHeights = function( container ) { 'use strict'; // Select image container. var $container = $( container ); var $images = $container.find( 'img' ); // Get all 'height' data attribute values in an array (the 'data-height' // attribute, containing the actual image height, must be added via PHP). var heightsData = $images.map( function() { return $( this ).data( 'height' ); }).get(); // Find the highest original image. var highestData = Math.max.apply( Math, heightsData ); var $imagesHighest = $images.filter('[data-height=' + highestData + ']'); // Set default height and width values: Give container descendants 100% // height in order to get every element between the container and the image // out of the way. $container.find( '*' ).css( 'height', '100%' ); $images.css( 'width', 'auto' ); // Set each image's height. $container.imagesLoaded().always( setImageHeights ); $( window ).on( 'resize', setImageHeights ); /** * Set Image Heights */ function setImageHeights() { // Get the displayed image height. var heightDisplay = $imagesHighest.height(); // Calculate and set height for each image, based on the natural image // dimensions and the highest image's current display height. $images.each( function( index, item ) { var $item = $( item ); var itemData = $item.data( 'height' ); var percentOfHighest = 100 / highestData * itemData; var itemTargetHeight = heightDisplay / 100 * percentOfHighest; $item.css({ 'height': itemTargetHeight + 'px', }); }); // Always set highest image to container height for proper resizing. $imagesHighest.css({ 'height': '100%', }) } }
When we call the function, we must pass the container element:
$( document ).ready( function() { var $list = $( '.bub-bookspines__list' ); // Pass container selector to the 'Relative Image Heights' helper. bubjs.setRelativeImageHeights( $list ); });
Let’s say we want our book shelf to fill the entire viewport width and height of our home page. And there should be some visual feedback, if a user hovers over an image. Here’s the CSS:
.home .bub-bookspines { height: 100vh; position: relative; visibility: hidden; } .home.bubijs-loaded .bub-bookspines { visibility: visible; } .home .bub-bookspines__list { height: 85%; margin: 0; /* Account for fixed site header and bottom bar. */ padding: 100px 0 0 0; overflow: hidden; position: absolute; bottom: 0px; } /* Add more top padding for Chrome Android */ @supports (-webkit-appearance: none) { .home.is-mobile .bub-bookspines__list { padding-top: 120px; } } .home .bub-bookspines__item { display: block; float: left; height: 100%; margin: 0 9px 0 0; list-style-type: none; } .home .bub-bookspines__item a { display: flex; align-items: flex-end; } .home .bub-bookspines__item img { box-shadow: 2px 2px 2px rgba(0,0,0,0.3), 2px 2px 10px rgba(0,0,0,0.3); height: 100%; transform: translateZ(0); transition: all 200ms; } .home .bub-bookspines__item img:hover { transform: scale(1.05) translate(0%, -3%) translateZ(0); } @media screen and (max-width: 800px) { .home .bub-bookspines__list { overflow-x: hidden; white-space: nowrap; } .home.is-mobile .bub-bookspines__list { overflow-x: scroll; white-space: nowrap; } .home .bub-bookspines__item { float: none; display: inline-block; margin: 0 5px 0 0; } } @media screen and (max-width: 300px) { .home .bub-bookspines__item img { width: auto; } }