---
title: "Using :scope Is Pretty Cool"
description: "I recently found out about the :scope pseudo-class. Here's a quick writeup on the things you can do with it!"
date: 2021-10-23
url: https://krismuniz.com/posts/scope-pseudo
---

# Using :scope Is Pretty Cool

While trying to solve a problem at work, a coleague and I found out about the [`:scope`](https://developer.mozilla.org/en-US/docs/Web/CSS/:scope) pseudo-class. In a nutshell, it allows you to refer to the current element's scope in a CSS selector.

When writing a stylesheet, `:scope` is the same thing as `:root`, because there is no way to write scoped stylesheets. Scoped stylesheets was a feature that was proposed some time ago, and some browsers experimented with, but have since been [removed from browsers](https://www.chromestatus.com/feature/5374137958662144) and [removed from the spec](https://github.com/whatwg/html/issues/552).

Even though we cannot do anything useful with `:scope` in our stylesheets, we can use the `:scope` pseudo-class with the DOM API!

When using DOM API methods that expect CSS selectors, you can use `:scope` to refer to the element from which you are calling it!

```javascript
element.matches(":scope") === true; // element is itself, so true!
```

## Things You Can Do with `:scope`

### Selecting Direct Descendants

`:scope` is particularly useful when selecting direct descendants of the current element that match some criteria!

```javascript
element.querySelector(":scope > input#username");
```

An alternative, would be to get all the children of `element` and match them against a selector.

```javascript
Array.from(element.children).find((child) => child.matches("input#username"));
```

Another approach would be to refer to the current element using an ID or class:

```javascript
element.setAttribute("id", "myid");
element.querySelector("#myid > input#username");
```

But this would be a bit harder to maintain, as the selector and whatever attribute we are using to match the current element would need to be in sync.

### Assertions That Involve Hierarchy

Another use case for `:scope` is to elegantly assert whether the current element matches a particular selector without needing to use an ID or class to refer to the current element.

```javascript
// element is a direct child of `div#wepa`
element.matches("div#wepa > :scope"); // => `boolean`

// element is descendant of #content
element.matches("#content :scope"); // => `boolean`

// element is the next sibling of a `label`
element.matches("label + :scope"); // => `boolean`

// the next sibling of the element is a `label`
element.matches(":scope + label"); // => `boolean`

// element is a descendant of a `form` element,
// and has a `textarea` as a child
element.matches("form :scope > textarea"); // => `boolean`
```

Again, there are other ways to do this, but this way is probably more expressive, concise, and easier to maintain.

## Limitations

A noteworthy limitation that I've noticed, however, is that using `Element.querySelector()` or `Element.querySelectorAll()` to match siblings of the current element does not work.

This likely due to the fact that `querySelector` and `querySelectorAll` only consider the descendants of the current element, so it makes sense.

```javascript
// try to get all siblings of the current element
element.querySelectorAll(":scope ~ *"); // => Empty NodeList

// try to get the next sibling of the current element
element.querySelectorAll(":scope + *"); // => Empty Nodelist
```

In that case we still have to use the traditional methods, like getting all the children of the parent element, matching them against a selector, and filtering out our "target element".

Likely for the same reason, `:scope` cannot be used to match the current element itself. Which would not be useful, but interesting to know about.

```javascript
element.querySelector(":scope"); // => null
```

## Browser Support

I expected `:scope` to have limited browser support. Surprisingly, at the time of this writing `:scope` has [very good browser support](https://caniuse.com/mdn-css_selectors_scope).

The TL;DR is if you don't have to support Internet Explorer, you can use `:scope`!

## What's Next?

I hope you enjoyed this quick writeup. I'm going to write a couple more about other interesting Web features that I've found myself using recently.
