JSFiddle - React, Tailwind, and code Playground

by mgibsonbr

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>

CSS

.contexto { position: absolute; width: 100px; height: 100px; }

#A { background: red; z-index: 2; }
#B { left: 80px; top: 80px; background: green; z-index: 1; }

.contexto div { position: absolute; width: 40px; height: 40px; }

#A div { bottom: 0; right: 0; background: blue; z-index: 10; }
#B div { top: 0; left: 0; background: yellow; z-index: 20; }

#semZ { 
    position: absolute; width: 100px; height: 100px;
    top: 0; left: 75px; background: lightgray;
}

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');

function naFrente(a, b) {
    var pa = $(a).parents(), ia = pa.length;
    var pb = $(b).parents(), ib = pb.length;
    
    // Salta todos os ancestrais em comum
    while ( ia > 0 && ib > 0 && pa[--ia] == pb[--ib] ) { }
    var ctxA = (ia >= 0 ? pa[ia] : a), za = zIndex(ctxA);
    var ctxB = (ib >= 0 ? pb[ib] : b), zb = zIndex(ctxB);
    
    // Em último caso, olha as posições relativas
    var relativo = ($(ctxA).index() - $(ctxB).index() < 0 ? a : b);
    
    // Acha o primeiro com zIndex definido
    while ( ctxA && za === undefined ) {
        ctxA = ia < 0 ? null : --ia < 0 ? a : pa[ia];
        za = zIndex(ctxA);
    }
    while ( ctxB && zb === undefined ) {
        ctxB = ib < 0 ? null : --ib < 0 ? b : pb[ib];
        zb = zIndex(ctxB);
    }
    
    if ( za !== undefined ) {
        if ( zb !== undefined )
            return za > zb ? a : za < zb ? b : relativo;
        return za > 0 ? a : za < 0 ? b : relativo;
    }
    else if ( zb !== undefined )
        return zb < 0 ? a : zb > 0 ? b : relativo;
    else
        return relativo;
}


function zIndex(ctx) {
    if ( !ctx || ctx === document.body ) return;
    
    // Verifica se o elemento está posicionado, 
    // e se tem z-index ou opacity
    var posicionado = css(ctx, 'position') !== 'static';
    var possuiZIndex = css(ctx, 'z-index') !== 'auto';
    var naoOpaco = +css(ctx, 'opacity') < 1;
        
    // Se ctx for um contexto, retorna
    //if(posicionado && (possuiZIndex || naoOpaco)) {
    if(posicionado && possuiZIndex) // Ignorando CSS3 por ora
        return +css(ctx, 'z-index');
}

function css(el, prop) {
     return window.getComputedStyle(el).getPropertyValue(prop);
}

//
// TESTE
//

console.log("A vs B: " + naFrente(ctxA,...