我有一个包含不同类别的表格。每个类别都有一个标题,我希望在滚动时将该标题的位置固定在表格的顶部。
简单示例:如果我看到 table 上有汽车,我希望 thead
说“Cars”。如果我继续滚动并开始看到摩托车,我希望它显示“摩托车”。
这是我的代码的简化版本的片段,用于表示上面的示例:
//start scrolling
$('tbody').scroll(function() {
doStuff();
});
function doStuff() {
$('.title').each(function() {
//Set vars
var $this = $(this);
var childPos = $this.position();
var parentPos = $('tbody').position();
var currentTop = childPos.top - parentPos.top;
//Listener
if (currentTop <= 0) {
$this.addClass('active')
$('thead').find('.active').remove();
$('.active').last().clone().appendTo($('thead'));
} else {
$this.removeClass('active');
}
});
}
.table-wrapper {
height: 180px;
overflow: hidden;
}
table {
font: arial;
text-align: left;
width: 300px;
}
tbody {
display: block;
height: 80px;
overflow: auto;
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th colspan="2">Vehicles</th>
</tr>
</thead>
<tbody>
<tr>
<th colspan="2" class="title">Cars</th>
</tr>
<tr>
<td>Opel</td>
<td>Corsa</td>
</tr>
<tr>
<td>Renault</td>
<td>Clio</td>
</tr>
<tr>
<td>Citroen</td>
<td>AX</td>
</tr>
<tr>
<th colspan="2" class="title">Motorbikes</th>
</tr>
<tr>
<td>Honda</td>
<td>Hornet</td>
</tr>
<tr>
<td>Yamaha</td>
<td>Virago</td>
</tr>
<tr>
<td>Suzuki</td>
<td>GSXR</td>
</tr>
<tr>
<th colspan="2" class="title">Planes</th>
</tr>
<tr>
<td>Boeing</td>
<td>737</td>
</tr>
<tr>
<td>Boeing</td>
<td>767</td>
</tr>
<tr>
<td>Boeing</td>
<td>777</td>
</tr>
</tbody>
</table>
</div>
此外,here's a jsfiddle .
当以这种方式做事时,每次滚动移动时我都会调用我的整个函数,所以正如我所预料的那样性能很差。
如何优化我的代码以使用滚动条运行?我见过的所有解决方案都适用于 setTimeout
,但我不想在 250 毫秒不滚动后才运行我的函数,因为用户可能会滚动一段时间并且 thead
将不正确。
请记住,我的表中可以有数千行。谢谢。
最佳答案
如果您不想设置超时来延迟 .scroll()
调用,您的选择仅限于优化您的 .scroll()
事件尽可能快。
我在这里写了一个替代方法,但我没有在大表上测试过它:https://jsfiddle.net/sduknuzw/69/
您会注意到我将所有昂贵的计算都移到了循环之外,并在初始加载时处理它们。这让我可以简单地处理 .scroll()
事件并只处理一次昂贵的计算。
//initialize headers
var headers = [];
$('.title').each(function(index) {
var header = {
thead: $(this),
top: $(this).parent().index() * $(this).outerHeight(),
bottom: 0
};
headers.push(header);
});
//calculate header container heights
for (var i = 0; i < headers.length; ++i) {
headers[i].bottom = (i+1 < headers.length) ? headers[i+1].top : 100000;
}
在上面的 header 代码中,我构建了一个 header 对象,其中顶部/底部是一个假想容器的位置值。稍后使用此容器来找出滚动条在哪个部分。
我会认为这部分代码有点老套。我不喜欢使用 CSS 值进行计算,因为它们在不同浏览器中并不总是相同的。此外,此代码不适应具有不同高度的行/标题,因此如果不根据您的需要对其进行修改,它可能无法工作。但它的效果足以证明我正在努力实现的目标。
为了更好地理解数学和我想要完成的事情,想象一下这个简化的场景:
<style>
table tr td {
height:20px
}
</style
<table>
<tbody>
<tr><th colspan="2" class="title">Cars</th></tr> //Row index 0
<tr><td>1</td></tr>
<tr><th colspan="2" class="title">Motorbikes</th></tr> //Row index 2
<tr><td>2</td></tr>
<tr><td>3</td></tr>
<tr><th colspan="2" class="title">Planes</th></tr> //Row index 5
<tr><td>4</td></tr>
</tbody>
</table>
“top”值是行索引乘以每行的高度。 “底部”值基本上只是下一个 header 的“顶部”值。
对于我们的小示例, header 容器值将是这样的:
Car {
top: 0,
bottom: 40
}
Motorbikes {
top: 40, // Row Index * Row Height
bottom: 100 // Next Headers Top
}
Planes {
top: 100,
bottom: 140
}
从视觉上看,它看起来像这样:
------------------- top: 0
| th | Cars |
| td | 1 |
------------------- top/bottom: 40
| th | Motors |
| td | 2 |
| td | 3 |
------------------- top/bottom: 100
| th | Planes |
| td | 4 |
------------------- bottom: 140
代码的最后一部分是 scroll
循环本身。由于我在上面所做的额外工作,我们不必遍历每个滚动条上的所有 .title
对象。该解决方案将开销减少为几个应该相对便宜的数值比较。
var headerIndex = 0;
function doStuff() {
var scroll = $('tbody').scrollTop();
if (scroll >= headers[headerIndex].top && scroll < headers[headerIndex].bottom) {
return true;
}
else {
//get new headerindex and update header
headers[headerIndex].thead.removeClass('active');
headerIndex = (scroll < headers[headerIndex].top) ? headerIndex-1 : headerIndex+1;
headers[headerIndex].thead.addClass('active');
$('thead').find('.active').remove();
$('.active').last().clone().appendTo($('thead'));
}
}
它的代码比你拥有的要多得多,并且有一定的改进空间,但我会尝试这样的事情,看看你是否看到任何明显的性能提升。
关于javascript - jQuery:在滚动时修复表行 - 性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36775523/