Search code examples
htmlcsshtml-tablesveltecss-tables

Table cell borders are transparent when fixing them


I am trying to create a Svelte table component (although I think Svelte has nothing to do with this) that can have fixed columns.

The basic logic seems to be working but there is a very strange display issue: The borders of the fixed cells disappear as soon as horizontal scrolling takes place, as if they:

A. Became transparent. One can see the contents of the non-fixed cells behind the border. B. Moved with the scollbar, as if they weren't fixed.

Component code:

<script>
    export let columns = [];
    export let data = [];
    export let showRowNumbers = false;
    export let rowNumberOffset = 0;
    export let widthUnit = 'em';

    const defaultWidth = 12;
    const rowNumberWidth = 2;
    let colWidths = [];
    let colLefts = {};
    let tableWidth;
    $: {
        tableWidth = 0;
        for (let i = 0; i < columns.length; ++i) {
            if (columns[i].fixed) {
                colLefts[columns[i].key] = tableWidth;
            }
            const colWidth = columns[i].width ?? defaultWidth;
            tableWidth += colWidth;
            colWidths[i] = colWidth;
        }
    }
</script>
<div class="grid-container">
    {#if columns.length}
        <table style:width={`${tableWidth}${widthUnit}`}>
            <colgroup>
                    {#if showRowNumbers}
                        <col />
                    {/if}
                {#each columns as col, i (col.key)}
                    <col style:width={`${colWidths[i]}${widthUnit}`} />
                {/each}
            </colgroup>
            <thead>
                <tr>
                    {#if showRowNumbers}
                        <th class="fixed"><div>&nbsp;</div></th>
                    {/if}
                    {#each columns as col, index (col.key)}
                        <th class:fixed={col.fixed} style:left={(col.fixed ? `${colLefts[col.key]}${widthUnit}` : 'initial')}>
                            <div>
                                <slot name="fixedColHeader" {col} {index}>
                                    {col.name}
                                </slot>
                            </div>
                        </th>
                    {/each}
                </tr>
            </thead>
            <tbody>
                {#if data?.length > 0}
                    {#each data as row, index (row.id)}
                        <tr class="data">
                            {#if showRowNumbers}
                                <td class="fixed" style:left="0" >
                                        {index + 1 + rowNumberOffset}
                                </td>
                            {/if}
                            {#each columns as col, index (`${row.id}-${col.key}`)}
                                <td class:fixed={col.fixed} style:left={col.fixed ? `${colLefts[col.key]}${widthUnit}` : 'initial'}>
                                        <slot name="dataCell" {col} {row} {index}>
                                            {row[col.key]}
                                        </slot>
                                </td>
                            {/each}
                        </tr>
                    {/each}
                {:else}
                    <tr>
                        {#if showRowNumbers}
                            <td class="fixed" left="0">
                                1
                            </td>
                        {/if}
                        {#each columns as col (col.key)}
                            <td class:fixed={col.fixed} style:left={col.fixed ? `${colLefts[col.key]}${widthUnit}` : 'initial'}>
                                &nbsp;
                            </td>
                        {/each}
                    </tr>
                {/if}
            </tbody>
        </table>
    {:else}
        <div class="no-cols">
            (No columns defined)
        </div>
    {/if}
</div>

<style>
    div.grid-container {
        width: 100%;
        height: 100%;
        overflow: auto;
    }
    
    div.no-cols {
        display: flex;
        align-items: center;
        justify-content: center;
        height: 100%;
    }

    table {
        border-spacing: 0;
        border-width: 0;
        border-collapse: collapse;
        table-layout: fixed;
        width: 100%;
        position: relative;
    }

    table th, table td {
        padding: 0.2em;
    }

    table th {
        background-color: lightgray;
        text-wrap: nowrap;
        text-overflow: ellipsis;
    }

    table td {
        background-color: white;
    }

    table td, table th {
        border-right: 0.1em solid #525252;
    }

    table tr > td:first-child, table tr > td:first-child {
        border-left: 0.1em solid #525252;
    }

    th.fixed, td.fixed {
        position: sticky;
        z-index: 2;
    }
</style>

Let me know if I should include anything else here.


Solution

  • Ok, I found a fix for this, but I must confess I don't know why.

    The table had the style border-collapse: collapse;. I changed the value to border-collapse: initial; and the problem is gone.