textarea与后置高亮div同步自适应宽高异常问题排查
问题分析与修复方案
你的核心问题出在直接用后方div的scrollHeight设置textarea高度,以及缺少必要的样式对齐和防抖处理,导致高度计算偏差和卡顿。
问题1:高度计算偏差
textarea和后方div的盒模型默认存在差异(比如textarea自带默认border、padding,浏览器也可能有默认样式),直接拿ColoredEd.scrollHeight赋值给textarea,会导致高度叠加。另外,你没有先重置textarea的高度为auto,每次计算的scrollHeight都是基于上一次的错误高度,越算越大。
问题2:卡顿
每次输入都触发auto_grow,频繁修改样式会导致浏览器频繁重绘重排,引发卡顿。
修复步骤
1. 统一两个元素的盒模型样式
确保#main-code和#color-code的border、padding、box-sizing完全一致,避免布局差异:
#color-code, #main-code { padding: 16px; top: 0; left: 0; right: 0; bottom: 0; white-space: pre-wrap; font-family: monospace; font-size: 16px; line-height: inherit; border: none; /* 统一border */ box-sizing: border-box; /* 让padding包含在宽高内 */ }
2. 修正高度计算逻辑
先把textarea的高度重置为auto,再获取真实的内容高度,避免历史高度干扰:
function auto_grow() { // 先重置高度,获取真实内容高度 Editor.style.height = 'auto'; // 用textarea自身的scrollHeight,而非后方div的 const correctHeight = Editor.scrollHeight; Editor.style.height = correctHeight + 'px'; // 宽度同理,先重置再计算 Editor.style.width = 'auto'; const correctWidth = Editor.scrollWidth; Editor.style.width = correctWidth + 'px'; }
为什么不用div的scrollHeight?因为textarea的内容和div是同步的,直接用自身的scrollHeight更准确,避免div的样式意外影响。
3. 添加防抖处理解决卡顿
用防抖函数限制auto_grow的调用频率,比如每100ms只执行一次:
// 防抖函数 function debounce(func, delay = 100) { let timeoutId; return function() { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, arguments), delay); }; } // 绑定防抖后的函数 const debouncedAutoGrow = debounce(auto_grow); // 给textarea绑定事件时用这个防抖函数 document.getElementById('main-code').addEventListener('input', debouncedAutoGrow);
4. 移除不必要的margin
你的textarea设置了margin-bottom:20px,这会导致容器高度额外增加,建议移除:
#main-code { position: relative; color: transparent; caret-color: hsl(50, 75%, 70%); /* margin-bottom: 20px; 移除这行 */ }
完整修正后的代码示例
HTML
<div class="my-code"> <div id='color-code'></div> <textarea id='main-code'></textarea> </div>
CSS
.my-code { position: relative; background: hsl(220, 16%, 14%); width: 80%; overflow: auto; } #color-code, #main-code { padding: 16px; top: 0; left: 0; right: 0; bottom: 0; white-space: pre-wrap; font-family: monospace; font-size: 16px; line-height: inherit; border: none; box-sizing: border-box; } #main-code { position: relative; color: transparent; caret-color: hsl(50, 75%, 70%); background: transparent; /* 确保背景完全透明 */ } #main-code:focus { outline: 1px solid hsl(220, 16%, 19%); } #main-code::selection { background: hsla(0, 0%, 90%, 0.2); } #color-code { position: absolute; user-select: none; }
JavaScript
const Editor = document.getElementById('main-code'); const ColoredEd = document.getElementById('color-code'); // 同步内容到高亮div(补充你原本的语法高亮逻辑) Editor.addEventListener('input', () => { // 替换为你的语法高亮处理逻辑,这里用textContent做示例 ColoredEd.textContent = Editor.value; }); function auto_grow() { // 重置高度获取真实值 Editor.style.height = 'auto'; Editor.style.height = Editor.scrollHeight + 'px'; // 重置宽度获取真实值 Editor.style.width = 'auto'; Editor.style.width = Editor.scrollWidth + 'px'; } // 防抖处理 function debounce(func, delay = 100) { let timeoutId; return function() { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, arguments), delay); }; } // 绑定事件 const debouncedAutoGrow = debounce(auto_grow); Editor.addEventListener('input', debouncedAutoGrow); // 初始化时执行一次 auto_grow();
内容的提问来源于stack exchange,提问作者ChessAndCode
相关产品推荐
相关产品推荐

