Responsive JavaScript With EnquireJS
For a dynamic client-side application, using CSS alone to show and hide components for different screen sizes isn't enough. Even when hidden, components may be listening for application state and re-rendering themselves or taking up resources for no visible effect, giving the perception of a laggy app. They may be making unnecessary AJAX requests to the server.
Screen size as application state
The client environment needs to be aware of the available screen size at a more fundamental level. In other words, screen size should be part of the application state. This way, the client can make decisions on whether or not to render certain components for a given screen size.
EnquireJS
It would be great to have a way to make media queries from the client code, and
be notified when the screen size changes.
EnquireJS is a lightweight library that
makes it easy to do this, using the
window.matchMedia
API and a simple registry/callback pattern that fires for different stages of
matching a media query. We can use enquirejs callbacks to set the value of a
screen-size
variable, which our app can use when deciding whether or not to render a
component. In the following ClojureScript example, model
is an atom that is responsible for the
application state:
1 2 3 4 5 6 7 8 9 | (defn update-size [model new-size] (fn [] (swap! model assoc :screen-size new-size))) (defn watch-screen-size [model] (-> js/enquire (.register "screen and (min-width: 0px) and (max-width: 520px)" (update-size model "xs")) (.register "screen and (min-width: 521px) and (max-width: 768px)" (update-size model "sm")) (.register "screen and (min-width: 769px) and (max-width: 1024px)" (update-size model "md")) (.register "screen and (min-width: 1025px)" (update-size model "lg")))) |
Now, by running watch-screen-size
at startup, we can check the value of the
screen-size
variable before rendering a component, making an AJAX call, or
even deciding which URL to call. The server API can be made smart about having
different endpoints with appropriate payloads based on the screen size.
Here's an example
The above code snippet was taken from an app that needs to display an SVG county map of the United States using D3 (look here for more on that). The county map contains over 3000 SVG elements, so it rendered and updated painfully slowly on devices such as tablets or phones. We used CSS to scale the SVG to fit the screen better, but we had to find a quicker, more efficient way to display the map details.
Having a handle on the screen-size
variable allowed us to do that. Instead of
rendering the county map, we now render a state map for smaller screens. This
brought the number of data points down by orders of magnitude, making the
render much more snappy.
The data to be displayed on the map also changes based on a selected month. On larger screens, this month selector appears as a slider, but on smaller screens we render a simple select list instead. We could render them both all the time and selectively shown one over the other, but knowing the screen size allows us to not have to do that.
Conclusion
Media queries with CSS can only take you so far. For a static site that doesn't make a lot of AJAX calls, this may be all you need. But with a rich client-side application, knowing the size of your user's screen is as valuable as any other user input that your app can react to.