window事件中clientHeight属性异常:窗口放大时容器高度不更新
问题分析与修复方案
问题根源
你碰到的这个问题,核心是行号生成逻辑和容器高度形成了循环依赖,再加上line-height的继承放大了这个问题:
- 你的逻辑是拿
code-container的clientHeight计算行号数量,再生成对应数量的行号塞回容器里 - 当给
code-container加了line-height:1.25rem,这个样式会被所有子元素继承,每个.line-number的高度就被固定成了这个值 - 窗口缩小时,代码换行变多,容器高度涨上去,行号数量跟着增加,这时候行号的总高度直接把容器撑住了;等窗口放大,代码换行减少,理论上容器高度该降下来,但防抖有500ms延迟,这期间旧的行号还在,它们的总高度卡着容器高度降不下来,算出来的行号数量还是最大值,就一直死循环了
- 把
code-container的line-height删掉后,行号高度只由自身内容决定,不会硬撑容器,自然就正常了
修复方案
方案1:把line-height只给需要的元素(最简单)
别在容器上全局加line-height,只给代码行和行号单独设置,避免行号硬撑容器:
/* 移除.code-container里的line-height */ .code-container { display: flex; } .line-number { color: #777; font-size: 12px; line-height: 1.25rem; /* 行号单独设置line-height */ } .code-line { padding-left: 8px; line-height: 1.25rem; /* 代码行单独设置line-height */ }
方案2:直接算代码的实际行数(最靠谱)
当前通过容器高度反推行号的逻辑本身就有问题,不如直接拿代码内容的实际高度算行数,彻底打破循环:
import { useState, useEffect, useRef } from "react"; import styles from "./CodeBlock.module.css"; function debounce(fn, ms) { let timer; return (_) => { clearTimeout(timer); timer = setTimeout((_) => { timer = null; fn.apply(this, arguments); }, ms); }; } const CodeBase = (props) => { const codeContainerRef = useRef(null); const [lineCount, setLineCount] = useState(0); useEffect(() => { const calculateLineCount = () => { const codeLineEl = codeContainerRef.current?.querySelector(`.${styles["code-line"]}`); if (!codeLineEl) return; // 拿代码行的实际line-height和滚动高度计算行数 const computedStyle = getComputedStyle(codeLineEl); const lineHeight = parseInt(computedStyle.lineHeight); const actualLineCount = Math.ceil(codeLineEl.scrollHeight / lineHeight); setLineCount(actualLineCount); }; const debouncedHandleResize = debounce(calculateLineCount, 500); calculateLineCount(); window.addEventListener("resize", debouncedHandleResize); return () => window.removeEventListener("resize", debouncedHandleResize); }, [props.children]); // 代码内容变化时重新计算 return ( <div className={styles["code-container"]} ref={codeContainerRef}> <div className={styles["line-numbers"]}> {Array.from({ length: lineCount }, (_, index) => ( <div key={index + 1} className={styles["line-number"]}> {index + 1} </div> ))} </div> <div className={styles["code-line"]}>{props.children}</div> </div> ); }; export default CodeBase;
这种方式直接基于代码内容的实际高度计算行数,不管容器怎么变,行号数量都能准确对应代码的实际行数,line-height随便加都没问题。
方案3:强制容器高度由父容器控制
如果你的容器本来就该占满父容器高度,给容器加固定高度约束,同时让行号超出部分隐藏:
.code-container { display: flex; line-height: 1.25rem; height: 100%; /* 强制容器高度继承父容器 */ } .line-numbers { display: flex; flex-direction: column; max-height: 100%; overflow-y: hidden; /* 超出容器的行号直接隐藏,避免撑大容器 */ }
内容的提问来源于stack exchange,提问作者Aslan
相关产品推荐
相关产品推荐

