【问题标题】:HTML table with fixed (frozen) columns and headers具有固定(冻结)列和标题的 HTML 表格
【发布时间】:2017-06-19 16:28:14
【问题描述】:

我一直在网上搜索制作具有固定(冻结)列和标题的表格的方法。 似乎我终于找到了解决方案并根据我的需要进行了修改。

原来的小提琴是here

Here 是我修改后的解决方案。我在 Chrome(版本:55.0.2883.87 m)和 Firefox(版本:51.0.1)中对其进行了测试。

问题是它在 IE(版本:11.0.9600.18427)中不能完全运行。在水平滚动期间,标题的冻结部分也会滚动。有人可以帮我让它在 IE 中工作吗? 还有一个问题:这种方法使用安全吗?我的意思是,如果它使用了一些未指定的行为,那么未来的一些浏览器甚至一些现代浏览器可能会以错误的方式显示我的表格,最好使用带有几个不同表格并同步滚动位置和行的安全解决方案高度。 UPD:还有一个问题:如何让这项工作在移动设备上稳定运行?

下面是一些演示该方法的代码:

$(document).ready(function() {
  $('tbody').scroll(function(e) { //detect a scroll event on the tbody
  	/*
    Setting the thead left value to the negative valule of tbody.scrollLeft will make it track the movement
    of the tbody element. Setting an elements left value to that of the tbody.scrollLeft left makes it maintain 			it's relative position at the left of the table.    
    */
    $('thead').css("left", -$("tbody").scrollLeft()); //fix the thead relative to the body scrolling
    $('thead th:nth-child(1)').css("left", $("tbody").scrollLeft()); //fix the first cell of the header
    $('tbody td:nth-child(1)').css("left", $("tbody").scrollLeft()); //fix the first column of tdbody
  });
});
.container {
  height:200px; 
  width:400px;
  overflow: hidden;
}

table {
  position: relative;
  background-color: #aaa;
  border-collapse: collapse;
table-layout: fixed;
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}


/*thead*/
thead {
  position: relative;
  display: block; /*seperates the header from the body allowing it to be positioned*/
}

thead th {
  background-color: #99a;
  min-width: 120px;
  border: 1px solid #222;
}

thead th:nth-child(1) {/*first cell in the header*/
  position: relative;
  background-color: #88b;
}


/*tbody*/
tbody {
  flex: 1;
  position: relative;
  display: block; /*seperates the tbody from the header*/
  overflow: auto;
}

tbody td {
  background-color: #bbc;
  min-width: 120px;
  border: 1px solid #222;
}

tbody tr td:nth-child(1) {  /*the first cell in each tr*/
  position: relative;
  background-color: #99a;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="container">

  <table>
    <thead>
      <tr>
        <th>Name<br/>123</th>
        <th>Town</th>
        <th>County</th>
        <th>Age</th>
        <th>Profession</th>
        <th>Anual Income</th>
        <th>Matital Status</th>
        <th>Children</th>
      </tr>
       <tr>
        <th>Name</th>
        <th>Town</th>
        <th>County</th>
        <th>Age<br/>123<br/>321</th>
        <th>Profession</th>
        <th>Anual Income</th>
        <th>Matital Status</th>
        <th>Children</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>John Smith</td>
        <td>Macelsfield</td>
        <td>Cheshire<br/>123</td>
        <td>52</td>
        <td>Brewer</td>
        <td>£47,000</td>
        <td>Married</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Jenny Jones<br/>123<br/>312</td>
        <td>Threlkeld</td>
        <td>Cumbria</td>
        <td>34</td>
        <td>Shepherdess</td>
        <td>£28,000</td>
        <td>Single</td>
        <td>0</td>
      </tr>
      <tr>
        <td>Peter Frampton</td>
        <td>Avebury</td>
        <td>Wiltshire</td>
        <td>57</td>
        <td>Musician</td>
        <td>£124,000</td>
        <td>Married</td>
        <td>4</td>
      </tr>
      <tr>
        <td>Simon King</td>
        <td>Malvern</td>
        <td>Worchestershire</td>
        <td>48</td>
        <td>Naturalist</td>
        <td>£65,000</td>
        <td>Married</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Lucy Diamond</td>
        <td>St Albans</td>
        <td>Hertfordshire</td>
        <td>67</td>
        <td>Pharmasist</td>
        <td>Retired</td>
        <td>Married</td>
        <td>3</td>
      </tr>
      <tr>
        <td>Austin Stevenson</td>
        <td>Edinburgh</td>
        <td>Lothian </td>
        <td>36</td>
        <td>Vigilante</td>
        <td>£86,000</td>
        <td>Single</td>
        <td>Unknown</td>
      </tr>
      <tr>
        <td>Wilma Rubble</td>
        <td>Bedford</td>
        <td>Bedfordshire</td>
        <td>43</td>
        <td>Housewife</td>
        <td>N/A</td>
        <td>Married</td>
        <td>1</td>
      </tr>
      <tr>
        <td>Kat Dibble</td>
        <td>Manhattan</td>
        <td>New York</td>
        <td>55</td>
        <td>Policewoman</td>
        <td>$36,000</td>
        <td>Single</td>
        <td>1</td>
      </tr>
      <tr>
        <td>Henry Bolingbroke</td>
        <td>Bolingbroke</td>
        <td>Lincolnshire</td>
        <td>45</td>
        <td>Landowner</td>
        <td>Lots</td>
        <td>Married</td>
        <td>6</td>
      </tr>
      <tr>
        <td>Alan Brisingamen</td>
        <td>Alderley</td>
        <td>Cheshire</td>
        <td>352</td>
        <td>Arcanist</td>
        <td>A pile of gems</td>
        <td>Single</td>
        <td>0</td>
      </tr>
    </tbody>
  </table>
  
</div>
</body>

【问题讨论】:

标签: javascript html css


【解决方案1】:

问题在于 IE 不允许独立于整个行来调整行中单元格的left 属性。我们可以通过直接使用 IE 中的开发者窗口和 Chrome 中的开发者窗口编辑 DOM 来看到这一点。

在 Chrome 中,当您左右滚动时,您可以在元素查看器中看到 left 属性在元素本身上发生了更改,这会覆盖所有 CSS。我们可以在同一屏幕中重新加载页面并手动设置元素属性:style:'left:300px',我们将看到标题单元格向右移动 300 像素并悬停在剩余的标题单元格上。这是良好的行为,也是使此方法有效的行为。

如果我们在 IE 中做同样的事情并将style: 'left:300px' 添加到第 th 元素,我们将看到单元格没有移动。我们对那个单元格的样式属性所做的任何事情都不会导致它离开它在表格中的位置。正是 IE 的这个“功能”导致该方法失败。无论对一行中的元素应用什么属性,IE 都坚持保持单元格顺序。

诀窍是以一种让所有浏览器都满意的方式来解决这个复杂问题。有很多方法可以做到这一点,但我可能会使用两个表,并使用 DIV 来定位它们以使边缘匹配。我会添加 javascript,以便如果一个 tbody 向上或向下滚动,它会以相同的方式影响另一个 tbody。如果它向右或向左滚动,第一个表格不会发生任何事情,它包含您的冻结列标题,并且右侧表格按计划沿滚动方向移动。

通过使用两个表,IE 不再将您尝试冻结的标题与正在移动的标题相关联。小心的 CSS 会伪装你的 hack 并使表格显示为一个表格。

祝你好运,编码愉快!

【讨论】:

  • 谢谢你,杰瑞德。其实我目前正在使用您描述的方法。但它的问题是必须同步固定表和滚动表中的行高。如果表格非常动态,可能会很烦人:如果您正在按需加载记录,用户是否可以编辑单元格内容,数据是否可以动态更改等等。我也将标题放在单独的 div 中,所以在 javascript 中我必须执行一些任务:同步水平滚动、垂直滚动、行高。我的梦想是把它全部做成一张桌子。
  • 这是一个可以理解的美好梦想。遗憾的是,IE 让每个人的生活都变得如此复杂。您可能会遇到有创意的替代方案。例如,也许不是滚动 div,而是可以将滚动事件转换为在表中显示的不同数据。 Javascript 可用于动态更改单元格值,使其看起来像是在离散度量中发生滚动动作,但实际上,单元格中的数据正在被更改。
【解决方案2】:

您应该参考 jquery.floatThead.js 尝试下面的代码示例。

                    var $demoTable = $("div.table-responsive table");
                    $demoTable.floatThead({
                        top: 200,
                        scrollContainer: function ($table) {                                
                            return $table.closest('.table-responsive');
                        },
                        position: 'absolute'
                    });

您需要获取 jquery.floatThead.js 文件的引用并尝试将其应用于表格。

您可以在下面的链接上检查此工作。 http://mkoryak.github.io/floatThead/

【讨论】:

  • 这很好,但它只是关于表头,它实际上将表头克隆到另一个表并绝对定位它。
  • 它不会克隆表头,而是将其移动到另一个表中(并根据定位选项返回)。但是,是的,它对列冻结没有任何作用
【解决方案3】:

通常对于冻结的行和列,我总是更喜欢使用纯 CSS 解决方案以获得最佳的浏览器兼容性。

我已尝试使用纯 CSS 解决方案在此处复制您的代码。

我正在使用 Mac,因此无法访问 IE。请验证它是否可以正常工作。

更新小提琴:https://jsfiddle.net/nashcheez/bzuasLcz/81/

参考代码:

table {
  position: relative;
  width: 700px;
  background-color: #aaa;
  overflow: hidden;
  border-collapse: collapse;
}
/*thead*/

thead {
  position: relative;
  display: block;
  /*seperates the header from the body allowing it to be positioned*/
  width: 700px;
  overflow: visible;
}
thead th {
  background-color: #99a;
  min-width: 120px;
  height: 36px;
  min-height: 36px;
  border: 1px solid #222;
}
thead th:nth-child(1) {
  /*first cell in the header*/
  position: relative;
  display: block;
  background-color: #88b;
}
tbody tr td:nth-child(2) {
  margin-left: 124px;
  display: block;
}
/*tbody*/

tbody {
  display: block;
  width: 700px;
  height: 239px;
  overflow-y: auto;
}
tbody td {
  background-color: #bbc;
  min-width: 120px;
  border: 1px solid #222;
  height: 36px;
  min-height: 36px;
}
tbody tr td:nth-child(1) {
  /*the first cell in each tr*/
  position: absolute;
  display: inline-block;
  background-color: #99a;
}
<body>
  <table>
    <thead>
      <tr>
        <th>Name</th>
        <th>Town</th>
        <th>County</th>
        <th>Age</th>
        <th>Profession</th>
        <th>Anual Income</th>
        <th>Matital Status</th>
        <th>Children</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>John Smith</td>
        <td>Macelsfield</td>
        <td>Cheshire</td>
        <td>52</td>
        <td>Brewer</td>
        <td>£47,000</td>
        <td>Married</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Jenny Jones</td>
        <td>Threlkeld</td>
        <td>Cumbria</td>
        <td>34</td>
        <td>Shepherdess</td>
        <td>£28,000</td>
        <td>Single</td>
        <td>0</td>
      </tr>
      <tr>
        <td>Peter Frampton</td>
        <td>Avebury</td>
        <td>Wiltshire</td>
        <td>57</td>
        <td>Musician</td>
        <td>£124,000</td>
        <td>Married</td>
        <td>4</td>
      </tr>
      <tr>
        <td>Simon King</td>
        <td>Malvern</td>
        <td>Worchestershire</td>
        <td>48</td>
        <td>Naturalist</td>
        <td>£65,000</td>
        <td>Married</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Lucy Diamond</td>
        <td>St Albans</td>
        <td>Hertfordshire</td>
        <td>67</td>
        <td>Pharmasist</td>
        <td>Retired</td>
        <td>Married</td>
        <td>3</td>
      </tr>
      <tr>
        <td>Austin Stevenson</td>
        <td>Edinburgh</td>
        <td>Lothian</td>
        <td>36</td>
        <td>Vigilante</td>
        <td>£86,000</td>
        <td>Single</td>
        <td>Unknown</td>
      </tr>
      <tr>
        <td>Wilma Rubble</td>
        <td>Bedford</td>
        <td>Bedfordshire</td>
        <td>43</td>
        <td>Housewife</td>
        <td>N/A</td>
        <td>Married</td>
        <td>1</td>
      </tr>
      <tr>
        <td>Kat Dibble</td>
        <td>Manhattan</td>
        <td>New York</td>
        <td>55</td>
        <td>Policewoman</td>
        <td>$36,000</td>
        <td>Single</td>
        <td>1</td>
      </tr>
      <tr>
        <td>Henry Bolingbroke</td>
        <td>Bolingbroke</td>
        <td>Lincolnshire</td>
        <td>45</td>
        <td>Landowner</td>
        <td>Lots</td>
        <td>Married</td>
        <td>6</td>
      </tr>
      <tr>
        <td>Alan Brisingamen</td>
        <td>Alderley</td>
        <td>Cheshire</td>
        <td>352</td>
        <td>Arcanist</td>
        <td>A pile of gems</td>
        <td>Single</td>
        <td>0</td>
      </tr>
    </tbody>
  </table>
</body>

【讨论】:

【解决方案4】:

这很奇怪。看来有问题的代码是这一行:

$('thead').css("left", -$("tbody").scrollLeft()); //fix the thead relative to the body scrolling

看起来 IE11 以不同方式处理嵌套元素的相对定位(与 Chrome 和其他浏览器不同)。在这种情况下,您使用带有偏移量的相对定位来定位 thead。您还使用偏移和相对定位定位thead th(它是孩子)。 Chrome 似乎将thead 相对于table 定位,然后将th 相对于thead 定位。另一方面,IE11 似乎是相对于table 定位thead,然后th 只是继承了相同的定位,而不管它自己的定位如何。

对此的解决方法如下:在 IE11 上,以不同方式处理 thead 的定位。不要在父thead 上设置定位,而是在thead th 元素上设置定位。这样,您的第一列将不会被“强制”继承thead 的定位(在 IE 中)。

$(document).ready(function() {
  var isIE11 = !!navigator.userAgent.match(/Trident.*rv\:11\./);
  var customScroller;
  if (isIE11)
    customScroller = function() {
      $('thead th').css("left", -$("tbody").scrollLeft()); //if using IE11, fix the th element 
    };
  else
    customScroller = function() {
      $('thead').css("left", -$("tbody").scrollLeft()); //if not using IE11, fix the thead element
    };

  $('tbody').scroll(function(e) { //detect a scroll event on the tbody
    /*
    Setting the thead left value to the negative valule of tbody.scrollLeft will make it track the movement
    of the tbody element. Setting an elements left value to that of the tbody.scrollLeft left makes it maintain             it's relative position at the left of the table.    
    */
    customScroller(); //fix the thead relative to the body scrolling
    $('thead th:nth-child(1)').css("left", $("tbody").scrollLeft());
//fix the first cell of the header
    $('tbody td:nth-child(1)').css("left", $("tbody").scrollLeft()); //fix the first column of tdbody
  });
});

这是一个完整的代码示例,显示了基于浏览器的不同处理方式:

https://jsfiddle.net/8tx4xfhx/5/

另外,很高兴看看是否有人以前注意到过这种行为。看来 IE11 处理嵌套相对定位的方式与其他浏览器不同。是否有规范定义标准应该是什么?是否应该像 IE 一样继承相对定位?还是应该相对定位总是相对于父级?我会认为是后者。但也必须考虑性能。

【讨论】:

  • 谢谢你,马特。你能谈谈问题的第二部分:使用这样的解决方案是否安全?它将display: flex 用于&lt;table&gt; 等等。恐怕它包含一些未指定的行为,并且该解决方案不适用于某些浏览器。
  • 您好,Artem。实际上,我只将display:flex 留在了那里,因为我认为您可能会将其用于其他用途。它可以在没有该属性的情况下完成。在这里,检查一下:jsfiddle.net/mspinks/wLren00o/2。这利用了外部容器滚动条。我认为这是一个更好、更可靠的解决方案。而且它不使用任何flex
  • 谢谢。 display:blocktheadtbody 怎么样?我尝试了这个解决方案,它运行良好,即使在 IE 中也是如此。但是在移动浏览器上滚动不是那么流畅,可以看到冻结的部分并没有真正停留在同一个地方。有什么关系吗?
  • 我认为display:block; 对于theadtbody 是安全的。就平滑度而言,这可能是移动浏览器中重新定位的滞后。您可能会在这样的任何解决方案中发现这一点。
猜你喜欢
  • 2010-10-15
  • 2012-01-27
  • 1970-01-01
  • 2012-01-15
  • 2023-01-10
  • 1970-01-01
  • 1970-01-01
  • 2014-02-22
相关资源
最近更新 更多