CSS @media rule in JavaScript
Tags: javascript, css, webdev
Published: 2020-06-11 19:39:43

JavaScript is in this very special place when it comes to web technologies. It either provides functionalities that can't be found anywhere else or can easily replace other web languages - HTML and CSS that is.

While most JS developers probably know of the DOM API and all the UI libraries and frameworks built on top of it, the knowledge of  **"CSS API"**  (it's not technically called that way, but you get the point), is less common.

I've already covered the API you can use to create your CSS stylesheets right from JS in  [my previous article](https://areknawo.com/messing-with-css-through-its-javascript-api/). Instead, today I'd like to focus on something more advanced - on how to control your  `@media`  CSS rules - you've guessed it - in JS!

# CSS API recap

Let's start with a very quick recap of the previous article.

You can access a stylesheet in 2 ways - either through the  `sheet`  property of a  `<style>`  tag DOM element or as one of the  `document.styleSheets`  indexed collection's items. In both cases, the result is an object implementing the  [`CSSStyleSheet`](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet) interface that then gives you further access to control methods like  `insertRule()`  and  `removeRule()`, as well as properties like  `cssRules`.

```javascript
const style = document.createElement("style");
document.head.appendChild(style);

const styleSheet = style.sheet;
const ruleIndex = styleSheet.insertRule(".example { background-color: red }");
const rule = styleSheet.cssRules[ruleIndex];
```

The  `insertRule()`  method returns the index at which the new CSS rule was inserted. This can then be used to access the rule object implementing the [`CSSRule`](https://developer.mozilla.org/en-US/docs/Web/API/CSSRule) interface. And such an object, as expected, has some properties of its own - mainly used to configure and access the rule's data.

# CSSRule

Now, here's where we slow down a little bit. That's because the  `CSSRule`  and its derivatives must be well-understood, to be able to create more complex  **JS-based stylesheets**.

On its own - although you'll never see it that way -  `CSSRule`  has only a few properties. The most important ones are probably the  `cssText`  - holding your rule's textual CSS representation and the  `type`  - a constant value indicating the type of the given  `CSSRule`.

```javascript
// ...
rule.cssText; // ".example { background-color: red; }"
```

There are multiple types and thus derivatives of the  `CSSRule`  interface. The most common one -  [`CSSStyleRule`](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleRule)  is responsible for rules like the one you see above. In addition to standard  `CSSRule`  properties, it also has some more interesting ones like  `selectorText`  - textual representation of the rule's CSS selector, and  `style`  - a  `CSSStyleDeclaration`  object, just like DOM element's inline styles you might be accustomed to.

```
// ...
rule.selectorText; // ".example"
rule.style.backgroundColor; // "red"
rule.style.backgroundColor = "green";
rule.style.backgroundColor; // "green"
```

As a nice bonus - did you know that you can change the  `style`  of your rule, altering it and all the elements it's applied to in real-time!?

# CSSMediaRule

But all the different  `CSSRule`s are not what you came here for - no. You've come for the CSS  `@media`  rule. And, as one might expect, it also has its reflection on the JavaScript side - this time in the form of the  [`CSSMediaRule`](https://developer.mozilla.org/en-US/docs/Web/API/CSSMediaRule).

`CSSMediaRule`  is that much interesting because of its deeper inheritance. Unlike simple  `CSSStyleRule`  that's a direct child of the  `CSSRule`,  `CSSMediaRule`  additionally has  [`CSSGroupingRule`](https://developer.mozilla.org/en-US/docs/Web/API/CSSGroupingRule)  and  [`CSSConditionRule`](https://developer.mozilla.org/en-US/docs/Web/API/CSSConditionRule)  (in the given order) as its parents.

This says a lot about the rule. The  `CSSGroupingRule`  is meant for the rules that contain nested rules within them, while  `CSSConditionRule`  means that they're applied only when a certain condition is met. Remember CSS syntax for a  `@media`  rule?

```CSS
@media screen and (min-width: 900px) {
  .example {
    background-color: blue;
  }
}
```

Now, both of the  `CSSMediaRule`  parents add important properties and methods to it. Going from all the way up (directly below  `CSSRule`  itself) the  `CSSGroupingRule`  adds methods like  `insertRule()`  and  `deleteRule()`  as well as  `cssRules`  property to the party. Sounds familiar? That's because these are similar functionalities to what we saw earlier, all the very beginning with the  `CSSStyleSheet`  interface.

```javascript
// ...
const mediaRuleText = `@media screen and (min-width: 900px) {
  .example {
    background-color: blue;
  }
}`;
const mediaRuleIndex = styleSheet.insertRule(ruleText);
const mediaRule = styleSheet.cssRules[mediaRuleIndex];

mediaRule.cssRules[0].selectorText; // ".example"
mediaRule.cssRules[0].style.backgroundColor; // "blue"
```

In our case, there's only 1 rule grouped by the  `CSSGroupingRule`  - a simple  `CSSStyleRule`, which means that we've come a full circle.

Next up, we've got the  `CSSConditionRule`  which brings with it the  `conditionText`  property. This guy allows us to access the textual representation of the CSS condition. In our case it's:

```javascript
mediaRule.conditionText; // "screen and (min-width: 900px)"
```

The  `CSSMediaRule`  also adds a property of its own -  `media`  - that's equal to an object implementing  `MediaList`  interface. Basically, a bit more advanced version of  `conditionText`. It's not really important for anything so if you're interested, just check out the MDN docs.

# The other way around

So, that pretty much wraps it up for the  `CSSMediaRule`  and related APIs. There are quite a few variations of  `CSSRule`  like this one, which when used together can lead to pretty impressive results. Dynamic, manageable CSS-in-JS libraries like my own  [Prototope](https://github.com/Isotope-js/isotope/tree/master/packages/prototope)  with complex real-time updates are definitely possible.

But you might also say that these things are best for the CSS to deal with. And you'd be absolutely right - that's what CSS was designed for. But if so, maybe you'd be interested in something different?

What if I told you that there's a way to evaluate  **media queries**  right in JS? To know when e.g. a window has the desired width or height? Well, that's surely possible and all thanks to  [`matchMedia()`](https://developer.mozilla.org/en-US/docs/Web/API/CSSConditionRule)

# matchMedia

So,  `matchMedia()`  is a method accessible directly on the  `window`  object (globally), that allows you to parse given media query and react to the changes in its activity.

```javascript
const mediaQuery = matchMedia("screen and (min-width: 900px)");
```

`matchMedia()`  returns what's called a  `MediaQueryList`  object. This guy provides you with everything you'd want when working with media queries. You've got the most important  `matches`  property to check whether the media query matches the current website's state, the  `media`  property to get back the provided media query string, and two  `addListener()`  and  `removeListener()`  methods to listen for the media query state changes.

```javascript
mediaQuery.addListener(() => {
	mediaQuery.matches; // true or false
});
mediaQuery.media; // "screen and (min-width: 900px)"
```

Now, you cannot argue with the usefulness of this feature. Being able to check whether the certain media query applies is extremely helpful when dealing with any kind of JS-driven UI - take Masonry Grid for example. The  `matchMedia()`  way is much  **faster**  than any other similar solution (especially the one with constant  `resize`  even monitoring). And have I already said that it has great cross-browser support with up (or rather down) to IE 10!

# Conclusion

With the CSS API and  `matchMedia()`  I think I've shown you an impressive side of JavaScript capabilities. I hope you've learned something new and will now be able to create all sorts of JS wonders - from simple JS-driven UI layouts to full-blown CSS-in-JS libraries.

For more web development guides and tutorials, follow me on  [Twitter](https://twitter.com/areknawo),  [Facebook](https://facebook.com/areknawoblog), or right here on Dev.to. I've also got a  [YouTube channel](https://www.youtube.com/channel/UCUw6bg379ONlG1E_oxhwi-A/)  (not very active recently, but I'm working on it), which you might want to check out and subscribe to. Thanks for reading this piece and I wish you happy coding!

<a href="https://www.buymeacoffee.com/areknawo" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" style="height: 25px !important;width: 100px !important;" ></a>