
本文介绍如何通过监听 textarea 光标位置,动态计算并高亮显示行号区域中与当前编辑行对应的数字,支持点击和键盘导航(如方向键、home/end 等),并确保仅一个行号处于高亮状态。
本文介绍如何通过监听 textarea 光标位置,动态计算并高亮显示行号区域中与当前编辑行对应的数字,支持点击和键盘导航(如方向键、home/end 等),并确保仅一个行号处于高亮状态。
实现行号高亮的核心在于:准确获取光标所在的行号 + 同步更新行号 DOM 的样式 + 清除上一次高亮。原始代码存在三个关键问题:
- 未清除历史高亮(导致多个行号同时被标记);
- 仅监听 click 事件,遗漏 keydown、input、focus 等关键交互(无法响应方向键、粘贴、鼠标拖拽等);
- 行号 <span> 默认为 display: inline,而 background-color 在内联元素上不生效(需设为 block 或 inline-block)。
以下是完整、健壮的解决方案:
✅ 正确的行号高亮逻辑
const ta = document.querySelector('#textarea');
const numbersContainer = ta.parentNode.querySelector('.numbers');
// 初始化行号 DOM
function updateRowNumbering() {
const lines = ta.value.split('\n').length;
numbersContainer.innerHTML = Array(lines)
.fill('<span></span>')
.join('');
}
// 获取当前光标所在行号(从 1 开始计数)
function getCurrentLineNumber() {
const pos = ta.selectionStart;
const text = ta.value;
let line = 1;
for (let i = 0; i < pos && i < text.length; i++) {
if (text[i] === '\n') line++;
}
return line;
}
// 高亮指定行号(自动清除旧高亮)
function highlightLineNumber(lineNumber) {
const spans = numbersContainer.querySelectorAll('span');
// 清除所有高亮
spans.forEach(span => {
span.style.backgroundColor = '';
span.style.color = '#888';
});
// 高亮目标行(注意:lineNumber 从 1 开始,索引从 0 开始)
if (spans[lineNumber - 1]) {
spans[lineNumber - 1].style.backgroundColor = '#4a5568';
spans[lineNumber - 1].style.color = '#fff';
spans[lineNumber - 1].style.display = 'inline-block'; // 关键:确保背景可见
spans[lineNumber - 1].style.width = '100%';
spans[lineNumber - 1].style.padding = '0 4px';
}
}
// 统一触发高亮更新
function updateActiveLineHighlight() {
const line = getCurrentLineNumber();
highlightLineNumber(line);
}
// 监听所有可能改变光标位置的事件
['click', 'keydown', 'input', 'focus', 'mouseup'].forEach(event => {
ta.addEventListener(event, updateActiveLineHighlight);
});
// 初始化
ta.addEventListener('load', updateRowNumbering);
updateRowNumbering();
updateActiveLineHighlight(); // 初始高亮第一行? 样式增强建议(可选)
为提升视觉一致性,可在 CSS 中补充:
.numbers span {
counter-increment: linenumber;
display: inline-block; /* 确保 background 生效 */
width: 100%;
padding: 0 4px;
box-sizing: border-box;
}
.numbers span::before {
content: counter(linenumber);
color: #888;
}⚠️ 注意事项
- 性能提示:对于超长文本(万行以上),split('\n') 和遍历字符可能轻微卡顿;生产环境建议使用更高效算法(如二分查找累积偏移量),但对普通编辑器完全够用。
- 换行符兼容性:本方案兼容 \n(Unix)、\r\n(Windows)——因 textarea.value 自动标准化为 \n。
- 初始状态:务必在页面加载后调用 updateActiveLineHighlight(),否则首行不会高亮。
- 无障碍友好:高亮仅作用于视觉,如需辅助技术支持,可添加 aria-current="line" 属性到高亮 <span>。
通过以上实现,你将获得一个响应迅速、行为一致、符合编辑器直觉的行号高亮功能——无论用户是点击、键盘移动、还是粘贴内容,当前行号都会即时、唯一地被清晰标识。

















