具有固定列和标题的可滚动表格,带有现代 CSS

Posted

技术标签:

【中文标题】具有固定列和标题的可滚动表格,带有现代 CSS【英文标题】:Scrollable table with fixed columns and header, with modern CSS 【发布时间】:2018-02-26 18:28:58 【问题描述】:

如何使用现代 CSS 制作尽可能少的 javascript 表格?

我想要的功能是:

固定列(定位和宽度) 可在 X 和 Y 轴上滚动 在 X 轴上响应(对于非固定宽度的列)。

注意:我确实看到并分析了 SO 中一些最受欢迎/看到的问题和答案,例如 here 和 here。

我知道这可以通过 JavaScript 滚动事件处理程序来完成,以便可以向下/向上移动固定列以跟随主列。我正在尝试构建的功能示例(但编写了大量脚本)could be like this。我们还可以在表的父元素中添加MutationObserver,以检测大小变化并再次计算表的大小。但这在性能上很昂贵,而且由于 CSS 也在不断发展和现代化,我想知道是否有新的方法......

是否有 2017/2018 解决方案仅使用 CSS 或尽可能少的 JS 开销?

我希望避免实现像这样容易崩溃的 JavaScript 解决方案:

我的想法:

a):在固定列中使用position: fixed;

问题:

<td> 元素高度必须由 JavaScript 定义或计算。 需要一个滚动事件侦听器,我们需要以编程方式滚动固定列

b):使用 div(s) 来“伪造”左列并在表格中显示可滚动的内容

问题:

div 高度必须与表格行高度同步 html 语义丢失,因为固定的“假”列不再是表格语法

c):使用 N 个表格只显示每个表格的一部分,这样我就可以有 HTML 标记,但保持固定的标题/列。

问题:

重复的 HTML x N 还必须使用 JavaScript 同步大小和滚动

使用滚动事件监听器和固定的左列,我们可以像这样使用 JavaScript:

const firstColumn = [...document.querySelectorAll('table tr > *:first-of-type')];
const tableContainer = document.querySelector('.table-container');
tableContainer.addEventListener('scroll', function() 
    const currentScrollPosition = this.scrollTop * -1;
	firstColumn.forEach(el => el.style.marginTop = currentScrollPosition + 'px');
);
.table-container 
    width: 600px;
    height: 300px;
    overflow-x: scroll;
    overflow-y: scroll;


.table-scroller 
    width: 1000px;


table 
    width: 100%;
    margin-left: -13px;
    table-layout: fixed;
    border-collapse: collapse;
    border: none;


table th,
table td 
    padding: 0.8em;
    border: 1px solid;
    background-color: #eeeeef;


table th 
    background-color: #6699FF;
    font-weight: bold;


table tr 
    height: 60px;
    vertical-align: middle;


table tr > *:first-of-type 
    position: fixed;
    width: 50px;
    margin-left: 13px;
    margin-top: 0;
    height: inherit;
<div class="table-container">
    <div class="table-scroller">
        <table>
            <tbody>
                <tr>
                    <td>Edrward 0</td>
                    <td>32</td>
                    <td>London Park no. 0</td>
                    <td>London Park no. 0</td>
                    <td>London Park no. 0</td>
                    <td>London Park no. 0</td>
                    <td>London Park no. 0</td>
                    <td>London Park no. 0</td>
                    <td>London Park no. 0</td>
                    <td>London Park no. 0</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 1</td>
                    <td>32</td>
                    <td>London Park no. 1</td>
                    <td>London Park no. 1</td>
                    <td>London Park no. 1</td>
                    <td>London Park no. 1</td>
                    <td>London Park no. 1</td>
                    <td>London Park no. 1</td>
                    <td>London Park no. 1</td>
                    <td>London Park no. 1</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 2</td>
                    <td>32</td>
                    <td>London Park no. 2</td>
                    <td>London Park no. 2</td>
                    <td>London Park no. 2</td>
                    <td>London Park no. 2</td>
                    <td>London Park no. 2</td>
                    <td>London Park no. 2</td>
                    <td>London Park no. 2</td>
                    <td>London Park no. 2</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 3</td>
                    <td>32</td>
                    <td>London Park no. 3</td>
                    <td>London Park no. 3</td>
                    <td>London Park no. 3</td>
                    <td>London Park no. 3</td>
                    <td>London Park no. 3</td>
                    <td>London Park no. 3</td>
                    <td>London Park no. 3</td>
                    <td>London Park no. 3</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 4</td>
                    <td>32</td>
                    <td>London Park no. 4</td>
                    <td>London Park no. 4</td>
                    <td>London Park no. 4</td>
                    <td>London Park no. 4</td>
                    <td>London Park no. 4</td>
                    <td>London Park no. 4</td>
                    <td>London Park no. 4</td>
                    <td>London Park no. 4</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 5</td>
                    <td>32</td>
                    <td>London Park no. 5</td>
                    <td>London Park no. 5</td>
                    <td>London Park no. 5</td>
                    <td>London Park no. 5</td>
                    <td>London Park no. 5</td>
                    <td>London Park no. 5</td>
                    <td>London Park no. 5</td>
                    <td>London Park no. 5</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 6</td>
                    <td>32</td>
                    <td>London Park no. 6</td>
                    <td>London Park no. 6</td>
                    <td>London Park no. 6</td>
                    <td>London Park no. 6</td>
                    <td>London Park no. 6</td>
                    <td>London Park no. 6</td>
                    <td>London Park no. 6</td>
                    <td>London Park no. 6</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 7</td>
                    <td>32</td>
                    <td>London Park no. 7</td>
                    <td>London Park no. 7</td>
                    <td>London Park no. 7</td>
                    <td>London Park no. 7</td>
                    <td>London Park no. 7</td>
                    <td>London Park no. 7</td>
                    <td>London Park no. 7</td>
                    <td>London Park no. 7</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 8</td>
                    <td>32</td>
                    <td>London Park no. 8</td>
                    <td>London Park no. 8</td>
                    <td>London Park no. 8</td>
                    <td>London Park no. 8</td>
                    <td>London Park no. 8</td>
                    <td>London Park no. 8</td>
                    <td>London Park no. 8</td>
                    <td>London Park no. 8</td>
                    <td><a href="#">action</a></td>
                </tr>
                <tr>
                    <td>Edrward 9</td>
                    <td>32</td>
                    <td>London Park no. 9</td>
                    <td>London Park no. 9</td>
                    <td>London Park no. 9</td>
                    <td>London Park no. 9</td>
                    <td>London Park no. 9</td>
                    <td>London Park no. 9</td>
                    <td>London Park no. 9</td>
                    <td>London Park no. 9</td>
                    <td><a href="#">action</a></td>
                </tr>
            </tbody>
        </table>
    </div>
</div>

但是我可以不使用 JavaScript 吗?


关于可能的重复建议:

我的问题中提到了可能的重复项,但它没有我要查找的内容。该问题要求“只冻结一个左列” - 这个问题的范围更广泛,它与固定标题、固定列和可滚动正文有关。 另一个问题和接受的答案来自 2009 年!我认为这个话题,我的具体范围和例子值得重新审视。另外:我的“JS 解决方案”只是 JS 如何破坏的一个示例,我正在寻找现代 CSS 技术来做到这一点。

【问题讨论】:

您只想要一个表格,还是可以接受嵌套元素? @klenium 我没有任何愿望,我的想法是让 CSS 进行滚动、调整大小和响应。也许我们还没有 CSS,我们可能需要 JavaScript。如果您有想法,请分享。我命名的问题和链接是一些单表,一些多表。但它们有一些限制和开销,可能在 2018 年我们不再需要 (?)。 您的 JS “解决方案”并不完美,在主页滚动时会中断 how do I create an HTML table with fixed/frozen left column and scrollable body?的可能重复 @ZachSaucier 在我的问题中提到了可能的重复项,但它没有我要查找的内容。该问题要求“只冻结一个左列” - 这个问题的范围更广泛,它与固定标题、固定列s 和可滚动正文有关.另一个问题和接受的答案来自 2009 年!我认为这个话题,我的具体范围和例子值得重新审视。另外:我的“JS 解决方案”只是 JS 如何破坏的一个示例,我正在寻找现代 CSS 技术来做到这一点。 【参考方案1】:

也许您可以尝试使用最近的position:sticky 来实现它。或者,至少,用更少的 javascript 开始方法。

div
  overflow:auto;
  width:100%;
  height:200px;

td,
th 
  border: 1px solid #000;
  width: 100px;

th background-color:red;

table 
  table-layout: fixed;
  width:100%;

td:first-child, th:first-child 
  position:sticky;
  left:0;
  z-index:1;
  background-color:grey;

td:last-child, th:last-child 
  position:sticky;
  right:0;
  z-index:1;
  background-color:blue;

thead tr th 
  position:sticky;
  top:0;

th:first-child, th:last-child z-index:2;background-color:red;
<div>
  <table>
    <thead>
      <tr>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
        <th>&nbsp;&nbsp;&nbsp;</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
      <tr>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
        <td>&nbsp;&nbsp;&nbsp;</td>
      </tr>
    </tbody>
  </table>
</div>

【讨论】:

固定高度是不行的,它应该是桌子所在容器的 100% 高度 添加了固定高度以显示示例中的sticky header,使其尽可能接近提供的gif... 对于那些不知道(就像我不知道)的人,您可以将table-layout: fixed 更改为table-layout: auto 以允许您的列宽自动调整大小。这可能会减慢在非常大的表上渲染的速度,因为浏览器需要等待整个表加载后才能确定正确的列大小。 太棒了。非常紧凑且可读的代码支持一个健壮的示例。 如果有人感兴趣,这里有一支 Codepen Pen。 codepen.io/kavinda1995/pen/OJbXxGw【参考方案2】:

只要看看我的代码有一个固定的标题并修复第一列。

    <style type="text/css">
    .table 
          font-family: arial, sans-serif;
  border-collapse: collapse;


.th 
  width: 126px;
  height: 56px;
  padding: 0px 16px;
  text-align: left;
  background-color: #fafafa;


.td 
  width: 118px;
  min-width: 118px;
  max-width: 118px;
  height: 56px;
  max-height: 56px;
  overflow: hidden;
  text-align: center;
  border-bottom: 2px solid #dddddd;

.pink_color 
  background-color: pink;


.table_wrapper 
  position: relative;

.table_scroll 
  height: 73vh;
  overflow: auto;

.table_wrapper table 
  width: 100%;


.table_wrapper table thead th .text 
  position: absolute;
  z-index: 2;

.tableFixHead
 
  overflow: auto; height: 63vh; 

.tableFixHead thead th 
 
  position: sticky; top: 0; z-index: 1; 



@media only screen and (min-width: 1440px) 
  .table_scroll 
    height: 77vh;
    overflow: auto;
  

</style>

<div style="padding-top: 16px;">
    <div class="table_wrapper">
        <div class="tableFixHead">
            <table class="table">
                <thead>
                  <th style="position: sticky; left: 0; z-index: 3;" class="text th"></th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                  <th class="text th">1</th>
                </thead>
                <tr>
                  <td style="position: sticky; left: 0; z-index: 2; background: #fafafa;" class="td">test</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                </tr>
                <tr>
                  <td style="position: sticky; left: 0; z-index: 2; background: #fafafa;" class="td">test</td>
                  <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                </tr>
                <tr>
                  <td style="position: sticky; left: 0; z-index: 2; background: #fafafa;" class="td">test</td>
                  <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                </tr><tr>
                  <td style="position: sticky; left: 0; z-index: 2; background: #fafafa;" class="td">test</td>
                  <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                </tr><tr>
                  <td style="position: sticky; left: 0; z-index: 2; background: #fafafa;" class="td">test</td>
                  <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                </tr><tr>
                  <td style="position: sticky; left: 0; z-index: 2; background: #fafafa;" class="td">test</td>
                  <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                </tr><tr>
                  <td style="position: sticky; left: 0; z-index: 2; background: #fafafa;" class="td">test</td>
                  <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                    <td class="td">test1</td>
                </tr>
            </table>
        </div>
    </div>
</div>

【讨论】:

以上是关于具有固定列和标题的可滚动表格,带有现代 CSS的主要内容,如果未能解决你的问题,请参考以下文章

如何使用角度和离子制作带有固定标题的可滚动表格

具有固定页眉、第一列和页脚的表格 + 向内滚动

css让表格第一列和第一行固定

具有固定列和流动列的 CSS 布局

使用 Reactstrap 响应式表的带有固定标题的可滚动表

创建带有可滚动标题的表格