How can you figure out the highest z-index in your document?

Viewed 46098

In order to set a div containing a transparent text image as the highest z-index in my document, I picked the number 10,000 and it solved my problem.

Previously I had guessed with the number 3 but it had no effect.

So, is there a more scientific way of figuring out what z-index is higher than that of all of your other elements?

I tried looking for this metric in Firebug but couldn't find it.

21 Answers

Stealing some code from abcoder site for the sake of clarity:

  var maxZ = Math.max.apply(null, 
    $.map($('body *'), function(e,n) {
      if ($(e).css('position') != 'static')
        return parseInt($(e).css('z-index')) || 1;
  }));

You could call findHighestZIndex for a particular element type such as <div> like this:

findHighestZIndex('div');

assuming a findHighestZindex function that is defined like this:

function findHighestZIndex(elem)
{
  var elems = document.getElementsByTagName(elem);
  var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
  for (var i = 0; i < elems.length; i++)
  {
    var zindex = Number.parseInt(
      document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
      10
    );
    if (zindex > highest)
    {
      highest = zindex;
    }
  }
  return highest;
}

Using ES6 a cleaner approach

function maxZIndex() {

     return Array.from(document.querySelectorAll('body *'))
           .map(a => parseFloat(window.getComputedStyle(a).zIndex))
           .filter(a => !isNaN(a))
           .sort()
           .pop();
}

There isn't a default property or anything, but you could write some javascript to loop through all elements and figure it out. Or if you use a DOM management library like jQuery, you could extend its methods (or find out if it supports it already) so that it starts tracking element z-indices from page load, and then it becomes trivial to retrieve the highest z-index.

I believe what you are observing is Voodoo. Without access to your complete style sheet I can of course not tell reliably; but it strikes me as likely that what really has happened here is that you have forgotten that only positioned elements are affected by z-index.

Additionally, z-indexes aren't assigned automatically, only in style sheets, which means that with no other z-indexed elements, z-index:1; will be on top of everything else.

I guess you have to do this yourself ...

function findHighestZIndex()
{
    var divs = document.getElementsByTagName('div');
    var highest = 0;
    for (var i = 0; i < divs .length; i++)
    {
        var zindex = divs[i].style.zIndex;
        if (zindex > highest) {
            highest = zindex;
        }
    }
    return highest;
}

The best way to solve this problem is, in my opinion, just to set yourself conventions for what kinds of z-indexes are used for different kinds of elements. Then, you'll find the correct z-index to use by looking back at your documentation.

The "ES6" version above is less efficient than the first solution because it does multiple redundant passes across the full array. Instead try:

findHighestZ = () =>
  [...document.querySelectorAll('body *')]
    .map(elt => parseFloat(getComputedStyle(elt).zIndex))
    .reduce((highest, z) => z > highest ? z : highest, 1)

In theory it would be even quicker to do it in one reduce step, but some quick benchmarking showed no significant difference, and the code is gnarlier

Array.reduce()

Here's another solution to determine the topmost z-index that uses Array.reduce():

const max_zindex = [...document.querySelectorAll('body *')].reduce((accumulator, current_value) => {
  current_value = +getComputedStyle(current_value).zIndex;

  if (current_value === current_value) { // Not NaN
    return Math.max(accumulator, current_value)
  }

  return accumulator;
}, 0); // Default Z-Index Rendering Layer 0 (Zero)

ShadowRoot solutions

We must not forget about custom-elements and shadow-root content.

function computeMaxZIndex() {
    function getMaxZIndex(parent, current_z = 0) {
        const z = parent.style.zIndex != "" ? parseInt(parent.style.zIndex, 10) : 0;
        if (z > current_z)
            current_z = z;
        const children = parent.shadowRoot ? parent.shadowRoot.children : parent.children;
        for (let i = 0; i < children.length; i++) {
            const child = children[i];
            current_z = getMaxZIndex(child, current_z);
        }
        return current_z;
    }
    return getMaxZIndex(document.body) + 1;
}

A solution highly inspired from the excellent idea of @Rajkeshwar Prasad .

 /**
 returns highest z-index
 @param {HTMLElement} [target] highest z-index applyed to target if it is an HTMLElement.
 @return {number} the highest z-index.
 */
 var maxZIndex=function(target) {
     if(target instanceof HTMLElement){
         return (target.style.zIndex=maxZIndex()+1);
     }else{
         var zi,tmp=Array.from(document.querySelectorAll('body *'))
             .map(a => parseFloat(window.getComputedStyle(a).zIndex));
         zi=tmp.length;
         tmp=tmp.filter(a => !isNaN(a));
         return tmp.length?Math.max(tmp.sort((a,b) => a-b).pop(),zi):zi;
     }
 };
#layer_1,#layer_2,#layer_3{
  position:absolute;
  border:solid 1px #000;
  width:100px;
  height:100px;
}
#layer_1{
  left:10px;
  top:10px;
  background-color:#f00;
}
#layer_2{
  left:60px;
  top:20px;
  background-color:#0f0;
  z-index:150;
}
#layer_3{
  left:20px;
  top:60px;
  background-color:#00f;
}
<div id="layer_1" onclick="maxZIndex(this)">layer_1</div>
<div id="layer_2" onclick="maxZIndex(this)">layer_2</div>
<div id="layer_3" onclick="maxZIndex(this)">layer_3</div>

If you're looking to show the IDs of all elements with the highest z indices:

function show_highest_z() {
    z_inds = []
    ids = []
    res = []
    $.map($('body *'), function(e, n) {
        if ($(e).css('position') != 'static') {
            z_inds.push(parseFloat($(e).css('z-index')) || 1)
            ids.push($(e).attr('id'))
        }
    })
    max_z = Math.max.apply(null, z_inds)
    for (i = 0; i < z_inds.length; i++) {
        if (z_inds[i] == max_z) {
            inner = {}
            inner.id = ids[i]
            inner.z_index = z_inds[i]
            res.push(inner)
        }
    }
    return (res)
}

Usage:

show_highest_z()

Result:

[{
    "id": "overlay_LlI4wrVtcuBcSof",
    "z_index": 999999
}, {
    "id": "overlay_IZ2l6piwCNpKxAH",
    "z_index": 999999
}]

Robust solution to find maximum zIndex in NodeList

  1. You should check both getComputedStyle and style object provided by node itself
  2. Use Number.isNaN instead of isNaN because of isNaN("") === false
function convertToNumber(value) {
  const asNumber = parseFloat(value);
  return Number.isNaN(asNumber) ? 0 : asNumber;
}

function getNodeZIndex(node) {
  const computedIndex = convertToNumber(window.getComputedStyle(node).zIndex);
  const styleIndex = convertToNumber(node.style.zIndex);

  if (computedIndex > styleIndex) {
    return computedIndex;
  }

  return styleIndex;
}

function getMaxZIndex(nodeList) {
  const zIndexes = Array.from(nodeList).map(getNodeZIndex);
  return Math.max(...zIndexes);
}

const maxZIndex = getMaxZIndex(document.querySelectorAll("body *"));

Very simple code using map and filter

function calMaxZIndex() {
  return Array.from(document.querySelectorAll('body *'))
    .map(a => parseFloat(window.getComputedStyle(a).zIndex || a.style.zIndex))
    .filter(a => !isNaN(a))
    .sort()
    .pop()
}

function getMax() {
  const max = calMaxZIndex() ?? 0
  console.log({
    max
  })
}

getMax()
#ticket-box {
  text-align: center;
  position: fixed;
  top: 0;
  right: 0;
  width: 100%;
  background-color: #e9d295;
  padding: 5px;
  z-index: 6;
}
<div id="menu">
  <a href="javascript:void(0);" onclick="closeMenu();" style="color: #ffffff; position: absolute; top: 15px; right: 15px;text-decoration: none;">CLOSE</a>

  <ul style="text-align:center;list-style-type:none;">
    <li><a href="#">FILM</a></li>
    <li><a href="#">MUSIC</a></li>
    <li><a href="#">SPORTS</a></li>
    <li><a href="#">FINANCE</a></li>
  </ul>
</div>

<div id="ticket-box">Have you bought your tickets for friday's event? No?! <a href="#">Grab yours now!</a></div>

<center><a href="javascript:void(0);" onclick="revealMenu();" style="display: inline-block; color: #333333; margin-top: 90px;">MENU</a></center>

Based on previous answers:

function with some modifications

let zIndexMax = () =>
    [...document.querySelectorAll('body > *')]
        .map(elem => parseInt(getComputedStyle(elem).zIndex, 10) || 0)
        .reduce((prev, curr) => curr > prev ? curr : prev, 1);

Prototype

HTMLElement.prototype.zIndexMax = function () {
    return [...this.children]
        .map(elem => parseInt(getComputedStyle(elem).zIndex, 10) || 0)
        .reduce((prev, curr) => curr > prev ? curr : prev, 1);
}

usage

document.querySelector('body').zIndexMax();

After looking through a lot of solutions here on StackOverflow - I've seen that none of them actually works correctly and considers how is zIndex actually working. I have written a solution that also takes into consideration the stacking context. You can refer to this article to understand how stacking context works in CSS.

const getZIndex = el => {
  const computedStyle = getComputedStyle(el, null)
  const zIndex = computedStyle.getPropertyValue('z-index')
  return zIndex !== 'auto' ? parseInt(zIndex) : null
}

const getZIndexWithinStackingContext = (el, context) => {
  let zIndex = getZIndex(el)
  if (!zIndex) return null

  let result = zIndex

  while (el.parentElement !== context) {
    el = el.parentElement
    zIndex = getZIndex(el)
    if (zIndex) {
      result = zIndex
    }
  }

  return result
}

const createZIndex = (overVisibleOnly = false, context = document.body) => {
  const elements = [...document.querySelectorAll('body *')]
  let highestZIndex = 0

  elements.forEach(el => {
    if (overVisibleOnly) {
      const isVisible = !!el.offsetParent
      if (!isVisible) return
    }

    const zIndex = getZIndexWithinStackingContext(el, context)

    if (zIndex && zIndex > highestZIndex) {
      highestZIndex = zIndex
    }
  })

  return highestZIndex + 1
}

Note that this solution considers all elements, and not only positioned ones because they can become positioned after some class is added. But you can fix this easily by just adding a check for the position computed style property.

I have found the provided methods don't work when there have been z-indices changed dynamically on the page (the current methods only grab the originally set z-indices).

This function also works with dynamically added z indices:

function find_max_z_index() {    
    const all_z = [];
    document.querySelectorAll("*").forEach(function(elem) {
        all_z.push(elem.style.zIndex)
    })
    const max_index = Math.max.apply(null, all_z.map((x) => Number(x)));
    return(max_index)
}

Here is my two-line function:

const getMaxZIndex = function () {
    const elements = [...document.querySelectorAll("body *")];
    return Math.max(...elements.map(x => parseInt(getComputedStyle(x, null).zIndex) || 0));
};
console.log(getMaxZIndex());
Related