Dynamic image loading in a responsive slideshow
Enter dynamically loaded content
Diving into the details (and the code)
By default, we used Drupal's image styles to scale the images. Doing this compresses the images after a user has uploaded them and therefore ensures we’re working with files sizes that aren’t through the roof. Alternatively, you could simply set the maximum upload size to something other than infinity. We then used the Flex Slider module which gave us a responsive slideshow and touch navigation right out of the box. To bring it all together, we used a view template in which we created three separate containers that held the three different image styles we created earlier. We then swapped out those divs in our media queries based on specific breakpoints.
Ye be warned, pitfalls abound
It’s time to get into the nitty gritty (the code), but before we do you’ll have to forgive me. This works. That doesn’t mean it’s perfect. Details on the less-than-ideal pieces later.
First, let's check out the view template
Here’s where we’re setting the three divs that contain our image styles. For simplicity, I’ve excluded some of code that contains our headlines, buttons, and other text.
<div class="flex-small-box" style="display: none"><br> <div class="fleximage small" style="background-image:url(<?php print $field->last_tokens['[field_image_1]']; ?>)"><br></div><br></div><br><br><div class="flex-medium-box" style="display: none"><br> <div class="fleximage medium" style="background-image:url(<?php print $field->last_tokens['[field_image_2]']; ?>)"><br></div><br></div><br><br><div class="flex-large-box" style="display: none"><br> <div class="fleximage large" style="background-image:url(<?php print $field->last_tokens['[field_image_3]']; ?>)"><br></div><br></div>
What we're doing here is printing out the fields that hold our three image styles and hiding them by default. We do this to ensure the large images never load on small devices. Everything is hidden until we tell it to be visible. Not so bad, right? Just override your view template, and add some custom markup around the fields.
Now, let's take a look at the CSS. Again, I'm only including the essential pieces of CSS. I'm using SASS here, so there aren't any braces or semicolons.
.view-frontpage-gallery<br> position: relative<br> overflow: hidden<br><br> .flexslider<br> border: none // Override default flexslider border<br> background: none<br> // Override default flexslider box shadow<br> @include box-shadow(none) <br><br> .flex-viewport<br> position: relative<br> width: 100%<br><br> .flex-large-box<br> display: block !important<br><br> .fleximage<br> // Don't use position absolute here to avoid breaking background size<br> height: 480px<br> background-repeat: no-repeat<br> // Stretch the image to the full width of the container<br> background-size: 100% auto
Where the magic happens
This is where we add our CSS based on specific break points by using media queries. Again, I’m using SASS to import stylesheets under my media queries. Alternatively, you could load the CSS directly under the query itself. There’s nothing really mysterious here... It’s actually far from magic.
@media only screen and (max-width: 480px)<br> @import 'small'<br><br>@media only screen and (max-width: 700px) and (min-width: 481px)<br> @import 'medium'<br><br>@media only screen and (max-width: 960px) and (min-width: 701px)<br> @import 'large' <br><br>// Large<br>view-frontpage-gallery<br> .flexslider<br> width: 960px // Prevent the image from scaling<br><br> .flex-nav-container<br> height: 290px<br> <br> .flex-medium-box, .flex-small-box<br> display: none !important<br><br> .flex-large-box<br> display: block !important // Show the largest image<br> <br> .fleximage<br> height: 320px<br><br>// Medium<br>.view-frontpage-gallery<br> .flexslider<br> width: 700px !important<br><br> .flex-large-box, .flex-small-box<br> display: none !important<br><br> .flex-nav-container<br> height: 230px<br><br> .flex-medium-box<br> display: block !important<br><br> .fleximage<br> height: 350px<br><br>// Small<br>.view-frontpage-gallery<br> .flex-medium-box, .flex-large-box<br> display: none !important<br><br> .flex-nav-container<br> height: 130px<br><br> .flex-small-box<br> display: block !important<br> .fleximage<br> display: block<br> height: 160px<br> width: 480px
There’s always a catch, isn’t there?
The catch to all of this is that it’s a little messy.
First, I’m using inline CSS in my view template which is not ideal. Overriding inline CSS often leads to adding ‘!important’ to other CSS rules which often leads to more overriding issues.
Second, because we’re using background images, I needed to tell the images' parent container to be a specific height in order to show that image. This part isn’t pretty either because I had to adjust that height at every break point as the image resized. It’s also not best practice to set heights on page elements. Ideally, you want to the page elements to be vertically flexible.
Last, the positioning of the elements within the slideshow container can be quite unruly in their desire to follow the block-model. I had to do some interesting things to force them into the desired location and even then, there are visual bugs that rear their ugly heads on certain screen sizes.
The end, with room for improvement
That's basically the method to the madness. In essence, I'm hiding and showing divs based on certain breakpoints and then adjusting the width and height of the those divs accordingly. What isn't shown here is that I hide the navigation controls on small screens because flex slider provides swiping navigation out of the box. This helps de-clutter the slideshow on small screens.
In an ideal world, I’d have crystal clear code, beautifully aligned text and navigation on all screen sizes, and a much leaner solution but I decided to err on the user experience side which is to have dynamically sized images based on a user’s device. In the end, I can live with some messy code. What I can’t live with is a user that hates their experience so much that they never return. I'm reminded of a common phrase I've read recently which is, “Nobody wants to wait while they wait.”