Responsive javascript helper – configurable breakpoints with matchmedia

June 5, 2019

This is my basic, but configurable, responsive device width service, that I use when implementing UI’s. It’s really just a wrapper for the native window​.match​Media() functionality. But I’ve built it so it matches (or the terminology/functionality matches) my responsive SASS mixins that you can find here.

The class takes an array of objects (breakpoints), and with these it’ll return a boolean value for the methods to(breakpointTarget), from(breakpointTarget), and until(breakpointTarget).

The code

class Respond {
    /**
     * Constructor
     * @param {array} breakpoints Array of breakpoint objects {name: String, value: Int}
     */
    constructor(breakpoints) {
        this.breakpoints = [...breakpoints].sort(function(a, b){
            return a.value - b.value;
        });
    }

    /**
     * Gets a breakpoint object
     * @param {string} breakpointName The breakpoint name
    * @return {Object|False} The breakpoint object, or false if none was found
     */
    getBreakpoint (breakpointName) {
        const breakpoint = this.breakpoints.filter((currentBreakpoint) => {
            return currentBreakpoint.name === breakpointName;
        });

        if (breakpoint.length) {
            return breakpoint[0];
        }

        return false;
    }

    /**
     * Gets the next (larger) breakpoint object based on a current one
     * @param {Object} breakpoint Current breakpoint object
     * @return {Object|False} The next breakpoint, or false if none was found
     */
    getNextBreakpoint (breakpoint) {
        const currentIndex = this.breakpoints.indexOf(breakpoint);

        if (currentIndex === -1) {
            return false;
        }

        if (this.breakpoints.length < currentIndex + 1) {
            return false;
        }

        return this.breakpoints[currentIndex + 1];
    }

    /**
     * Will return true until device width matches breakpoint
     * @param {string} breakpointName The name of the breakpoint
     * @return {bool} True if matches
     */
    until (breakpointName) {
        const breakpoint = this.getBreakpoint(breakpointName);

        if (!breakpoint) {
            return false;
        }

        if (window.matchMedia('(max-width: ' + breakpoint.value + 'px)').matches) {
            return true;
        }

        return false;
    }

    /**
     * Will return true when device width matches, and is larger, than breakpint
     * @param {string} breakpointName The name of the breakpoint
     * @return {bool} True if matches
     */
    from (breakpointName) {
        const breakpoint = this.getBreakpoint(breakpointName);

        if (!breakpoint) {
            return false;
        }

        if (window.matchMedia('(min-width: ' + breakpoint.value + 'px)').matches) {
            return true;
        }

        return false;
    }

    /**
     * Will return true when the device width is at breakpoint but smaller than the next breakpoint
     * @param {string} breakpointName The name of the breakpoint
     * @return {bool} True if matches
     */
    to (breakpointName) {
        const breakpoint = this.getBreakpoint(breakpointName);

        if (!breakpoint) {
            return false;
        }

        const breakpointTo = this.getNextBreakpoint(breakpoint);

        if (!breakpointTo) {
            return false;
        }

        if (window.matchMedia('(min-width: ' + breakpoint.value + 'px) and (max-width: ' + (breakpointTo.value - 1) + 'px)').matches) {
            return true;
        }

        return false;
    }

};

Breakpoints and setup

First thing’s first, you’ll need to specify/configure your breakpoints. A breakpoint is defined as some kind of width value in pixels, it’s generally mapped to normal/popular device widths. I usually go with something like this.

You can add more, or remove some, that’s up to you.

const BREAKPOINTS = [
    {name: "xs", value: 480},
    {name: "s", value: 624},
    {name: "sm", value: 768},
    {name: "m", value: 896},
    {name: "md", value: 1024},
    {name: "l", value: 1195},
    {name: "lg", value: 1366}
];

const respondService = new Respond(BREAKPOINTS);

Respond from

The Respond.from(targetBreakpoint) method is used when you want something to happen from a specific device width. This is probably the method I use the most, because it’s most compatible with a mobile-first approach in development (i.e. you start with the mobile UI, and add properties on top of it, i.e. adding properties from a width).

Respond to

The Respond.to(targetBreakpoint) method only responds to specific sizes, the size that you sent in as an argument all the way until the next breakpoint. I.e. Respond.to('m') would only return true when the device width matches 896px until 1023px.

I generally use this if I need to hack something, or quickly add something to a very specific size. I generally never use this function, although it’s good to have when you’re in a pinch.

Respond until

The last method is the Respond.until(targetBreakpoint), which is the same as Respond.from, but inverse.

Conclusion

In essence, this class will help you streamline how you handle breakpoints in your code, turning on and off different features for different devices/displays.

Tags