Search code examples
htmlcsssticky

Elements with position sticky on table cells overlaps and z-index doesn't work


I have this table:

.collapsible {
    --line-height: 40;
    overflow-y: scroll;
    height: calc(var(--line-height) * 6px);
    min-height: calc(var(--line-height) * 5px);
    max-height: calc(var(--rows) * (var(--line-height) + 1) * 1px);
}
.collapsible table {
    position: relative;
}
.level-2 [rowspan] {
    vertical-align: top;
}
table td,
table th {
    font-size: 14px;
    line-height: 20px;
    letter-spacing: 0.25px;
}
table td:not([rowspan]),
table [rowspan] div {
    padding: 8px 0 8px 8px;
}
table.new th:not([rowspan]) {
    position: sticky;
    top: 0;
}
table.new th:not([rowspan]) div {
    z-index: 100;
    background: white;
    height: calc(var(--line-height) * 1px);
}
table.new td:not([rowspan]),
table.new th:not([rowspan]) div,
table [rowspan] div {
    --padding: calc(1px * (var(--line-height) - 20) / 2);
    padding: var(--padding) 8px var(--padding) 8px;
}

.level-2 [rowspan] div {
    position: sticky;
    background: white;
    z-index: 0;
    top: calc(var(--line-height) * 1px);
}
<div class="collapsible">
   <table class="new">
        <tr>
            <th><div>Project Stage</div></th>
            <th><div>Terapeutic Area</div></th>
            <th class="right"><div>Year</div></th>
            <th class="right"><div>Percentage Share</div></th>
            <th class="right"><div>Unit Share</div></th>
        </tr>
        <tr class="level-2 agragated-2-row">
            <th rowspan="9" class="level-0"><div>DEV</div></th>
            <td rowspan="3"><div>Cancer Immunology</div></td>
            <td>2020</td>
            <td>4%</td>
            <td>16</td>
        </tr>
        <tr class="level-2">
            <td>2019</td>
            <td>1%</td>
            <td>2</td>
        </tr>
        <tr class="level-2">
            <td>2018</td>
            <td>2%</td>
            <td>8</td>
        </tr>
        <tr class="level-2 agragated-1-row">
            <td rowspan="3"><div>Immunology</div></td>
            <td>2020</td>
            <td>4%</td>
            <td>16</td>
        </tr>
        <tr class="level-2">
            <td>2019</td>
            <td>1%</td>
            <td>2</td>
        </tr>
        <tr class="level-2">
            <td>2018</td>
            <td>2%</td>
            <td>8</td>
        </tr>
        <tr class="level-2 agragated-1-row">
            <td rowspan="3"><div>Infectious Diseases</div></td>
            <td>2020</td>
            <td>4%</td>
            <td>16</td>
        </tr>
        <tr class="level-2">
            <td>2019</td>
            <td>1%</td>
            <td>2</td>
        </tr>
        <tr class="level-2">
            <td>2018</td>
            <td>2%</td>
            <td>8</td>
        </tr>
        <tr class="level-2 agragated-2-row">
            <th rowspan="6" class="level-0"><div>ED</div></th>
            <td rowspan="3"><div>Cancer Immunology</div></td>
            <td>2020</td>
            <td>4%</td>
            <td>16</td>
        </tr>
        <tr class="level-2">
            <td>2019</td>
            <td>1%</td>
            <td>2</td>
        </tr>
        <tr class="level-2">
            <td>2018</td>
            <td>2%</td>
            <td>8</td>
        </tr>
        <tr class="level-2 agragated-1-row">
            <td rowspan="3"><div>Immunology</div></td>
            <td>2020</td>
            <td>4%</td>
            <td>16</td>
        </tr>
        <tr class="level-2">
            <td>2019</td>
            <td>1%</td>
            <td>2</td>
        </tr>
        <tr class="level-2">
            <td>2018</td>
            <td>2%</td>
            <td>8</td>
        </tr>
    </table>
</div>

The problem is that table cells that are not in the header are above the header sticky cells when you scroll.

So in this CSS:

/* this is for first row of th */
table.new th:not([rowspan]) div {
    position: sticky;
    background: white;
    z-index: 100;
}
/* this is for rowspan that is sticky, they are below first row */
.level-2 [rowspan] div {
    position: sticky;
    background: white;
    z-index: 0;
}

z-index is ignored. And when adding a negative z-index the cells disappear.

I've tried to put div:

<th>
    <div>
        <div class="filler">
            Project Stage
        </div>
    </div>
</th>

and css

.filler {
    position: absolute;
    z-index: 100;
    background: white;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
}

But this doesn't work. The sticky element that is next is always on top of the previous sticky element in HTML. Is there a way to change the order of sticky elements? I'm happy with any hack that makes this work.


Solution

  • set z-index "level-2 [rowspan] div" class with value -100 this will force this element to be sent back of all other elements

    .level-2 [rowspan] div {
        position: sticky;
        background: white;
        z-index: -100;
        top: calc(var(--line-height) * 1px);
    }
    

    and after that, you will find that the header row needs a little adjustment to increase the height of "table.new th:not([rowspan]) div" class with 5 px

    table.new th:not([rowspan]) div {
        z-index: 100;
        background: white;
        height: calc(5+ var(--line-height) * 1px);
    }
    

    this the full code snippet

    .collapsible {
        --line-height: 40;
        overflow-y: scroll;
        height: calc(var(--line-height) * 6px);
        min-height: calc(var(--line-height) * 5px);
        max-height: calc(var(--rows) * (var(--line-height) + 1) * 1px);
    }
    .collapsible table {
        position: relative;
    }
    .level-2 [rowspan] {
        vertical-align: top;
    }
    table td,
    table th {
        font-size: 14px;
        line-height: 20px;
        letter-spacing: 0.25px;
    }
    table td:not([rowspan]),
    table [rowspan] div {
        padding: 8px 0 8px 8px;
    }
    table.new th:not([rowspan]) {
        position: sticky;
        top: 0;
    }
    table.new th:not([rowspan]) div {
        z-index: 100;
        background: white;
        height: calc(5+ var(--line-height) * 1px);
    }
    table.new td:not([rowspan]),
    table.new th:not([rowspan]) div,
    table [rowspan] div {
        --padding: calc(1px * (var(--line-height) - 20) / 2);
        padding: var(--padding) 8px var(--padding) 8px;
    }
    
    .level-2 [rowspan] div {
        position: sticky;
        background: white;
        z-index: -100;
        top: calc(var(--line-height) * 1px);
    }
    <div class="collapsible">
       <table class="new">
            <tr>
                <th><div>Project Stage</div></th>
                <th><div>Terapeutic Area</div></th>
                <th class="right"><div>Year</div></th>
                <th class="right"><div>Percentage Share</div></th>
                <th class="right"><div>Unit Share</div></th>
            </tr>
            <tr class="level-2 agragated-2-row">
                <th rowspan="9" class="level-0"><div>DEV</div></th>
                <td rowspan="3"><div>Cancer Immunology</div></td>
                <td>2020</td>
                <td>4%</td>
                <td>16</td>
            </tr>
            <tr class="level-2">
                <td>2019</td>
                <td>1%</td>
                <td>2</td>
            </tr>
            <tr class="level-2">
                <td>2018</td>
                <td>2%</td>
                <td>8</td>
            </tr>
            <tr class="level-2 agragated-1-row">
                <td rowspan="3"><div>Immunology</div></td>
                <td>2020</td>
                <td>4%</td>
                <td>16</td>
            </tr>
            <tr class="level-2">
                <td>2019</td>
                <td>1%</td>
                <td>2</td>
            </tr>
            <tr class="level-2">
                <td>2018</td>
                <td>2%</td>
                <td>8</td>
            </tr>
            <tr class="level-2 agragated-1-row">
                <td rowspan="3"><div>Infectious Diseases</div></td>
                <td>2020</td>
                <td>4%</td>
                <td>16</td>
            </tr>
            <tr class="level-2">
                <td>2019</td>
                <td>1%</td>
                <td>2</td>
            </tr>
            <tr class="level-2">
                <td>2018</td>
                <td>2%</td>
                <td>8</td>
            </tr>
            <tr class="level-2 agragated-2-row">
                <th rowspan="6" class="level-0"><div>ED</div></th>
                <td rowspan="3"><div>Cancer Immunology</div></td>
                <td>2020</td>
                <td>4%</td>
                <td>16</td>
            </tr>
            <tr class="level-2">
                <td>2019</td>
                <td>1%</td>
                <td>2</td>
            </tr>
            <tr class="level-2">
                <td>2018</td>
                <td>2%</td>
                <td>8</td>
            </tr>
            <tr class="level-2 agragated-1-row">
                <td rowspan="3"><div>Immunology</div></td>
                <td>2020</td>
                <td>4%</td>
                <td>16</td>
            </tr>
            <tr class="level-2">
                <td>2019</td>
                <td>1%</td>
                <td>2</td>
            </tr>
            <tr class="level-2">
                <td>2018</td>
                <td>2%</td>
                <td>8</td>
            </tr>
        </table>
    </div>