在前端开发中,经常会遇到需要在 textarea 左侧添加行号的需求。例如,代码编辑器、日志展示等场景。直接使用原生 HTML 属性无法实现,需要借助 CSS 和 JavaScript 来模拟实现。本文将深入探讨实现原理,并提供多种解决方案,以及实战中的避坑经验。
实现原理深度剖析
实现 textarea 左侧添加行号的核心思路是:创建一个与 textarea 高度和滚动同步的容器,在该容器内生成行号。可以使用两种主要方式:
双层
div模拟:
- 外层
div作为容器,设置overflow: hidden;,内部包含两个div,一个用于显示行号,另一个是textarea。 - 通过 JavaScript 同步两个
div的滚动事件。
- 外层
pre元素与textarea并排:
- 使用
<pre>元素模拟行号显示区域,并与textarea水平排列。 - 监听
textarea的输入事件,动态更新<pre>中的行号。 - 同样需要 JavaScript 同步滚动。
- 使用
这两种方法都涉及 DOM 操作和事件监听,需要注意性能优化,避免频繁操作导致页面卡顿。特别是在处理大量文本时,可以使用虚拟 DOM 或节流/防抖等技术。
代码/配置解决方案
以下提供一个基于双层 div 模拟的解决方案:
HTML 结构
<div class="container">
<div class="line-numbers"></div>
<textarea class="code-input"></textarea>
</div>
CSS 样式
.container {
display: flex;
border: 1px solid #ccc;
overflow: hidden; /* 隐藏滚动条 */
}
.line-numbers {
width: 30px;
background-color: #f0f0f0;
text-align: right;
padding: 5px;
font-size: 14px;
line-height: 1.5;
white-space: pre-wrap; /* 保留空白符和换行 */
user-select: none; /* 禁止选中行号 */
}
.code-input {
flex: 1;
padding: 5px;
font-size: 14px;
line-height: 1.5;
border: none;
outline: none;
resize: none; /* 禁止调整大小 */
overflow: auto; /* 允许内部滚动 */
}
JavaScript 代码
const container = document.querySelector('.container');
const lineNumbers = document.querySelector('.line-numbers');
const codeInput = document.querySelector('.code-input');
// 初始化行号
function updateLineNumbers() {
const lines = codeInput.value.split('\n').length;
let numbers = '';
for (let i = 1; i <= lines; i++) {
numbers += i + '\n';
}
lineNumbers.textContent = numbers;
}
// 同步滚动
container.addEventListener('scroll', () => {
lineNumbers.scrollTop = codeInput.scrollTop; // 或者 container.scrollTop,取决于滚动发生在哪个元素上
});
// 监听输入事件
codeInput.addEventListener('input', updateLineNumbers);
// 初始调用
updateLineNumbers();
使用 pre 元素
<div class="container">
<pre class="line-numbers"></pre>
<textarea class="code-input"></textarea>
</div>
.container {
display: flex;
}
.line-numbers {
width: 30px;
background-color: #f0f0f0;
text-align: right;
padding: 5px;
font-size: 14px;
line-height: 1.5;
white-space: pre-wrap; /* 重要:保留空白和换行 */
overflow: hidden; /* 隐藏滚动条 */
user-select: none; /* 禁止选择行号 */
}
.code-input {
flex: 1;
padding: 5px;
font-size: 14px;
line-height: 1.5;
border: none;
outline: none;
resize: none; /* 禁止调整大小 */
overflow: auto; /* 允许滚动 */
white-space: pre-wrap; /* 重要:保留空白和换行 */
}
const container = document.querySelector('.container');
const lineNumbers = document.querySelector('.line-numbers');
const codeInput = document.querySelector('.code-input');
function updateLineNumbers() {
const lines = codeInput.value.split('\n').length;
let numbers = '';
for (let i = 1; i <= lines; i++) {
numbers += i + '\n';
}
lineNumbers.textContent = numbers;
}
codeInput.addEventListener('input', updateLineNumbers);
container.addEventListener('scroll', () => {
lineNumbers.scrollTop = codeInput.scrollTop;
});
updateLineNumbers();
实战避坑经验总结
- 性能优化:
- 避免频繁 DOM 操作,使用
requestAnimationFrame优化更新频率。 - 对于大型文本,考虑使用虚拟 DOM 或懒加载行号。
- 使用节流或防抖函数处理
scroll和input事件。
- 避免频繁 DOM 操作,使用
- 样式同步:
- 确保
textarea和行号容器的字体、行高、内边距等样式一致,避免出现错位。 - 需要考虑不同浏览器的兼容性问题,特别是滚动条样式。
- 确保
- 空格与换行:
white-space: pre-wrap非常重要,它能让<pre>标签正确显示空格和换行,保证行号与文本内容对齐。
- 文本对齐:
box-sizing: border-box避免因为padding造成的宽度计算问题。
- 滚动同步:
- 使用
container.addEventListener('scroll', ...)监听容器的滚动事件,避免由于浏览器差异导致的滚动条位置不一致问题。
- 使用
通过上述方法,可以有效地在 textarea 左侧添加行号,提升用户体验。在实际应用中,需要根据具体场景选择合适的方案,并注意性能优化和兼容性问题。
冠军资讯
代码一只喵