JSFiddle - React, Tailwind, and code Playground
HTML
<div class="contexto" id="A">
A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A
<div id="dentroDeA"></div>
</div>
<div class="contexto" id="B">
B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B
<div id="dentroDeB"></div>
</div>
<div id="semZ">
<div id="dentroDeSemZ"></div>
</div>
<div id="semZ2"></div>
<table border="1">
</table>
CSS
.contexto { position: absolute; width: 100px; height: 100px; }
#A { background: red; z-index: 0; }
#B { left: 80px; top: 80px; background: green; z-index: 0; }
.contexto div { position: absolute; width: 40px; height: 40px; }
#A div { bottom: 5px; right: 5px; background: blue; z-index: 0; }
#B div { top: 5px; left: 0px; background: yellow; z-index: 0; }
#semZ {
position: absolute; width: 100px; height: 100px;
top: 0; left: 75px; background: lightgray;
}
#semZ div {
position: absolute; width: 40px; height: 40px;
top: 20px; left: 20px; background: cyan;
}
#semZ2 {
position: absolute; width: 100px; height: 100px;
top: 40px; left: 85px; background: gray;
}
table {
position:absolute;
left: 200px;
}
JavaScript
var ctxA = document.getElementById('A');
var ctxB = document.getElementById('B');
var dentroDeA = document.getElementById('dentroDeA');
var dentroDeB = document.getElementById('dentroDeB');
var semZ = document.getElementById('semZ');
var semZ2 = document.getElementById('semZ2');
var DsemZ = document.getElementById('dentroDeSemZ');
function naFrente(a, b, memo) {
// Guardando o elemento passado na chamada original,
// em caso de recursão
memo = memo || [[],[]];
memo[0].push(a);
memo[1].push(b);
// Contextos de empilhamento dos elementos passados
var ctxA = contexto(a);
var ctxB = contexto(b);
// Se a é descendente de b, considera que a está na frente
if(descendente(a, b)) return a;
// Se b é descendente de ba, considera que b está na frente
if(descendente(b, a)) return b;
// Se no mesmo contexto, compara z-índices
if(ctxA === ctxB) {
var zA = +css(a, 'z-index');
var zB = +css(b, 'z-index');
// Comparando dois z-índices definidos
if(!isNaN(zA) && !isNaN(zB)) {
return zB > zA ? memo[1][0] : memo[0][0];
// Primeiro termo não definido:
// retorna o segundo se não for NaN
} else if(isNaN(zA)) {
return isNaN(zB) ? memo[0][0] : memo[1][0];
// Ambos NaN, retorna o primeiro termo
} else {
return memo[0][0];
}
// Recursão no caso de contextos diferentes
} else {
// Se subiu até o body, restaura o contexto anterior
// para achamada recursiva
ctxA = ctxA === document.body ? a : ctxA;
ctxB = ctxB === document.body ? b : ctxB;
return naFrente(ctxA, ctxB, memo);
}
}
// Determina o elemento que representa o contexto de empilhamento de outro
function contexto(el) {
var ctx = el.parentElement;
if(ctx && ctx !== document.body && el !== document.body) {
// Verifica se o elemento está posicionado,
...