r/javascript • • Aug 21 '18

Effectively supported in all browsers, JavaScript has its own form of media queries just like CSS.

https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia
465 Upvotes

114 comments sorted by

65

u/PortablePawnShop Aug 21 '18

Holy shit, I've been doing absurdly complicated workarounds for some of this

5

u/IIIIRadsIIII Aug 22 '18

What’s a good use case for this?

8

u/flaiks Aug 22 '18

hmm I would say rendering different component sets for mobile/desktop ?

5

u/13steinj Aug 22 '18

Because of a "legacy" internal app I support that deals with lots of html tables that have complex "show/hide this column" rules in CSS, that.

Much easier to just have some JS conditionally fire or fire with a swutch than deal with many confusing media queries in the same CSS document. Depending on the implementation you also save in "time it takes for the dom to load because all the CSS has to be parsed and applied".

1

u/IIIIRadsIIII Aug 22 '18

Yesh. Yeah that makes sense there.

6

u/backlashsid Aug 21 '18

Like what?

39

u/PortablePawnShop Aug 22 '18

Clumsily using SetInterval to track window.innerwidth to notify JS of the corresponding CSS media query and sloppily try to fasten JS and CSS together, and more I probably shouldn't admit lol. Self-taught, program recreationally

41

u/lamb_pudding Aug 22 '18

Ho boy. I imagine you've found the window resize event useful these days?

16

u/undercover_geek Aug 22 '18

I'm imagining PortablePawnShop now learning about the window resize event and the slow realisation of how much more time could have been saved.

6

u/roger_comstock Aug 22 '18

If you go this route, don't you end up with thousands of unnecessary resize events?

Is this advice from Mozilla still relevant? https://developer.mozilla.org/en-US/docs/Web/Events/resize#Examples

Edit: Added link

12

u/fucking_passwords Aug 22 '18

There’s definitely a way to detect when the page has stopped resizing.

Also, debounce.

7

u/DOSMasterrace Aug 22 '18

Debounce, baby.

1

u/backlashsid Aug 22 '18

And what was your requirement ? What were you trying to do?

1

u/Meph1k Aug 22 '18

what??

0

u/[deleted] Aug 22 '18

[deleted]

3

u/saitilkE Aug 22 '18

Ok, this is actually quite elegant for a workaround

3

u/hankDraperCo Aug 22 '18

This is what I've done and it works quite nicely. I knew about matchMedia but it is annoying to manage 2 sets of the same card -- one is scss and the other in js

1

u/deadlyicon Aug 22 '18

Can’t you get everything you get from css media queries, in JS, by listening to window resize events and doing a bit of math?

14

u/mort96 Aug 22 '18

Media queries can do more than just detect the screen size; it can tell if the screen is in landscape or portrait, the screen's pixel density, the color bit depth, whether the device's input method allows the user to hover over stuff (as opposed to a touch screen, where you're either not interacting with the screen at all or clicking on something), the light level from the device's light sensor, how precise the user's pointing device is (if any), whether there's a speech synthesizer, whether the page is being printed, etc.

https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries

6

u/dzkn Aug 22 '18

Yes, but now you don't have to

17

u/occsceo Aug 21 '18

wait...wait...wait. how new is this? anyone have this running in a production app?

25

u/dudeatwork Aug 22 '18

I'm using it in a production site. Browser support is good - https://caniuse.com/#feat=matchmedia - goes back to IE 10.

1

u/monkh Aug 23 '18

MDN listed android browser, chrome for android and Samsung browser as unknown.

Guess they need to update that.

4

u/nickgcattaneo Aug 22 '18

Yep - we use this to detect breakpoints that are tied to various functionality we only support in certain views.

2

u/Canowyrms Aug 22 '18

I used it a year and a half ago on a production site. The site is no longer live though. Browser support back then sufficed for that project.

2

u/kevinkace Aug 22 '18

Definitely have used them. Check https://www.guildwars2.com/path-of-fire

1

u/totomo26 Aug 22 '18

Did you work on that website? If yes, what's with those weird class names?

1

u/kevinkace Aug 22 '18

Yup, I am the creator of that site 😀

We use modular-css a lot here, the class names are prefixed with a hash of the file path so they are namespaced.

1

u/totomo26 Aug 22 '18

Oh nice. Cool website by the way.

1

u/kevinkace Aug 22 '18

Thank you!

2

u/Rhym Aug 22 '18

I use it for enabling carousels in larger screen sizes, and just falling back to showing the first "slide" in smaller screen sizes.

-14

u/esr360 Aug 21 '18

This has been around for ages. I've been using it in production for years. Read the docs sometimes guys lol, this is a standard documented API.

17

u/senocular Aug 21 '18

this is a standard documented API

Its in draft form currently: https://www.w3.org/TR/cssom-view-1/ So "standard" is a little bit of a stretch (in that its current form has not been formally standardized). The MDN page for MediaQueryList tags this as "This is an experimental technology" as well, so its understandable why this wouldn't necessarily be in widespread use.

2

u/XiMingpin91 Aug 21 '18

Explains why it isn’t more widely known

-10

u/esr360 Aug 22 '18

Guess I'm too much of a cowboy. No doubt about it that you're right here. But the fact remains that I've been using it for literally years with no issues. It's hardly new.

0

u/[deleted] Aug 22 '18

[deleted]

-4

u/esr360 Aug 22 '18

The same is true of any API. You can't guarantee that any API will never become deprecated or obsolete. You just have to ask yourself if it's likely. To me it seems very likely that this API will work as-is for the next few years at least, and if I had any genuine concerns I would use a polyfill.

0

u/[deleted] Aug 22 '18

[deleted]

1

u/AndrewGreenh Aug 22 '18

This is not part of ECMAScript, this belongs to browser APIs.

-2

u/esr360 Aug 22 '18

Question whatever you want, but the fact is I've been benefiting from this API for years already and will continue to benefit from it for years to come. I don't know what else to tell you.

0

u/[deleted] Aug 22 '18

[deleted]

2

u/esr360 Aug 22 '18

And I understand that. But when this has been working as expected for years already and has full support on caniuse, I'm suggesting that in this particular case the risk is very much worth it, because it's an extremely useful API.

-4

u/occsceo Aug 21 '18

you aren't wrong (rtfm). :doh:

32

u/MayorMonty Aug 21 '18 edited Aug 22 '18

Huh, TIL. And to think I've been using resize events for all these years now...

Edit: scroll events are different

2

u/lackbotone Aug 22 '18

How are scroll events related to this? Just curious, I genuinely don't understand.

3

u/MayorMonty Aug 22 '18

Oh shoot, I meant to say resize events. I need to proofread more ;-)

20

u/GottfriedEulerNewton Aug 22 '18

Nah, just catch it at runtime

1

u/deadlyicon Aug 22 '18

Scroll events? Do you mean window resize events?

1

u/BONUSBOX _=O=>_();_() Aug 22 '18

matchMedia can still be combined with resize if you want components and functionality to adapt to a changing screen size or orientation. def simplifies things in any case...

45

u/SalemBeats Aug 22 '18

This is one of those things that, when someone brings it up, I think "yeah, I was aware of that". But it's still so obscure that it never comes to mind in the few situations it would actually be useful.

1

u/laichejl Aug 25 '18

It's extremely useful if working with CSS-in-JS.

3

u/SalemBeats Aug 25 '18

I can't tell whether you're referring to a specific library, or just the general tendency to prefer styling elements directly via Javascript and avoiding static external stylesheets.

1

u/laichejl Aug 25 '18

More-so the rise of libs over the past couple years that let you style with JavaScript. At my place of work, we have been using emotion quite a bit, and have created some responsive helper components that use matchMedia to render certain components at corresponding breakpoints. The nice thing is, we declare all our breakpoints and stuff just right there in our JS , so we are able to both style our components based on breakpoint (standard media query css) and also render them based on breakpoint, all referencing the same variables!

26

u/elysgaard Aug 22 '18

Been using this in our React/Redux app for a while now and it's awesome being able to programatically change how components are rendered based on screen sizes.

const MyComponent = ({isMobile}) => {
    if (isMobile) {
        return <div>You're on mobile size!</div>
    }
    return <div>Do the normal stuff</div>
};
export default compose(
    responsiveState(['isMobile'])
)(MyComponent);    

We listen to the changes with

import {Observable} from 'rxjs';
const mediaQueries = window.matchMedia('...');
const mediaQueryEvent = Observable.create((observer) => {
    mediaQueries.addListener(e => observer.next(e));
}).share();

Then the responsiveState high-order-component has something similar to this

componentDidMount() {
    this.mediaSubscription = mediaQueryEvent.subscribe(...);
}

This means than we only have one listener to the media queries no matter how many components are using it at the same time.

1

u/Console-DOT-N00b Aug 28 '18

Or better yet...just slightly change things.... to make people think they are insane...

11

u/sMarvOnReddit Aug 21 '18

the browser compatibility is very similar to CSS media queries

10

u/DOG-ZILLA Aug 21 '18

I had been using a utility in Modernizr for years for this kinda thing before discovering match media literally the other day.

Felt great to remove another small dependency from the codebase.

14

u/dombrant Aug 22 '18

Few things match the high of removing unnecessary code for me.

5

u/el_rezzo Aug 22 '18

Mind blown.

5

u/AwesomeInPerson Aug 22 '18

It is well supported and works flawlessly, so that's nice.

But in case you build a JS-powered web application, be aware that window.matchMedia (or window in general) is not available in Node, so if you ever try to get into server-side-rendering, relying too much on it can really bite you!
Learned that the hard way, lots of refactoring necessary...

4

u/Karokendo Aug 22 '18

wtf it exists

2

u/[deleted] Aug 22 '18

Removing features on small screens?

2

u/[deleted] Aug 22 '18

How did I miss this?

2

u/atubofsoup Aug 24 '18

Just learned about this in one of my last projects and wrote a react HoC that takes a breakpoints object, adds listeners, re-renders when breakpoints change, and passes the active breakpoints to the wrapped component:

function responsive(breakpoints) {
  return Wrapped => class Responsive extends React.Component {
    state = Object.keys(breakpoints).reduce((breakpointValues, key) => {
      breakpointValues[key] = false;
      return breakpointValues;
    }, {});

    constructor(props) {
      super(props);
      this._queryList = Object.entries(breakpoints).map(([key, breakpoint]) => {
        const query = window.matchMedia(breakpoint);
        query._key = key;
        query.addListener(this.computeBreakpointMatches);
        return query;
      });
      this.computeBreakpointMatches();
    }

    computeBreakpointMatches = () => {
      const { state } = this;
      const newState = {};
      let changed = false;
      for(let query of this._queryList) {
        const key = query._key;
        const { matches } = query;
        if(state[key] !== matches) {
          changed = true;
          newState[key] = matches; 
        }
      }
      if(changed) this.setState(newState);
    };

    componentWillUnmount() {
      for(let query of this._queryList) {
        query.removeListener(this.computeBreakpointMatches);
      }
    }

    render() {
      return (<Wrapped {...this.props} breakpoints={this.state}/>);
    }
  };
}

@responsive({
  large: 'screen and (min-width: 1100px)',
  medium: 'screen and (min-width: 700px) and (max-width: 1099px)',
  small: 'screen and (max-width: 699px)',
})
class MyComponent extends Component {
  render() {
    const { breakpoints } = this.props;
    if(breakpoints.small) return (<div>Small</div>);
    if(breakpoints.medium) return (<div>Medium</div>);
    return (<div>Large</div>);
  }
}

1

u/klebsiella_pneumonae Aug 22 '18

Pretty useful when using jsx or so!

Always use css as the primary solution though!

1

u/cbung Aug 22 '18

Very neat, last thing I used a while back for this was jRespond (https://github.com/ten1seven/jRespond)

1

u/kenok Aug 22 '18

Chrome on Android is ? then MediaQueryList has 9. eh?!?

1

u/rendermouse Aug 22 '18

You can also use CSS properties and read them from script. For example, set the z-index of the <body> to 1 for desktop, 2 for tablet, 3 for mobile using media queries. Then if your JS needs to know the current media query, just check the z-index style prop of the body tag. Yes, it's also a workaround...I was not aware of this matchMedia() method.

1

u/[deleted] Aug 22 '18

I don't think there's much CSS can that JS can't. It's more the opposite, CSS can't do a lot of stuff JS can.

1

u/eye_gargle Aug 22 '18

Wish I would have known this sooner...

1

u/GeneralYouri Aug 23 '18

For the longest time you've been able to edit CSS styles from within JS. So when CSS introduces media queries, JS needs a way to work with those to continue being able to edit CSS styles properly. As far as I know, that's about all this is.

Also, as with other CSS styling, you'll want to prefer defining those directly via CSS. Handling them via the JS DOM is for those use cases where CSS itself isn't enough - such as when you need variables depending on runtime state.

-2

u/[deleted] Aug 22 '18 edited Aug 22 '18

Can anyone answer why this would be better than using window.innerWidth?

Because I've been using window.innerWidth to check if I should render a mobile version of React component and it did its job without problem (that I know of).

Edit: I don't know what the problem is of these people downvoting me? I'm asking a normal question. Your downvotes are just rude.

4

u/Treolioe Aug 22 '18

They have little in common. You might be able to solve your problem with innerWidth. But some problems can be based on that users can resize the window or change the viewport orientation. This is generally handled with media queries in css, but you might want to handle this dynamically in JS as well.

-2

u/jutsu9 Aug 22 '18

How is it different from window.innerWidth?

https://developer.mozilla.org/fr/docs/Web/API/Window/innerWidth

6

u/Treolioe Aug 22 '18

Well you can compare their docs and see for yourself

-6

u/jutsu9 Aug 22 '18

Your response is of no value. I have seen the difference of implementation. They can both accomplish the same thing and I want to know if you can do something different with those JavaScript media queries. Only fools don't ask questions. But bigger fools sometime respond.

6

u/Treolioe Aug 22 '18

As the spec reveals you can dynamically react to media queries similar to what you can do in css. These two props literally have nothing in common. You might be able to solve the same problem with either however

-9

u/amejin Aug 22 '18

I mean.. Neat.. I guess? For those weird times you want a button to launch a modal instead of a menu?

Im pretty sure this is Microsoft's doing as a means to provide app functionality for multiple devices that run on JS as an engine instead of C#

5

u/[deleted] Aug 22 '18

Responsive design?

-3

u/amejin Aug 22 '18 edited Aug 22 '18

Im not sure your script should behave differently than intended just because the window size changes.

As I learned it, layout, in majority, should be handled by css. Script is for data and state, and fringe cases where css cant meet the need or is not optimal or performant.

Maybe its my limited vision, but Im not sure this would be the thing I run to when thinking about how a page will look and feel on different devices. A menu button should continue to display a menu. The menu layout and design can be controlled with media queries already and the script should have nothing to do with it (in my very humble opinion).

Now - we all know in programming there are always multiple avenues to solve a problem. If this solves yours, feel free to use it. :)

Edit: punctuation, a word

6

u/[deleted] Aug 22 '18

You might hide things in a menu on a small screen or add cascading chevrons.

2

u/amejin Aug 22 '18

All controllable through css. Unless you're saying dynamic elements added would be different. I that case, you kinda have an argument... But to me that is indicative of odd design and user experience/expectations. However, sometimes you just didn't have a choice... :\

2

u/Treolioe Aug 22 '18

There’s a consensus in the UX community that less information should be displayed on smaller devices. You seem to only consider the separation of concerns ”codewise” in your statement but this thinking is no longer the norm on the web. I would probably mainly use this to prevent rendering of features that are not feasible for the viewport.

1

u/[deleted] Aug 22 '18

There is a trend of doing styling in JS instead of CSS.

1

u/dzkn Aug 22 '18

This is absolutely golden for React/Vue etc. You can render one component on desktop and one on mobile for example.

-34

u/StoneCypher Aug 21 '18

um, this actually is css, not javascript

8

u/ejfrodo Aug 22 '18

um, this is actually javascript, not css. unleses this example from the article somehow works in a .css file

if (window.matchMedia("(min-width: 400px)").matches) {
  console.log('Larger than 399px');
} else {
  console.log('Less than 400px');
}

2

u/[deleted] Aug 22 '18 edited Aug 23 '18

i agree with you

although i thought the window object was part of the dom

1

u/DilatedTeachers Aug 22 '18

Did you read the article? And try it in a js script? It's really fun to use!

0

u/[deleted] Aug 21 '18

[deleted]

-33

u/StoneCypher Aug 21 '18

Seems to work as expected

<html><head><style type="text/css">
  @media (min-width: 400px) { body { background-color: green; } }
  @media (max-width: 399px) { body { background-color: red; } }
</style></head><body>
  <p>hiya</p>
</body></html>

14

u/rabidhamster Aug 22 '18

Which is not even close to what was asked of you. The only thing that matches is the "min-width: 400px". The rest of the syntax is entirely different.

Try this in your CSS:

if (window.matchMedia("(min-width: 400px)").matches) {
  console.log('Larger than 399px');
} else {
  console.log('Less than 400px');
}

See how media queries based in JavaScript might have some utility and use-cases beyond what CSS can provide?

-16

u/StoneCypher Aug 22 '18

I think that you are confusing "I can use css from Javascript" with "this must not be css"

11

u/rabidhamster Aug 22 '18

Except that this isn't necessarily about styling the page. You can run different conditional branching, loops, JSON queries to the server, etc. based on the results of the window width. Just because you personally would use it to make style changes doesn't make it inherently CSS. By that logic, Javascript is just FORTRAN, because they both have "if" statements.

-12

u/StoneCypher Aug 22 '18

OK, try putting some Fortran in there and see what happens

13

u/rabidhamster Aug 22 '18

Uh, it won't work, which is precisely my point: Media queries in JavaScript are not CSS, just as JavaScript is not FORTRAN despite sharing a few basic similarities. I'm not even sure what you're arguing at this point.

-13

u/StoneCypher Aug 22 '18

Except, of course, media queries as defined in css work here

If you are not sure what my point is, it seems weird that you keep saying that I'm wrong

Have a nice day

8

u/crobison Aug 22 '18

Don’t be obtuse.

-8

u/StoneCypher Aug 22 '18

Insults won't change my opinion. Neither will mass downvoting.

If the code is defined by the css spec, it's css.

9

u/crobison Aug 22 '18

You are an idiot.

18

u/bearcherian Aug 21 '18

You should probably actually read the linked article and look at its examples. If you still think you're right, I sincerely hope you are not a professional web developer.

-16

u/StoneCypher Aug 22 '18

That's nice

-8

u/twinkiac Aug 22 '18

9

u/tsteuwer Aug 22 '18

Bad bot

-2

u/B0tRank Aug 22 '18

Thank you, tsteuwer, for voting on twinkiac.

This bot wants to find the best and worst bots on Reddit. You can view results here.


Even if I don't reply to your comment, I'm still listening for votes. Check the webpage to see if your vote registered!

0

u/[deleted] Aug 22 '18

[deleted]

-2

u/StoneCypher Aug 22 '18

No. You said to try it in css, so I did, and it works, because it is css.

Afterwards, the reddit rage squad came to tell me that it hopes I'm not a programmer, because this js interface to css is useful in js.

0

u/[deleted] Aug 22 '18

[deleted]

0

u/StoneCypher Aug 22 '18

CSS in a style block is the same as CSS in an external file, other than timing issues

For clarity, I do understand that you are talking about the call interface, and that I am talking about what it calls

0

u/[deleted] Aug 22 '18

[deleted]

0

u/StoneCypher Aug 22 '18

For clarity, I do understand that you are talking about the call interface, and that I am talking about what it calls

I’m not convinced we’re talking about the same thing here.

good thing it's been such a friendly discussion this entire time, huh

.

What may be tripping you up

i'm really not interested in watching you repeat yourself in progressively more condescending tones.

.

I’m not sure what your angle is

i know.

.

just picking this incorrect argument as your reddit hill to die on.

i think you've wildly overestimated the importance of you yelling at people on reddit.

i think i'll stop responding. i seem to be just encouraging you to escalate

0

u/[deleted] Aug 22 '18

[deleted]

8

u/xraminator Aug 22 '18 edited Aug 22 '18

Media queries can be used for much more than window width.

With regard to it being an event, it is. window.matchMedia returns a MediaQueryList object that includes addListener and removeListener methods. The listeners are fired when the matches state of the media query changes. As your listener is only invoked when the relevant threshold is crossed this technique is much more efficient than using a resize event which will fire constantly during resizing.

https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/addListener#Examples

EDIT: Typo.

4

u/_www_ Aug 22 '18

My eyes are bleeding when I see typos in function names.

2

u/xraminator Aug 22 '18

It's not a function :-P

0

u/[deleted] Aug 22 '18

[deleted]

3

u/jonny_wonny Aug 22 '18

Why is that hard to believe?

0

u/[deleted] Aug 23 '18

[deleted]

0

u/jonny_wonny Aug 23 '18

You obviously haven't been paying attention. You realize that you failing to understand that most people don't thoroughly read all documentation reflects just as poorly on you as people's ignorance of specific aspects of the specs reflects on them?

0

u/[deleted] Aug 23 '18

[deleted]

0

u/jonny_wonny Aug 23 '18

To people and how they behave. Why do you think the phrase “RTFM” entered into common usage? Because generally speaking, people don’t read the fucking manual.