如何在悬停行号显示面板时同步两个容器的滚动
如何在悬停行号显示面板时同步两个容器的滚动
首先,问题的核心矛盾在于:当我们给#line-numbers设置overflow: visible时,虽然能让悬停面板完整显示,但该容器不再支持通过scrollTop进行滚动同步(因为overflow: visible下容器本身不会产生滚动行为,scrollTop的设置无法影响内容位置);而overflow-y: hidden虽然能保证滚动同步,但会裁剪掉超出容器范围的面板内容。
解决思路是将悬停面板从行号容器中移出,改为通过绝对定位动态显示在父容器内,这样既保留行号容器的overflow-y: hidden以维持滚动同步,又能让面板完整显示。下面是具体实现步骤:
步骤1:修改Svelte组件模板
移除行号内部的panel元素,添加一个全局的面板到text-area-container中:
<div id="text-area-container"> <!-- 全局悬停面板 --> {#if hoveredLineData} <div class="panel" style="top: {hoveredLineData.top}px; left: {hoveredLineData.left}px;"> <div>Min: {hoveredLineData.exeTime?.minExeTime || 0} ms</div> <div>Avg: {hoveredLineData.exeTime?.avgExeTime || 0} ms</div> <div>Max: {hoveredLineData.exeTime?.maxExeTime || 0} ms</div> <div class="panel-icons"> <button on:click={handleToggleDebug}></button> <button on:click={handleShowLogs}></button> </div> </div> {/if} <div id="line-numbers" bind:this={lineNumbersContainer} on:scroll={preventLineNumberScroll}> {#each lineNumbers as line, index} {#if line !== null} <div class:highlighted={highlightedLines.includes(String(line.lineNumber))} class={line.className} on:mouseenter={(e) => showPanel(e, line)} on:mouseleave={() => hidePanel()} > {line.lineNumber} </div> {:else} <div> </div> {/if} {/each} </div> <!-- 其他元素保持不变 --> <div id="highlighted-text" bind:this={highlightedTextContainer}></div> <textarea id="text-area" bind:this={textarea} bind:value={text} {placeholder} {disabled} on:keyup={handleKeyUp} on:keydown={handleKeyDown} on:mouseup={handleClick} on:scroll={syncScroll} /> </div>
步骤2:添加面板控制逻辑
在script部分新增变量和函数,用于控制面板的显示、隐藏和位置计算:
// 新增变量:存储悬停行的数据和位置 let hoveredLineData = null; // 显示面板:计算行号元素位置并填充数据 function showPanel(event, line) { const lineElement = event.target; const containerRect = lineNumbersContainer.getBoundingClientRect(); const lineRect = lineElement.getBoundingClientRect(); hoveredLineData = { ...line, top: lineRect.top - containerRect.top, // 相对于行号容器的顶部偏移 left: containerRect.width // 面板显示在行号容器的右侧 }; } // 隐藏面板 function hidePanel() { hoveredLineData = null; }
步骤3:调整CSS样式
恢复行号容器的overflow-y: hidden设置,并更新全局面板的样式:
/* 恢复行号容器的overflow设置,保证滚动同步 */ #line-numbers { position: relative; width: 40px; padding: 10px 5px; text-align: right; border-right: 1px solid #ccc; background-color: #f0f0f0; user-select: none; overflow-y: hidden; /* 关键:维持滚动同步能力 */ height: 100%; z-index: 3; } /* 全局面板样式 */ .panel { position: absolute; background-color: white; border: 1px solid #ccc; padding: 10px; border-radius: 4px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); z-index: 10; /* 确保面板在最上层不被遮挡 */ white-space: nowrap; /* 避免内容换行导致面板变形 */ }
方案生效原因
- 行号容器保持
overflow-y: hidden,因此我们仍然可以通过设置scrollTop来同步滚动位置,解决了滚动不同步的问题。 - 悬停面板不再是行号容器的子元素,而是通过绝对定位直接放在父容器中,不会被行号容器的overflow属性裁剪,同时可以根据行号的位置动态调整自身位置。
- 面板的z-index设置为10,确保它显示在所有其他元素之上,不会被遮挡。
备注:内容来源于stack exchange,提问作者Amit Kumar Gupta




