You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 16:25:38