Skip to main content

Cognition

Enquire for Backgrounds

I often turn to background images and inline-CSS to achieve certain designs. In these cases, I’m looking for an image fill in available space without looking “broken” and background-size: cover is the usual go-to.

This works great, my image fills in space as needed. Some of the graphics edges are sacrificed so that it doesn't look broken and the design team is happy that nothing looks… well, looks funny.


<div class="image" style="background-image: url('image.jpg')"></div>

.image {
    background-size: cover;
    background-repeat: no-repeat;
    background-position: center;
}

The snag is that we often need to swap images for different images be it a different dimension. At a certain point, background-size: cover just isn't enough. Or we need accommodate the ever-increasing list of devices that have high-resolution screens.

Well, I don't see any working group for responsive background images with inline-css. We'll have to fix this ourselves.

Okay, not entirely by ourselves. I've turned to enquire.js to help smooth out responsive issues many times and this is a perfect example of where this useful little tool excels. This solution also makes use of jQuery and matchMedia (a matchMedia polyfill is included in Modernizr or available separately).

The Mark-up

Let's get started. First, our mark-up:


<div class="article-block" style="background-image: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/9186/sky-square.jpg')"">

  <div class="article-block__body">
    <h1>Blue Heaven on Earth</h1>
  </div>

</div>

Ooof. Inline styles, I know. But that CMS can't write to the CSS file so this is how it needs to happen. It'll get worse before it gets better.

From here, we need to add a hook for our JavaScript—we don't want to mingle our CSS classes with javascript, so I'll add a js-bgimg class.


<div class="article-block js-bgimg" style="background-image: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/9186/sky-square.jpg')">
…
</div>

Great. We have four images we need to swap in and out. And yes, I may refer to things as “mobile” and “desktop” sizes when I know full well that is a misnomer. You'll just have to accept that. We have:

  • Mobile and standard resolution: sky-square.jpg.
  • Mobile and high resolution: sky-square@2x.jpg.
  • Desktop and standard resolution: sky.jpg.
  • Desktop and high resolution: sky@2x.jpg.

I like to put these in data attributes in the markup, like so:


<div class="article-block js-bgimg" style="background-image: url('sky-square.jpg')" data-bgimg="sky-square.jpg" data-bgimg-retina="sky-square@2x.jpg" data-bgimg-large="sky.jpg" data-bgimg-large-retina="sky@2x.jpg">
  …
</div>
  …
</div>

Let's go ahead and set up our script.

To the jQuery!

We're going to call this for each of our js-bgimg elements. Let's use the jQuery each() function:


$('.js-bgimg').each(function() {
    // we'll write some more code here.
});

This will iterate through each of our background image elements if we have more than one on a page. From there, it's useful and encouraged to start using some variables. Let's do that for our js-bgimg element and each of our images in the data attributes.


var bgImg = $(this);
var imgStandard = bgImg.data('bgimg');
var imgRetina = bgImg.data('bgimg-retina');
var imgLarge = bgImg.data('bgimg-large');
var imgLargeRetina = bgImg.data('bgimg-large-retina');

It'd also be useful to have a function that swaps the image on the style attribute, so let's do that as well:


function replaceImg(el, replacementImg) {
  el.attr('style', 'background-image: url("'+ replacementImg +'")');
}

The replaceImg() function will accept two parameters: the target element with the background and the new image. It uses the the attr() jQuery function to do this. We'll be able to pass it images later to do the swapping work.

Enquire About Things

We're all set, now we need to start writing our enquire.js object. It's set up like this:


enquire.register(mediaquery, {
    match: function() {
        // execute code when media query is matched
    },
    unmatch: function() {
        // execute code when media query goes from matched to unmatched
    },
    setup: function() {
        // execute code when enquire is first registered
    }
});

(There is more to enquire beyond just this, so I encourage you to look more into its features.)

It's worth noting that setup is only run once—when the handler is first registered. Otherwise, we're dealing with match and unmatch. Once we plug in our replaceImg() function, enquire is going to look like this:


enquire.register("screen and (min-width: 62em)", {
    match : function() {
      replaceImg(bgImg, imgLarge);
    },
    unmatch : function() {
      replaceImg(bgImg, imgStandard);
    }
});

When 62ems matches, it'll load in our desktop sized image. When it's unmatched swap in the mobile size. Perfect. What we're not accounting for here is high resolution devices. For that we'll use a second query chained to the first:


enquire.register("screen and (min-width: 62em)", {
    …
})
.register("screen and (min-width: 62em) and (-webkit-min-device-pixel-ratio: 2), screen and (min-width: 62em) and (min-resolution: 192dpi)", {
  match : function() {
    replaceImg(bgImg, imgLargeRetina);
  },
  unmatch : function() {
    replaceImg(bgImg, imgRetina);
  }
});

The second query checks for screen resolution and screen width so we know we're swapping in the right files.

Wrapping Up

The last piece has to do with setup and our first load. Since our HTML has the low-resolution and mobile version baked into the markup, we don't need to worry about that. But what if it turns out we're on a mobile device with a retina screen? That's pretty common, I hear. We'll need to check for that.

In our first enquire query, we'll use setup to check for retina devices. Enquire already uses matchMedia to do its magic, so we can safely use that as well.


setup : function() {
  if (matchMedia('screen and (-webkit-min-device-pixel-ratio: 2), screen and (min-resolution: 192dpi)').matches) {
    replaceImg(bgImg, imgRetina);
  }
}

A short if-statement that checks for retina devices and swaps in the mobile retina quality graphic does the trick. Remember, since it's in the default HTML we don't really need to check for non-retina screens.

Call the function, and it’s golden. There is a demo of this I've put together on codepen with detailed comments to help along anyone who wants to dive in.

I’ve found enquire.js to be a useful tool for optimized inline background images, but this is just one example of it working wonders. It's useful for any kind of JavaScript you're writing that needs to handle different media query contexts. Have you used enquire.js or a similar tool to help manage JavaScript in your projects? Let us know in the comments below.

Back to Top

comments powered by Disqus