html - 绝对定位于复杂结构
问题描述
我有一个非常复杂的 UI 控件需要开发。左半部分是一个 TreeTable 组件,由一个<table>
元素组成。右半部分是一个SVG
组件,用于显示左 treeTable 的每一行的数据。双方需要根据要求相对独立,但需要在同步滚动等方面表现得像一个人。
因此,整体结构看起来像这样
<div>
<div id="leftComponent">
<table>...</table
</div>
<div id="rightComponent">
<svg>...</svg>
</div>
</div>
我为超过 1m 行的大型数据集添加了性能优化,这样只有任一侧视口中的行才会被实际渲染并在 DOM 中。
这会导致以下问题:
- 当我在右侧滚动时,元素是绝对定位的,因此我可以使用原生平滑滚动。例如,一行的一半可能在视口顶部可见,然后是所有其他行,然后另一半行在视口底部可见。如何以某种方式偏移 leftComponent 表格内容,以便表格的行再次与右侧的行匹配?'s上的绝对定位
<tr>
不起作用。我还实现了光栅化滚动,以便用户只能滚动 rowHeight 的倍数,但这个想法很快就被否决了。<table>
用类似的东西偏移整个元素<table style="margin-top:1337px">
也不起作用,因为<table>
标题不会在顶部粘住。有没有更好的方法? - 由于两边都是相对独立的,所以每一边都有自己的滚动条。leftComponent 的 y 轴滚动条是不需要的,但它会干扰 UI 的整体感觉。简单地将其设置为
overflow-y: hidden
不起作用,因为现在我无法在左侧滚动。我对此的唯一想法是从中人工捕获并为 rightComponentWheelEvent
创建一个。ScrollEvent
我也在scrollbar-width: none;
CSS 中玩过,但这会禁用两个滚动条。x 轴滚动条需要在视觉上和功能上保持不变,只是 y 轴滚动条需要在视觉上保持不变,但在功能上仍然保持不变。我还尝试用 隐藏 y 轴滚动条margin-right: -20px
,这确实有效,但对我来说似乎非常 hacky。
总结一下:
- 双方相对独立,需要保持这种状态,但需要表现得“同步”
- 两个组件之间不应该有视觉上的 y 轴滚动条,但我仍然需要能够在左侧组件上滚动
- 我还需要能够将左侧
<table>
部分滚动/偏移任意数量的像素以匹配可以自由滚动的右侧。 - 如果可能的话,应该避免完全重写左侧并将
<table>
结构转换为<div>
可以绝对定位的结构,因为这将是很多工作。 - 我仍然需要能够对 leftComponent 应用我的性能改进,以便仅渲染视口中的 X 行,但在正确的 y 位置与右侧保持同步。
关于这个有什么好主意吗?
解释。在第一张图片中,您可以看到所有内容都滚动到顶部。每侧的两行都完美匹配。在第二张图片中,您会看到我所说的将行向下滚动一半的意思。最上面的行只有几个像素可见。自然地,我希望它看起来像第一张图片,两行都完美对齐。但是,由于左侧组件只有一个<table>
元素,一旦我的性能优化处于活动状态,我无法将<tr>
元素向下定位几个像素以使其与右侧匹配。此外,中间的滚动条需要移动,如上所述,但 x 轴滚动仍然需要在左右组件中都处于活动状态。
如果您需要更多信息,请询问我。
解决方案
这是一个纯 JS 模型,使用一种scrollTop
方法,正如我之前建议的那样,结合行上的相对定位。
我认为这是合理的自我解释。如果您有任何问题,请告诉我。
const leftComponent = document.getElementById("leftComponent");
const rightComponent = document.getElementById("rightComponent");
const svg = rightComponent.querySelector("svg");
const theadHeight = leftComponent.querySelector("thead").offsetHeight;
const tbodyRowHeight = leftComponent.querySelector("tbody tr").offsetHeight;
const numRows = 100; // Number of rows in th table
document.documentElement.style.setProperty('--table-height', (theadHeight + numRows * tbodyRowHeight) + 'px');
// Capture rightComponent scroll events
rightComponent.addEventListener('scroll', evt => {
// Calculate top row index number based on scroll offset and row height
const topRowIndex = Math.floor(rightComponent.scrollTop / tbodyRowHeight);
// Work out how many pixels into the top row that the scroll position is
const scrollFractionalAdjustment = - (rightComponent.scrollTop % tbodyRowHeight);
// Set the position relative top value for the table rows.
// This will adjust their vertical position on the page based on scroll fractional offset we just calculated.
document.documentElement.style.setProperty('--scroll-fractional-adjustment', scrollFractionalAdjustment + 'px');
// Update the table and graph based on out top row index
populateTableRows(topRowIndex);
populateGraph(topRowIndex);
});
function populateTableRows(topRowIndex)
{
let n = topRowIndex;
const rows = leftComponent.querySelectorAll("tbody tr");
rows.forEach((row, i) => {
const cols = row.querySelectorAll("td");
cols[0].textContent = "row" + n + " with very long text";
cols[1].textContent = "row" + n;
n++;
});
}
function populateGraph(topRowIndex)
{
}
// Initial setup
populateTableRows(0);
populateGraph(0);
:root {
--scroll-fractional-adjustment: 0px;
--table-height: 10000px;
}
html, body {
margin: 0;
}
body {
font-family: sans-serif;
font-size: 20px;
}
th, td {
height: 36px;
line-height: 36px;
padding-top: 0;
padding-bottom: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
th {
background-color: #aaa;
color: white;
}
.system {
display: flex;
width: 100%;
height: 100vh;
overflow: hidden;
}
#leftComponent.
#rightComponent {
height: var(--table-height);
}
#leftComponent {
width: 30%;
height: 100%;
overflow: scroll hidden;
}
#leftComponent tbody {
display: block;
overflow: hidden;
}
#leftComponent tbody td {
position: relative;
top: var(--scroll-fractional-adjustment);
}
#leftComponent table th,
#leftComponent table td
{
max-width: 150px;
}
#rightComponent {
width: 70%;
overflow: scroll;
}
#rightComponent .rightContainer {
width: 100%;
height: var(--table-height);
}
#rightComponent svg {
position: fixed;
}
<div class="system">
<div id="leftComponent">
<table>
<thead>
<tr>
<th>Name</th>
<th>ID Blub</th>
<th>Foo</th>
<th>Sort string</th>
</tr>
</thead>
<tbody>
<tr>
<td>row0 with very long text</td>
<td>row0</td>
<td>Foo something</td>
<td>r2u58</td>
<tr>
<td>row0 with very long text</td>
<td>row0</td>
<td>Foo something</td>
<td>r2u58</td>
</tr>
<tr>
<td>row0 with very long text</td>
<td>row0</td>
<td>Foo something</td>
<td>r2u58</td>
</tr>
<tr>
<td>row0 with very long text</td>
<td>row0</td>
<td>Foo something</td>
<td>r2u58</td>
</tr>
<tr>
<td>row0 with very long text</td>
<td>row0</td>
<td>Foo something</td>
<td>r2u58</td>
</tr>
</tbody>
</table>
</div>
<div id="rightComponent">
<div class="rightContainer">
<svg>
<circle cx="50" cy="50" r="50"/>
</svg>
</div>
</div>
</div>
推荐阅读
- javascript - 基本的 JavaScript 按钮交互
- java - 提供者 org.eclipse.jetty.apache.jsp.JuliLog 不是子类型
- php - 如何创建一个将返回数组或单个项目的类?
- amazon-web-services - AWS IAM - 如何提供对账单的访问权限?
- php - mysqli 多查询不工作
- sql - 匹配没有身份证V2的人
- python - OSError: dlopen() 加载库失败:cairo / cairo-2 / cairo-gobject-2
- python - 图形标签和刻度后面的黑色背景,仅在保存图形后而不是在 Python 交互视图中(具有 Jupyter 功能的 VS 代码)?
- javascript - Highcharts 在全屏上变平
- scala - 在 GraphX 中计算图形直径的正确方法是什么