如何用JavaScript识别滚动textarea中当前显示的文本行?
解决方案:textarea滚动与右侧HTML展示区同步
一、精准识别textarea可见文本行
核心利用浏览器的caretPositionFromPoint() API,它能直接定位视觉可见区域的文本位置,自动处理textarea的自动换行问题:
1. 获取可见区域坐标与文本位置
const textarea = document.getElementById('markdown-textarea'); textarea.addEventListener('scroll', () => { const rect = textarea.getBoundingClientRect(); // 获取可见区域顶部和底部的屏幕坐标 const topPoint = textarea.caretPositionFromPoint(rect.left + 10, rect.top + 10); const bottomPoint = textarea.caretPositionFromPoint(rect.left + 10, rect.bottom - 10); if (!topPoint || !bottomPoint) return; // 转换为文本索引 const startIndex = topPoint.offset; const endIndex = bottomPoint.offset;
2. 映射为文本行号
将textarea内容按换行符分割,通过索引匹配到对应的行:
const lines = textarea.value.split('\n'); let currentOffset = 0; let startLine = 0; let endLine = lines.length - 1; // 匹配起始行 for (let i = 0; i < lines.length; i++) { currentOffset += lines[i].length + 1; // +1 包含换行符长度 if (currentOffset > startIndex) { startLine = i; break; } } // 匹配结束行 currentOffset = 0; for (let i = 0; i < lines.length; i++) { currentOffset += lines[i].length + 1; if (currentOffset >= endIndex) { endLine = i; break; } } // 此时startLine/endLine就是当前可见的首尾行号 syncPreview(startLine); });
二、同步右侧HTML展示区滚动
需要提前建立markdown行与HTML元素的映射关系(渲染HTML时记录):
1. 构建行-元素映射表
假设你的markdown解析器会将连续行转换为HTML块(如段落、标题),渲染时记录每个块对应的markdown行范围:
// 示例:解析后生成的映射表,根据实际解析逻辑调整 const blockMap = [ { startLine: 0, endLine: 2, element: document.getElementById('preview-h1') }, { startLine: 3, endLine: 7, element: document.getElementById('preview-p1') }, { startLine: 8, endLine: 12, element: document.getElementById('preview-ul1') }, // ... 所有HTML块的映射 ];
2. 滚动到对应HTML位置
function syncPreview(targetLine) { const previewContainer = document.getElementById('html-preview'); // 找到包含目标行的HTML块 const targetBlock = blockMap.find(block => targetLine >= block.startLine && targetLine <= block.endLine); if (targetBlock) { // 滚动到块的顶部,适配容器偏移 previewContainer.scrollTop = targetBlock.element.offsetTop - previewContainer.offsetTop; } }
三、性能与边界优化
- 防抖处理:避免滚动事件频繁触发逻辑,使用
requestAnimationFrame节流:
let isSyncing = false; textarea.addEventListener('scroll', () => { if (!isSyncing) { window.requestAnimationFrame(() => { // 执行上述行识别与同步逻辑 isSyncing = false; }); isSyncing = true; } });
- 边界适配:当滚动到textarea顶部/底部时,直接将预览区滚动到对应端点:
if (textarea.scrollTop === 0) { previewContainer.scrollTop = 0; return; } if (textarea.scrollTop + textarea.clientHeight >= textarea.scrollHeight) { previewContainer.scrollTop = previewContainer.scrollHeight; return; }
内容的提问来源于stack exchange,提问作者pointonn
相关产品推荐
相关产品推荐

