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> </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'}>
</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.
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.