by

What’s the highest z-index on a page (and other CSS property problems)

Reading Time: 4 minutes

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 simple JavaScript function that can do that.

Memes provide a safe way for me to tell you that CSS makes me very sad sometimes

Remember the CSSOM?

I’ve written about the CSSOM in the past (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.

First, Loop through 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.

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

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.

 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 forin, 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.

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:

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 very blog post and discover that WordPress developers don’t give a crap what z-index is.

Seriously, div.jp-carousel-fadeaway , who wrote you and what were they thinking?

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, if that’s what you’re looking for.