74

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.

TylerH
  • 19,065
  • 49
  • 65
  • 86
Charlie Kotter
  • 1,759
  • 3
  • 15
  • 13
  • 1
    Note that finding the largest z-index is not exactly relevant, but it will work. What you need is the largest z-index of those elements that form a stacking context that are in the same stacking context as the element you're trying to place. In other words, if you find an element that is `position: relative; z-index: 10000;` that is inside of an element that is `position: relative; z-index: 100;`, then the number that you need to beat is 100, not 10,000. – Chris Calo Jan 28 '13 at 15:31
  • z-index basics: http://stackoverflow.com/a/32515284/3597276 – Michael Benjamin Sep 13 '15 at 22:04

16 Answers16

44

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;
  }));
Michel Floyd
  • 16,271
  • 4
  • 21
  • 38
Jimmy Chandra
  • 6,333
  • 4
  • 24
  • 37
  • this one seems to be the most shortest and efficient method of getting z-index i've seen so far. – ThatGuy Sep 02 '11 at 19:31
  • The only answer I've found that isn't limited to the max for a certain element type. I want my z-index higher that *all* other z-indexes and this is a gem. – ProfK Oct 16 '17 at 03:43
  • 1
    elegant but not usable because, from mdn of `Math.max`: "both spread `(...)` and `apply` will either fail or return the wrong result if the array has too many elements" – oriadam Nov 26 '18 at 07:35
  • 2
    ^ Having said that, `Math.max` can accept up to 100,000 arguments on Chrome, 300,000 on Firefox, 400,000 on Edge, 150,000 on IE11 (tested on Win10, all browsers latest). – oriadam Nov 26 '18 at 08:02
40

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;
}
Peter W
  • 554
  • 3
  • 14
flo
  • 629
  • 5
  • 5
  • Workaround here for IE's lack of getComputedStyle http://erik.eae.net/archives/2007/07/27/18.54.15/ – Matthew Lock Sep 02 '09 at 06:37
  • Why would you limit it to one element type? – Chris Calo Jan 28 '13 at 15:25
  • 1
    @ChristopherJamesCalo, it is a generic solution. If you use '*' as the argument of `findHighestZIndex`, it'll get for all elements. – user2064000 May 30 '13 at 11:51
  • Currently doesn't work properly because highest is saved as string. Instead, set zindex after calling parseInt on the z-index that you've found (note: this means you should check for !isNaN() rather than != 'auto'). – 190290000 Ruble Man Jul 21 '14 at 18:10
  • 1
    This only works if all z-index are below 999. It is comparing strings so it thinks 999 > 1000. It also assumes that the highest z-index is above zero. It is possible that the highest is a negative number. http://devmunchies.com/tutorial/finding-highest-z-index-on-page – Sam Eaton May 07 '16 at 00:17
  • @190290000RubleMan actually the `zindex > highest` is sufficient to rule out cases where zindex is NaN, because `NaN > highest` would always return `false`. – Peter W Aug 10 '20 at 09:12
16

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();
}
  • 6
    Sort needs an comparator to work properly. Consider this: `[2 ,1, 100].sort()` gives in result `[1, 100, 2]` which is wrong. Use comparator instead: `[2 ,1, 100].sort((a,b) => a-b)` which gives correct `[1, 2, 100]` – Adam Szmyd Apr 26 '18 at 06:32
  • 4
    using `sort` seems like a whole lot of unnecessary cpu and memory consumption – oriadam Nov 26 '18 at 07:36
  • 2
    A `reduce` would only go through the set of indices once so `O = n`, while `sort` has `O = n log(n)`. So one can easily replace `sort` and `pop` with `reduce`. – Robert Koritnik Dec 09 '19 at 08:38
  • This answer itself did not work. Add `.sort((a,b) => a-b)` as @AdamSzmyd said make it working. – vee Jan 26 '20 at 17:42
8

I’d like to add my ECMAScript 6 implementation that I use in one of my UserScripts. I’m using this one to define the z-index of specific elements so that they always appear the highest.

In JS, you can additionally set certain attributes or class names to elements that you may want to exclude. For instance, consider your script setting a data-highest attribute on an element that you want to appear as the highest element (e.g. a popup); and consider an element with class name yetHigher that you don’t control, which should be even higher (e.g. a custom context menu). I can exclude these elements with a chained :not selector. Note that :not([data-highest], .yetHigher) is possible, but experimental, and only has limited browser support as of January 2021.

let highestZIndex = 0;

// Then later, potentially repeatedly
highestZIndex = Math.max(
  highestZIndex,
  ...Array.from(document.querySelectorAll("body *:not([data-highest]):not(.yetHigher)"), (elem) => parseFloat(getComputedStyle(elem).zIndex))
    .filter((zIndex) => !isNaN(zIndex))
);

The lower five lines can run multiple times and update the variable highestZIndex repeatedly by finding out the maximum between the current highestZIndex value and all the other computed z-indexes of all elements. The filter excludes all the "auto" values.

Sebastian Simon
  • 14,320
  • 6
  • 42
  • 61
  • According to MDN one cannot use `Math.max` to do this: "both spread `(...)` and `apply` will either fail or return the wrong result if the array has too many elements [...] The reduce solution does not have this problem" – oriadam Nov 26 '18 at 07:40
  • Having said that, this will probably cover most real life cases as Chrome supports up to 100k values, Firefox 300k, Edge 400k. – oriadam Nov 26 '18 at 07:58
4

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.

Rahul
  • 11,973
  • 5
  • 41
  • 63
4

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.

Gareth
  • 115,773
  • 31
  • 143
  • 154
  • 1
    While I agree this is a best practice and could help in most situations, it doesn't help if you are building a plugin or script that may run on an unknown page where your script isn't privy to the z-index conventions used. (sorry to comment on your 9-year-old post.) – Micah Murray Feb 25 '18 at 15:25
4

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.

Williham Totland
  • 26,865
  • 5
  • 48
  • 68
  • I'll expand on your second paragraph to explain that only a `z-index` value of <> 0, and not `auto`, actually creates a new stacking context. – Kevin Peno Aug 19 '11 at 20:03
4

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;
}
Philippe Gerber
  • 16,204
  • 5
  • 42
  • 40
2

Using jQuery:

if no elements supplied, it checks all elements.

function maxZIndex(elems)
{
    var maxIndex = 0;
    elems = typeof elems !== 'undefined' ? elems : $("*");

    $(elems).each(function(){
                      maxIndex = (parseInt(maxIndex) < parseInt($(this).css('z-index'))) ? parseInt($(this).css('z-index')) : maxIndex;
                      });

return maxIndex;
}
Misfer Ali
  • 21
  • 3
2

I had to do this for a project recently, and I found that I benefitted a lot from @Philippe Gerber's great answer here, and @flo's great answer (the accepted answer).

The key differences to the answers referenced above are:

  • Both the CSS z-index, and any inline z-index style are calculated, and use the larger of the two for comparison and calculation.
  • Values are coerced into integers, and any string values (auto, static, etc) are ignored.

Here is a CodePen for the code example, but it's included here as well.

(() => {
  /**
   * Determines is the value is numeric or not.
   * See: https://stackoverflow.com/a/9716488/1058612.
   * @param {*} val The value to test for numeric type.
   * @return {boolean} Whether the value is numeric or not.
   */
  function isNumeric(val) {
    return !isNaN(parseFloat(val)) && isFinite(val);
  }

  
  /**
   * Finds the highest index in the current document.
   * Derived from the following great examples:
   *  [1] https://stackoverflow.com/a/1118216/1058612
   *  [2] https://stackoverflow.com/a/1118217/1058612
   * @return {number} An integer representing the value of the highest z-index.
   */
  function findHighestZIndex() {
    let queryObject = document.querySelectorAll('*');
    let childNodes = Object.keys(queryObject).map(key => queryObject[key]);
    let highest = 0;
    
    childNodes.forEach((node) => {
      // Get the calculated CSS z-index value.
      let cssStyles = document.defaultView.getComputedStyle(node);
      let cssZIndex = cssStyles.getPropertyValue('z-index');
      
      // Get any inline z-index value.
      let inlineZIndex = node.style.zIndex;

      // Coerce the values as integers for comparison.
      cssZIndex = isNumeric(cssZIndex) ? parseInt(cssZIndex, 10) : 0;
      inlineZIndex = isNumeric(inlineZIndex) ? parseInt(inlineZIndex, 10) : 0;
      
      // Take the highest z-index for this element, whether inline or from CSS.
      let currentZIndex = cssZIndex > inlineZIndex ? cssZIndex : inlineZIndex;
      
      if ((currentZIndex > highest)) {
        highest = currentZIndex;
      }
    });

    return highest;
  }

  console.log('Highest Z', findHighestZIndex());
})();
#root {
  background-color: #333;
}

.first-child {
  background-color: #fff;
  display: inline-block;
  height: 100px;
  width: 100px;
}

.second-child {
  background-color: #00ff00;
  display: block;
  height: 90%;
  width: 90%;
  padding: 0;
  margin: 5%;
}

.third-child {
  background-color: #0000ff;
  display: block;
  height: 90%;
  width: 90%;
  padding: 0;
  margin: 5%;
}

.nested-high-z-index {
  position: absolute;
  z-index: 9999;
}
<div id="root" style="z-index: 10">
  <div class="first-child" style="z-index: 11">
    <div class="second-child" style="z-index: 12"></div>
  </div>
  <div class="first-child" style="z-index: 13">
    <div class="second-child" style="z-index: 14"></div>
  </div>
  <div class="first-child" style="z-index: 15">
    <div class="second-child" style="z-index: 16"></div>
  </div>
  <div class="first-child" style="z-index: 17">
    <div class="second-child" style="z-index: 18">
      <div class="third-child" style="z-index: 19">
        <div class="nested-high-z-index">Hello!!! </div>
      </div>
    </div>
  </div>
  <div class="first-child">
    <div class="second-child"></div>
  </div>
  <div class="first-child">
    <div class="second-child"></div>
  </div>
  <div class="first-child">
    <div class="second-child"></div>
  </div>
</div>
User 1058612
  • 3,371
  • 24
  • 32
2

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;
}

2

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

podperson
  • 2,157
  • 2
  • 22
  • 22
  • 1
    I prefer this way. Slightly modified to cover if "highest" is not NaN. + I wanted to increase by 1 to place my element on top. ```isNaN(z) || z < highest ? (isNaN(highest) ? 1 : highest + 1) : z``` – Tina Hammar Apr 26 '21 at 15:23
  • Good catch! I'll edit the answer, – podperson May 07 '21 at 20:13
  • I was about to incorporate your extra isNaN check, but upon reflection realised highest is initialized to a number and never set to anything that could be isNaN. if isNaN(z), highest doesn't need to be increased it's already highest. So for every bad z value it would unnecessarily increment highest. – podperson May 07 '21 at 20:19
  • You need the extra `isNaN(z) || z < highest ? (isNaN(highest) ? 1 : highest) : z` check because if no z-index is set, to any element in `body`, `highest` will be `NaN` and you will probably want to return at least `1` from the function to be able to use it for `element.style.zIndex = findHighestZ()`. – Tina Hammar May 14 '21 at 17:20
  • Ah yes. "auto". Thanks for following up. – podperson May 19 '21 at 19:19
  • The larger bug is the parameters are around the wrong way and the default is overridden :) — so it gets simpler rather than more complex when I flip the comparison because NaN will never be > than an actual value. – podperson May 19 '21 at 19:31
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>
yorg
  • 514
  • 5
  • 7
1

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
}]
Cybernetic
  • 9,374
  • 12
  • 69
  • 99
1

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)
Grant Miller
  • 19,410
  • 15
  • 108
  • 135
1

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 *"));
Daniel Bultas
  • 51
  • 1
  • 3
  • I'd like to leave my [fiddle](https://jsfiddle.net/awolf2904/xw37n8of/) here. I've also added some tests in the fiddle (not sure if I have covered everything). – AWolf Jan 04 '21 at 15:05