htmlcssvuejs3

How to give position:sticky to table's header and the first column when table also has overflow-x:scroll?


Here's the table:

| Units | KPI1 | KPI2 | KPI3 | KPI4 | KPI5 | KPI6 | KPI7 |
----------------------------------------------------------
| Unit 1 | val |  val |  val |  val |  val |  val |  val |
| Unit 2 | val |  val |  val |  val |  val |  val |  val |
| Unit 3 | val |  val |  val |  val |  val |  val |  val |
| Unit 4 | val |  val |  val |  val |  val |  val |  val |
| Unit 5 | val |  val |  val |  val |  val |  val |  val |
| Unit 6 | val |  val |  val |  val |  val |  val |  val |
| Unit 7 | val |  val |  val |  val |  val |  val |  val |
| Unit 8 | val |  val |  val |  val |  val |  val |  val |
| Unit 9 | val |  val |  val |  val |  val |  val |  val |
----------------------------------------------------------

Because the table is wide, I applied overflow-x: scroll to the parent element of the table. At the same time, I need to apply position: sticky to the header (the row with "Units," "KPI1," "KPI2," etc.) so that it stays fixed at the top when scrolling vertically. I also need to apply position: sticky to the first column of each row (where "Unit 1," "Unit 2," etc., are listed) so that it stays fixed when scrolling horizontally.

The code looks like this:

<div class="block-body">
    <table class="table">
        <thead class="table-head">
            <tr>
                <th class="sticky-col">Units</th>
                <th>KPI1</th>
                <th>KPI2</th>
                <!-- etc. -->
            </tr>
        </thead>
        <tbody>
            <tr>
                <td class="sticky-col">Unit 1</td>
                <td>val</td>
                <td>val</td>
                <!-- etc. -->
            </tr>
            <!-- etc. -->
        </tbody>
    </table>
</div>

.table-head th {
    position: sticky;
    top: 0;
    z-index: 2;
}
.sticky-col {
    position: sticky;
    left: 0;
    z-index: 1;
}

The overflow-x: scroll style is applied to .block-table. position: sticky isn't working, and I believe it's because of the overflow-x: scroll.

I tried adding a parent element .horizontal-scroll with the style position: relative;

<div class="block-body">
    <div class="horizontal-scroll">
        <table class="table">
            <thead class="table-head">
                <tr>
                    <th class="sticky-col">Units</th>
                    <th>KPI1</th>
                    <th>KPI2</th>
                    <!-- etc. -->
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td class="sticky-col">Unit 1</td>
                    <td>val</td>
                    <td>val</td>
                    <!-- etc. -->
                </tr>
                <!-- etc. -->
            </tbody>
        </table>
    </div>
</div>

.horizontal-scroll {
    overflow-x: scroll;
    position: relative;
}
.table {
    border-collapse: collapse;
    width: 100%;
}
.table-head th {
    position: sticky;
    top: 0;
    z-index: 10;
}
.sticky-col {
    position: sticky;
    left: 0;
    z-index: 5;
}

Solution

  • I have created a simple example to illustrate your requirement.

    1. We will create a div with class table-container and inside it we have our table. For table-container we will add two main properties overflow: auto & height: 300px (which will add vertical scroll).
    2. For table we will add this two properties table-layout: fixed and width: 100%;.
    3. To make header sticky we will add position: sticky and z-index: 10 (So that its above unit row when we add sticky to that) to our th.
    4. To apply position: sticky to the first column of each row, we will add td:nth-child(1){position: sticky;left: 0}

    That's all. Below is working example.

    body {
                margin: 0;
                padding: 0;
                color: white;
                background-color: #333;
            }
    
            .table-container {
                height: 300px;
                overflow: auto;
                margin: 20px;
            }
    
            table {
                table-layout: fixed;
                width: 100%;
            }
            thead{
                z-index: 10;
                background-color:#333;
                position: sticky;
                top: 0;
                left:0;
            }
            th, td {
                width: 200px;
                padding: 8px;
            }
            td:nth-child(1){
                background-color:#333;
                position: sticky;
                left: 0
            }
    <div class="table-container">
            <table>
                <thead>
                    <tr>
                        <th>Units</th>
                        <th>KPI1</th>
                        <th>KPI2</th>
                        <th>KPI1</th>
                        <th>KPI2</th>
                        <th>KPI1</th>
                        <th>KPI2</th>
                        <th>KPI1</th>
                        <th>KPI2</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>Unit 1</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                    </tr>
                    <tr>
                        <td>Unit 2</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                    </tr>
                    <tr>
                        <td>Unit 3</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                    </tr>
                    <tr>
                        <td>Unit 4</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                    </tr>
                    <tr>
                        <td>Unit 5</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                    </tr>
                    <tr>
                        <td>Unit 6</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                    </tr>
                    <tr>
                        <td>Unit 7</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                    </tr>
                    <tr>
                        <td>Unit 8</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                    </tr>
                    <tr>
                        <td>Unit 9</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                    </tr>
                    <tr>
                        <td>Unit 1</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                    </tr>
                    <tr>
                        <td>Unit 1</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                    </tr>
                    <tr>
                        <td>Unit 1</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                        <td>val</td>
                    </tr>
                </tbody>
            </table>
        </div>