The Lowdown
What is it?
enquire.js is a lightweight, pure JavaScript library for responding to CSS media queries.
- JavaScript callbacks for media queries matching and unmatching.
- Clean & intuitive API
- Absolutely tiny - around 0.8kb minified & gzipped!
Why should I use it?
In responsive design, CSS media queries can only take you so far. enquire.js allows you to harness media queries in JavaScript, giving you the tools to create advanced responsive sites.
Dependencies?
None! Not even jQuery.
The most you will need to do is provide a matchMedia
polyfill if you wish to support old/incapable browsers.
Downloads & All That Jazz
Latest Build
Grab the latest code, ready to go, from one of the locations below:
- Development — unminified
- Production — minified
If you wish to browse/fork the source, you can do so on GitHub.
Install via Bower
To install via bower, enter the following at the command line:
bower install enquire
Install via npm
To install via npm, enter the following at the command line:
npm install enquire.js
Build From Source
If you would like to build from source (and run all unit tests etc.), enter the following at the command line:
git clone git://github.com/WickyNilliams/enquire.js.git
cd enquire.js
npm install
grunt build
Quick Start
The main method you will be dealing with is register. Its signature is as follows:
enquire.register("screen and (max-width:45em)", {
// OPTIONAL
// If supplied, triggered when a media query matches.
match : function() {},
// OPTIONAL
// If supplied, triggered when the media query transitions
// *from a matched state to an unmatched state*.
unmatch : function() {},
// OPTIONAL
// If supplied, triggered once, when the handler is registered.
setup : function() {},
// OPTIONAL, defaults to false
// If set to true, defers execution of the setup function
// until the first time the media query is matched
deferSetup : true,
// OPTIONAL
// If supplied, triggered when handler is unregistered.
// Place cleanup code here
destroy : function() {}
});
This should be enough to get you going, but please read on for a more detailed guide.
Walkthrough
The easiest way to learn is by example. So with that in mind, let's work through a problem using enquire.js to solve our issues. We won't have full code for the examples, as the details are less important than the concepts at play.
The Scenario
We're developing a website with using a mobile-first approach. For small viewports we want a single column containing just the main content. For larger viewports we want to display a sidebar containing supplementary content. A large portion of our user base accesses the site on mobile devices with slow connections - so it's required that the page weight be kept to a minimum.
Normally we would use CSS media queries for showing and hiding the sidebar, but this has been ruled out by that final requirement. If performance is paramount we shouldn't be loading content needlessly! So the only other option is to use a JavaScript-based solution. In the past, such an approach would probably be unreliable and ugly. Thankfully, times have changed…
Match
enquire.js makes solving this problem trivial. Let's say that we want the sidebar displayed at 45em. A simple solution would be as follows:
enquire.register("screen and (min-width:45em)", function() {
// Load sidebar content in via AJAX.
// Show sidebar
});
By supplying a function to register
, you will be able to respond to a media query being matched. Therefore, when the query is matched we will load in content and insert it into the DOM to be displayed. Problem solved? Almost, but not quite!
Unmatch
We also need to handle the case where the query goes from a matched state to an unmatched state. For this we can supply an object to register
, instead of a function. This gives us a lot more power and allows us to handle unmatching:
enquire.register("screen and (min-width: 40em)", {
match : function() {
// Load sidebar content in via AJAX.
// Show sidebar
},
unmatch : function() {
// Hide the sidebar
}
});
In a lot of circumstances this is an adequate and complete solution. However, for our scenario we can do better.
Setup
There's a problem with the above solution. What happens if the query gets matched a second time? We needlessly make a second AJAX request. We could put a check in our match
callback to see if the content has already been loaded, but that sucks because match no longer has a single responsibility. This is where setup
comes in.
enquire.register("screen and (min-width: 45em)", {
setup : function() {
// Load in content via AJAX (just the once)
},
match : function() {
// Show sidebar
},
unmatch : function() {
// Hide sidebar
}
});
Setup is run once, as soon as a handler is registered. So now all our one-time code is in a setup callback, meaning no needless AJAX requests. This gives a neat separation of concerns, freeing the match callback from handling any upfront work.
Deferring Setup
So now we've got to a point were our task is almost complete. Content is dynamically loaded in at setup, and the sidebar shown and hidden on match and unmatch respectively. But we can still make one further improvement! By default setup
is executed as soon as the query is registered. What this means is that our AJAX request is being made even for small viewports - where it will never be needed because the content will never be shown! The solution to this problem is simple: deferSetup
.
enquire.register("screen and (min-width: 45em)", {
deferSetup : true,
setup : function() {
// load content via AJAX
},
match : function() {
// show sidebar
},
unmatch : function() {
// hide sidebar
}
});
And with that simple adjustment, we now make our AJAX request just once, the first time the media query is matched!
Delving Deeper
Multiple Handlers
As the complexity of your site increases, it can get cumbersome to have all your logic in one handler. enquire allows you to register multiple handlers for a media query, so you can divide functionality into logical chunks. Supply an array as the second parameter to register
to have multiple handlers per media query:
// You can pass an array containing your handlers
enquire.register("screen and (min-width: 45em)", [
{ match : function() { console.log("handler 1 matched"); } },
{ match : function() { console.log("handler 2 matched"); } }
]);
// Or you can make multiple calls to register
var query = "screen and (min-width:45em)";
enquire.register(query, function() { console.log("handler 3 matched"); });
// then later in code...
enquire.register(query, function() { console.log("handler 4 matched"); });
Multiple Queries
Often you will need more than a single media query, and for that reason enquire allows multiple queries to be registered. Calls to register
can be chained together for this purpose.
enquire
.register("screen and (max-width:50em)", function() {
console.log("handler 1 matched");
})
.register("screen and (max-width:40em)", function() {
console.log("handler 2 matched");
});
Unregister Handlers
Sometimes you may wish to unregister a specific handler, or group of handlers. For that we have the unregister
method. unregister
accepts a media query as it's first parameter, and optionally, a reference to a specific handler as it's second parameter. If the second parameter is omitted all handlers for the supplied media query are unregistered. When a handler is unregistered its destroy
callback is executed. If a handler does not have a destroy
callback then the unmatch
callback is fired instead. Once a handler is unregistered it will no longer respond to changes to a media query's state.
var query1 = "screen and (min-width: 40em)",
query2 = "screen and (min-width: 50em)",
handler1 = {
match : function() {},
destroy : function() { console.log("handler 1 destroyed"); }
},
handler2 = {
match : function() {},
unmatch : function() { console.log("handler 2 unmatched"); }
};
enquire.register(query1, handler1);
enquire.unregister(query1); // "handler 1 destroyed"
enquire.register(query2, handler2);
enquire.unregister(query2, handler2); // "handler 2 unmatched"
Mobile-First
If you're taking a mobile-first approach you will typically run into issues with incapable legacy browsers not understanding CSS3 media queries, meaning that browsers such as IE8 will be served the mobile versions of the site. Again, enquire has you covered here!
register
can accept an optional third parameter, shouldDegrade
. When this is passed as true
(it defaults to false
) it signifies to enquire that if the browser is incapable of understanding CSS3 media queries, then always consider this query a match.
enquire.register("screen and (min-width:40em)", function() {
//execute some code for large-screen devices
}, true); // note the `true`!
Because this only affects incapable browsers, modern browsers will respect the media query and behave as expected. This allows you to adopt the mobile-first paradigm, whilst still serving a desktop experience to incapable browsers.
Legacy Browsers
enquire relies on the matchMedia API. It utilises both matchMedia
and matchMedia.addListener
. Unfortunately the matchMedia API isn't universally supported in browsers. Fear not, there are polyfills available which provide various levels of support. Which polyfill you choose is dependent on how much support you need.
Basic support
You can use Paul Irish/Scott Jehl's matchMedia polyfill (both matchMedia and matchMedia.addListener) if you just wish to support CSS3 capable browsers who do not implement the matchMedia API (IE9 and Opera 12.0 are the main examples). Use this in combination with enquire's shouldDegrade
flag to offer a short-circuit for earlier browsers.
Deep support
If you wish to give full support to incapable browsers you can use David Knight's media-match polyfill. With this you do not need to use shouldDegrade
, everything will work exactly as you would expect in more capable browsers. This has been tested with enquire and works all the way back to IE6.
API
enquire.register( mediaQuery, handler )
- mediaQuery (Required)
string
- The media query you wish to respond to
- handler (Required)
function
orobject
-
A function to handle the media query being matched, or an object to handle more advanced scenarios
enquire.unregister( mediaQuery[, handler] )
- mediaQuery (Required)
string
- The media query you wish to unregister.
- handler (Optional)
function
orobject
- If supplied, only this handler will be unregistered
handler object
- deferSetup (Optional)
boolean
- Flag to determine whether setup function should be deferred until the media query is first matched. Defaults to
false
- setup (Optional)
function
-
A setup function is run just once. By default it will be called as soon as the handler is registered. If
deferSetup
is set totrue
setup will instead be called the first time the media query is matched. - match (Optional)
function
- The callback to handle the media query being matched
- unmatch (Optional)
function
- The callback to handle the media query being unmatched
Examples
Changelog
v2.1.3 (2017-03-08)
- Bug fixes
- Switch to CommonJS internally
v2.1.2 (2014-08-04)
- Fixed registration of array of functions
v2.1.1 (2014-05-07)
- Improve UMD support
v2.1.0 (2013-09-13)
- Cumulative bug fixes
- UMD support
v2.0.0 (2013-04-17)
- Tinier than ever — now only 0.8kb minified & gzipped
- Improved performance — resize events replaced with
matchMedia.addListener
- Simplified API —
listen
andfire
no longer required
If you are upgrading from v1, be aware that there are some small breaking changes.
listen
and fire
are no longer needed as of v2, and have been dropped from the API. Removing any usage of them in your code is all that is required.
Also, as enquire no longer relies on resize events you must make sure your polyfill supports matchMedia.addListener
. Details of this can be found in the legacy browser section.
v1.5.6 (2013-01-30)
- Fix bug with missing
useCapture
parameter onaddEventListener
License
Licensed under the MIT License.