JavaScript
Article
By Damon Bauer

Introducing enquire.js

By Damon Bauer

For some time now, I’ve been using the enquire.js library to augment the use of media queries in JavaScript. I’ve had a lot of success using the library in various projects, but most developers I’ve met confessed they’ve never used or even heard of it.

In this article, I want to introduce you the library, explain why and how you’d want to use it.

What is enquire.js?

enquire.js is a lightweight, pure JavaScript library for responding to CSS media queries. In my own words, enquire.js gives you additional functionality and flexibility on top of window.matchMedia() when working with media queries in JavaScript.

The library was written by Nick Williams and it’s been around for about three years, having a healthy amount of open source activity. Its aim is not to replace or polyfill matchMedia; rather, it exists to provide additional functionality around media queries that don’t exist natively.

--ADVERTISEMENT--

Why Would I Use It?

There are lots of use cases where enquire.js could be used, including:

  • Move nodes around the DOM
  • Load supplementary content (e.g. ads) via Ajax
  • Load and run a JavaScript library (e.g. Packery) to enhance a page

Each of these use cases is covered in the following sections.

DOM Manipulation

Although there are some performance and maintainability considerations with using JavaScript to manipulate the DOM, there are many reasons why would do this.

Let’s assume that on your blog, you have a bio with an image in a sidebar. On small screens, the sidebar is hidden – but you still want the bio visible at the end of your post. Instead of duplicating the bio (and image) markup, you could move it around the DOM depending on the screen size avoiding markup duplication. Here’s an example:

window.$ = document.querySelector.bind(document);

enquire.register("screen and (max-width:40em)", {
  match: function() {
    $(".Post-content").appendChild($(".Bio"));
  },

  unmatch: function() {
    $(".Sidebar").insertBefore($(".Bio"), $(".Sidebar").firstChild);
  }
});

A live demo of this example is shown below:

See the Pen MwNYjp by SitePoint (@SitePoint) on CodePen.

This is a rudimentary example, and honestly doesn’t do much more than what native matchMedia() provides. In fact, here’s how you could do this with just matchMedia():

window.$ = document.querySelector.bind(document);

(function() {
  var mq = window.matchMedia("(min-width:40em)");

  mq.addListener(positionBio);

  function positionBio(mediaQuery) {
    if (mediaQuery.matches) {
      $(".Sidebar").insertBefore($(".Bio"), $(".Sidebar").firstChild);
    } else {
      $(".Post-content").appendChild($(".Bio"));
    }
  }

  // On load
  positionBio(mq);
}());

A live demo of this example is shown below:

See the Pen WvVbor by SitePoint (@SitePoint) on CodePen.

Let’s get back to enquire.js and take the example from above a step further. In the next code we’ll use the setup function. It’s a function that runs just once as soon as enquire.register is executed. In the example we’ll also cache a few selectors to improve the performance of the code, and make it more readable and maintainable. While this is a contrived example (and you could do the same without enquire.js), it enables me to introduce you to the setup function and to explore more complex examples:

window.$ = document.querySelector.bind(document);

enquire.register("screen and (max-width:40em)", {
  setup: function() {
    this.bio = $(".Bio");
    this.content = $(".Post-content");
    this.sidebar = $(".Sidebar");
  },

  match: function() {
    this.content.appendChild(this.bio);
  },

  unmatch: function() {
    this.sidebar.insertBefore(this.bio, this.sidebar.firstChild);
  }
});

A live demo of this example is shown below:

See the Pen MwNYbp by SitePoint (@SitePoint) on CodePen.

Supplementary Content

Let’s focus our attention on another use case: loading ad content via Ajax depending on the size of the viewport. This example uses the setup() function again and introduces another feature of enquire.js: deferSetup(). It also uses the reqwest Ajax library (no typo here) and the classList API to demonstrate how a more complex example could be written without jQuery. In case you don’t know what this API does, you can read the article Exploring the classList API.

In the next example, inside the setup method (that I remind you that only runs once, when this media query matches), we use jQuery’s ajax() method to request the page ad.html and to store the result in an ad property. Then, we set the content of .Ad to the property and show the container by adding an is-visible class (handled with CSS).

If the media query is ever “unmatched”, we’ll remove the is-visible class that will, in turn, hide the .Ad container.

Finally, we’ll use the deferSetup flag along with the setup() method so that the Ajax request only happens once, and only when the media query is matched. This is useful because the call to Ajax is delayed until the point the information is needed instead of making a premature request.

The code that implements what I’ve just described is reported below:

window.$ = document.querySelector.bind(document);

enquire.register("screen and (min-width:40em)", {
  deferSetup: true,
  setup: function() {
    this.ad = "";
    this.adContainer = $(".Ad");
    this.visibleClass = "is-visible";
    this.adUrl = "//codepen.io/damonbauer/pen/LVgxxN.html";

    reqwest({ url: this.adUrl, crossOrigin: true})
      .then(function(response) {
        this.ad = response;
      }.bind(this)).fail(function() {
        this.ad = "Ad could not be loaded.";
      }.bind(this)).always(function(response) {
        this.adContainer.innerHTML = this.ad;
      }.bind(this));
  },

  match: function() {
    this.adContainer.classList.add(this.visibleClass);
  },

  unmatch: function() {
    this.adContainer.classList.remove(this.visibleClass);
  }
});

In addition to the previous JavaScript code, we also have to use the small CSS snippet reported below:

.Ad {
 display: none;
}

.is-visible {
 display: block;
}

A live demo of this example is shown below:

See the Pen vOoEyb by SitePoint (@SitePoint) on CodePen.

Page Enhancements

In this section, we’ll take a look at adding the Packery library to enhance a page at larger sizes. According to its documentation, Packery “fills empty gaps”. It helps you accomplish something similar to Pinterest’s layout, where cards fill available space in a grid.

Let’s say that you have the following HTML in your page:

<div id="Container">
  <div class="Item">...</div>
  <div class="Item">...</div>
  <div class="Item">...</div>
  ...
</div>

The following code will use the packery library to achieve what I previously described:

enquire.register("screen and (min-width:30em)", {
  deferSetup: true,

  setup: function() {
    this.container = $( "#Container" );
  },

  match: function() {
    this.container.packery({
      itemSelector: '.Item'
    });
  },

  unmatch: function() {
    this.container.packery('destroy');
  }
});

A live demo of this example is shown below:

See the Pen OVKPWL by SitePoint (@SitePoint) on CodePen.

This is pretty similar to the first example. It starts by storing a container as a property in the setup() method. Then, if the media query matches, it’ll layout items following the packery convention; otherwise we destroy the packery instance by calling its destroy method.

A potential use case for this example might be a tablet being rotated from landscape to portrait. Perhaps we’ve determined that the screen is too narrow to display the items in a certain way, so we can opt for the items to be stacked vertically.

Potential Pain Points

Hopefully, you’ve gotten a glimpse at how quick and powerful enquire.js can be to use. I’d like to share a couple of instances where I’ve run into problems when using the library.

Old Versions of Internet Explorer

While it doesn’t fully support IE8, I can tell you that sites I built are don’t have any render-breaking JavaScript errors. Enquire.js depends on the matchMedia API, which is not supported in versions of Internet Explorer prior to 10. In case you have to support such browsers, I suggest you to use the matchMedia polyfills written by Paul Irish.

To do so, you have to load the files provided before enquire.js. Moreover, I suggest you to place these scripts in an IE conditional comment as shown below:

<!--[if lte IE 9]>
  <script src="/path/to/matchMedia.js"></script>
  <script src="/path/to/matchMedia.addListener.js"></script>
<![endif]-->

Thanks to this approach, only the browsers who need the polyfills will load them. It’s important to highlight that even if you load them in browsers supporting these features, you’ll still have the native implementation as both the scripts are written in a non-invasive way. This means that before executing any code, they detect if the browser supports the features natively. If that’s the case, the polyfill code is skipped altogether.

Manipulating the DOM

In one of the examples above, I showed how to use enquire.js to manipulate the DOM. While this technique works, I would advise a healthy bit of caution in employing it. Watch out for adding lots of DOM changes in multiple enquire.js match and unmatch methods as the more operations you add, the harder it’ll become to debug the page if something goes wrong. In addition, since manipulating the DOM is usually slow, using this technique can lead to page jank and poor performance.

Progressive Content

My suggestion is to use enquire.js to “progressively” provide content or functionality, instead of rely on it to show critical parts of content. My advice is to initially show basic content and then enhance it with enquire.js, rather than depending on all your content being enhanced.

In case of the page enhancements’ example, the grid of items is visible by default; if the screen is large enough, the items are laid out using the Packery.js library. This ensures that if for whatever reason (e.g. broken JavaScript, slow connection, and so on) the JavaScript isn’t executed, a user can still consume the content.

Mobile First

It’s a common and suggested practice these days to build sites using a mobile-first approach. If a browser you need to support doesn’t understand media queries (such as IE8), enquire.js has a parameter called shouldDegrade that comes in handy. It specifies that the result of the enquire.register block should always be executed if the browser doesn’t understand media queries. By default, it’s value is set to false but you can change it to true to fit your needs.

An example of use of this parameter is reported below:

enquire.register("screen and (min-width:40em)", function() {
  // execute some code for large-screen devices
}, true); // note the true!

By using snippets like this, you can still build mobile-first websites and support older browsers to have a desktop experience. My advise is to use this parameter or the polyfills mentioned above, not both.

Conclusions

In this article I’ve introduced you to enquire.js, a powerful library written in pure JavaScript for responding to CSS media queries. Hopefully, I’ve demonstrated you how it gives you extra features on top of the native matchMedia() method to allow you to build performant, feature-rich websites across all screen sizes. While this has not been an exhaustive overview of the library, this should be enough to push you to learn more about it and to think if there’s a place for it in your projects.

Have you ever heard about enquire.js? Have you ever tried to use it in your projects? What about your results? Share your comments below and let’s start a discussion!

  • Aankhen

    In my own words, enquire.js gives you additional functionality and flexibility on top of window.matchMedia() when working with media queries in JavaScript.

    To be honest, all I see is a tiny bit of encapsulation above and beyond what matchMedia() provides, which could just as easily be achieved with an anonymous function as in one of the examples.

  • Ayan Dey

    I have used enquire.js but the problem I faced is with resizing the browser window. It does jump to the required match unless a page refresh is done.

Recommended
Sponsors
Get the latest in JavaScript, once a week, for free.