如何实现表格 行 和 列 各种情况的 sticky ?

表格图示如下:

在尽量使用 CSS 的情况,如何实现以下需求:

1. 垂直滚动时,当前 Team 的表头部分(绿色整行),在 该Team 没有滚出视口前,保持在 Summary 的下方
2. 垂直滚动时,当前 Team 第一列的 Team Name,能否保持在 该Team 表头同一行位置,不被滚出视口
3. 横向滚动时,如何保持 Head X 表头(4列)冻结,不被滚出视口,表头列需要 宽度自适应,不能写死 `width`
4. 以上需求尽量使用 `CSS`,非必要不使用 `JS`

表格页面基础代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
    .frame {position:relative;width:800px;height:500px;box-shadow:0 0 0 1px #000;overflow:auto;}

    .frame table {border-spacing:0;line-height:2em;}
    .frame table tr th {padding:0 0.5em;font-weight:normal;white-space:nowrap;border:1px solid #CCC;}
    .frame table tr td {padding:0 0.5em;border:1px solid #CCC;background-color:#FFF;}

    .frame table tr.gap td {height:1px;background-color:#000;}

    .frame table thead tr th {color:#369;background-color:#F0F9FF;}

    .frame table tbody tr th {color:#393;background-color:#F0FFF0;}
    .frame table tbody tr td {min-width:200px;color:#999;}

    .frame table tfoot tr th {color:#993;background-color:#FFFFF0;}
    .frame table tfoot tr td {color:#993;background-color:#FFFFF0;}

    .frame table thead.sticky {position:sticky;top:0;}
    .frame table tfoot.sticky {position:sticky;bottom:0;}
    </style>
</head>
<body>
    <div class="frame">
        <table>
            <thead class="sticky">
                <tr>
                    <th rowspan="4">Summary</th>
                    <th>Head A</th>
                    <th>Head B</th>
                    <th>Head C</th>
                    <th>Head D</th>
                    <th>Head E</th>
                    <th>Head F</th>
                    <th>Head G</th>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th>Head B</th>
                    <th>Head C</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr class="gap">
                    <td colspan="7"></td>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <th rowspan="4">Team 1</th>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <th>Head D</th>
                    <th>Head E</th>
                    <th>Head F</th>
                    <th>Head G</th>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
            </tbody>
            <tbody>
                <tr>
                    <th rowspan="4">Team 2</th>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <th>Head D</th>
                    <th>Head E</th>
                    <th>Head F</th>
                    <th>Head G</th>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
            </tbody>
            <tbody>
                <tr>
                    <th rowspan="4">Team 3</th>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <th>Head D</th>
                    <th>Head E</th>
                    <th>Head F</th>
                    <th>Head G</th>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
            </tbody>
            <tbody>
                <tr>
                    <th rowspan="4">Team 4</th>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <th>Head D</th>
                    <th>Head E</th>
                    <th>Head F</th>
                    <th>Head G</th>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
            </tbody>
            <tbody>
                <tr>
                    <th rowspan="4">Team 5</th>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <th>Head D</th>
                    <th>Head E</th>
                    <th>Head F</th>
                    <th>Head G</th>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
            </tbody>
            <tbody>
                <tr>
                    <th rowspan="4">Team 6</th>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <th>Head D</th>
                    <th>Head E</th>
                    <th>Head F</th>
                    <th>Head G</th>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
                <tr>
                    <th>Head A</th>
                    <th colspan="2">Head B</th>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                    <td>data</td>
                </tr>
            </tbody>
            <tfoot class="sticky">
                <tr class="gap">
                    <td colspan="7"></td>
                </tr>
                <tr>
                    <th colspan="4">Foot</th>
                    <td>f</td>
                    <td>f</td>
                    <td>f</td>
                    <td>f</td>
                </tr>
            </tfoot>
        </table>
    </div>
</body>
</html>
阅读 704
avatarAI BotBETA

为了实现你的需求,我们可以使用 CSS 的 position: sticky; 属性。然而,这个属性并不直接支持跨行或跨列的“粘性”定位,特别是当涉及到复杂的表头和行合并时。但是,我们可以通过一些结构和样式调整来尽量接近你的需求。

1. 垂直滚动时,Team 的表头部分保持在 Summary 的下方

对于 Team 的表头(如“Team 1”),我们可以将这些行包装在一个具有 position: sticky;<thead><div> 内,并为其指定一个适当的 top 值(以 px 为单位),使其在滚动时保持在指定位置。但由于表头通常属于 <thead>,我们需要改变 HTML 结构或添加额外的样式类来应用粘性效果。由于结构限制,我们可能需要用额外的 <div> 或通过 JavaScript 动态生成结构。

:在标准的 HTML 表格中,这通常较难实现,因为 <thead> 只应该包含一行的表头。我们可以通过为 <tbody> 的特定行添加类来实现部分效果,但可能不会完全符合需求。

2. 垂直滚动时,Team Name 保持在同一行位置

这个需求更加复杂,因为 CSS position: sticky; 不支持直接粘在某个行旁边。这可能需要使用 JavaScript 来计算并更新位置,或者设计一个完全不同的布局,如使用两个表格或列表项 (<li>) 和分隔线来模拟。

3. 横向滚动时,保持 Head X 表头冻结

这可以通过在 <thead>th 元素上使用 position: sticky; left: 0; 来实现。注意,这需要表格外层容器(如 .frame)具有滚动属性(overflow-x: auto;)。在你的代码中,由于表头已经是 <thead> 的一部分,并应用了 position: sticky; top: 0;,我们需要调整它以包括横向滚动的情况。

.frame th {
    position: -webkit-sticky; /* Safari */
    position: sticky;
    top: 0; /* 对于垂直滚动 */
    left: 0; /* 对于横向滚动 */
    background-color: #f9f9f9; /* 添加背景色以便更容易看到效果 */
    z-index: 1; /* 提高 z-index 以确保它在内容之上 */
}

/* 仅对顶层表头(Summary 下的)进行调整 */
.frame thead th:first-child {
    z-index: 2; /* 更高的 z-index 确保其不会被下面的表头覆盖 */
}

注意:你可能需要调整 z-index 来处理表头之间的覆盖问题。

4. 尽量使用 CSS,非必要不使用 JS

对于大多数情况,你可以使用上述 CSS 方法来尽可能接近你的需求。然而,对于复杂的行为(如将 Team Name 保持在行旁边),可能需要使用 JavaScript 来精确控制位置和响应滚动事件。

由于直接在你的 HTML 结构中完全实现所有需求可能会受到限制,建议重新考虑你的布局或接受一些折衷方案。如果可能的话,考虑使用 CSS Grid 或 Flexbox 来创建更灵活的布局,这些布局可以更容易地实现复杂的粘性行为。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏