What’s the highest z-index on a page (and other CSS property problems)
Tags: cssom, css, javascript
Published: 2020-01-24 20:14:46

Ever been the new person on an old project where you were trying to not make things substantially worse? Maybe you need to position an element, but you don’t know what `z-index` value is safe to use. You don’t want to layer something to `z-index: 9999` if `z-index: 10` will suffice… but how do you find out what the highest `z-index` is?

Or, for that matter, how do you find everything that’s using `opacity`?

Sure, you’ve got a modern browser with great debugging tools. But they don’t make it easy for you to search by CSS properties. So I wrote a JavaScript function that can do that.

## Remember the CSSOM?

[I’ve written about the CSSOM in the past](https://blog.frankmtaylor.com/?s=cssom) (CSS Object Model) and I’ll say again that it’s an under-appreciated and under-utilized feature of the web browser environment.

If you need to do something so peculiar as searching for selectors by CSS properties, this is the thing the CSSOM is good for.

## It starts with the `stylesheets`

Let’s start by creating a new function that’ll take two arguments; `queryPropName` and `queryPropValue`. We’ll access some `styleSheets` and loop through them since they’re enumerable, but not an array.

```JavaScript
function queryCSSByProperty(queryPropName, queryPropValue) {
  const styleSheets= document.styleSheets; 
  const properties = new Map(); 
  
  Object.values(styleSheets).forEach(styleSheet => {

  });
}
```

### Next, try to loop through the rules

We’ll need to wrap the interior of our loop in a try-catch because the browser may throw some restrictions at us trying to access some of the `cssRules`. Then we’ll loop through the rules of that `styleSheet`

```JavaScript
Object.values(styleSheets).forEach(styleSheet => {
      try {
        const rules = styleSheet.cssRules; 

        Object.values(rules).forEach(rule => {
        });
      } catch (err) {
        console.log(err);
}
```

### Then, look for a style on the rule

Now that we’re inside of a rule inside of a `stylesheet`, we’re where we need to be. So now what we want to do is see if the CSS property we want is there.

First, let’s pull out some things that will be useful, like `style`, which will be an object that contains every possible CSS property. Let’s also grab some `selectorText`, which is strictly the CSS selector part of this ruleset (e.g. `.someSelector > h1 > .foo` )

Once we get that out, let’s be safe and check to be sure style is truthy. Not every `cssRule` will have a style property.

```JavaScript
 Object.values(rules).forEach(rule => {
          const {style, selectorText} = rule;
          
          if (style) {

          }
});

```

#### And if there’s a style, check if it’s the one you want

This is where some logic could probably be cleaned up.

First, let’s use the `CSSOM`, rather than `hasOwnProperty` or for–in to find out if there’s a value for our property. `getPropertyValue` turns out to be the perfect way to get the value off of a specific CSS property in our style.

Next, because we want an option to see search by value of a property, we have `hasPropValMatch` to tell us exactly that. And for right now, rather than `===`, let’s use `indexOf` because there’s always the chance that there’s an `!important` floating here. It’d be better to search within the string rather than assume it’s a pure equality. We’ll probably need more robust matching in the future.

Finally, we check if our `queryPropValue` is there (because, after all, maybe you’re just interested in `z-index`, not `z-index: 10` ). If it is, let’s set a value to our map.

We’ll make the key of our item the CSS selector, which is the `selectorText`. Then we’ll make the value a mashup of the property name and the property value. It won’t be a duplicate of what’s written in the actual stylesheet — just what’s important for search purposes. 

```JavaScript
if (style) {
        const propertyValue = style.getPropertyValue(queryPropName);
        const hasPropValMatch = queryPropValue && propertyValue.indexOf(queryPropValue) !== -1;

        if (queryPropValue && hasPropValMatch) {
          properties.set(selectorText, `${queryPropName}:${propertyValue}`);
        }

        if (!queryPropValue && propertyValue) properties.set(selectorText, `${queryPropName}:${propertyValue}`);
}
```

### Finally, stitch it all together

Once we’ve done all this, let’s make sure that we return those properties we’re setting values in:

```JavaScript
function queryCSSByProperty(queryPropName, queryPropValue) {
  const styleSheets= document.styleSheets; 
  const properties = new Map();
  
  if (!queryPropName) return properties;
  
  Object.values(styleSheets).forEach(styleSheet => {
      
    try {
      const rules = styleSheet.cssRules;

        Object.values(rules).forEach(rule => {
          const {style, selectorText} = rule;
          
          if (style) {
            const propertyValue = style.getPropertyValue(queryPropName);
            const hasPropValMatch = queryPropValue && propertyValue.indexOf(queryPropValue) !== -1;
            
            if (queryPropValue && hasPropValMatch) {
              properties.set(selectorText, `${queryPropName}:${propertyValue}`);
            }
            if (!queryPropValue && propertyValue) properties.set(selectorText, `${queryPropName}:${propertyValue}`);
          }
      });
      } catch (err) {
        console.log(err);
      }
    });
    return properties;
}
```
## Final Result? 

You should be able to run `queryCSSByProperty('z-index')` on this page and discover that there's over 60 CSS rulesets using z-index. Quite nicely though for dev.to, they're managing most of them with CSS custom properties. 


A function like this will help you not be that person who’s picking random numbers out of a hat. In fact, a function like this might be a useful way to make sure your element is always `lastzIndex + 1`.

Of course, this could have other uses, too, beyond `z-index`. But you get the idea.

BTW, the final version of this is a [gist](https://gist.github.com/paceaux/2b163c39062ec7ed4d78f70d78e5a2fc), if that’s what you’re looking for.