热搜:fiddler git ip ios 代理
历史搜索

js如何动态监听DOM元素高度变化

游客2024-10-26 07:30:01
目录文章目录
  1. 一、背景
  2. 二、MutationObserver
  3. 三、IntersectionObserver
  4. 四、ResizeObserver
  5. 五、监听所有资源的 onload 事件
  6. 六、Iframe
  7. 七、总结

一、背景

考虑这样一种情况,产品同学希望达到以下功能:

在我们的网页中有一个固定区域,这个区域会用于渲染从后端拉取的含有图片等资源的富文本字符串。

它需要在内容不超过一个最大高度的时候完全显示所有内容,超过最大内容后仅展示最大高度范围内的内容,超出部分隐藏,并通过一个按钮 “展示更多” 来给用户展示更多的选择。

js如何动态监听DOM元素高度变化 1

五、监听所有资源的 onload 事件

既然上述方法都不行,那么我绞尽脑汁,又想出了另外一种方法:监听所有带有 src 属性的 DOM 元素的 onload 事件,通过他的回调来判断当前容器的高度情况

这种实现方式,在思路上是完全符合目的的,具体做法参考如下:

const [height, setHeight] = useState(-1);
const [showMore, setShowMore] = useState(false);
// contentRef 的定义见 MutationObserver 一节
useEffect(() => {
  const sources = contentRef.current.querySelectorAll("[src]");
  sources.onload = () => {
    const height = contentRef?.current?.clientHeight ?? 0;
    const show = height >= parseInt(MAX_HEIGHT, 10);

    setHeight(height);
    setShowMore(show);
  };
}, []);

通过这种方式可以实现对富文本中的图片进行加载后,对容器高度进行相应的判断。

但是这种方式,存在不确定性,即无法判断是否找齐了所有高度由内容撑开的资源。

六、Iframe

这是终极方案,也是在此背景中所采用的方案。

既然 window 可以监听到 resize 事件,那么我们就可以利用 iframe 来达到同样的效果,具体做法就是在容器里面嵌套一个隐藏的高度为 100% 的 iframe,通过监听他的 resize 事件,来判断当前容器的高度。

话不多说,具体实现方式如下:

const Detail: FC<{}> = () => {
  const ref = useRef<HTMLDivElement>(null);
  const ifr = useRef<HTMLIFrameElement>(null);
  const [height, setHeight] = useState(-1);
  const [showMore, setShowMore] = useState(false);
  const [maxHeight, setMaxHeight] = useState(MAX_HEIGHT);
  const introduceInfo = useAppSelect(
    (state) => state.courseInfo?.data?.introduce_info ?? {}
  );
  const details = introduceInfo.details ?? "";
  const isFolded = maxHeight === MAX_HEIGHT;
  const onresize = useCallback(() => {
    const height = ref?.current?.clientHeight ?? 0;
    const show = height >= parseInt(MAX_HEIGHT, 10);

    setHeight(height);
    setShowMore(show);
    if (ifr.current && show) {
      ifr.current.remove();
    }
  }, []);

  useEffect(() => {
    if (!ref.current || !ifr.current?.contentWindow) return;
    ifr.current.contentWindow.onresize = onresize;
    onresize();
  }, [details]);

  if (!details) returnnull;

  return (
    <section className="section detail-content">
      <div className="content-wrapper">
        <div
          className="content"
          dangerouslySetInnerHTML={{ __html: details }}
          style={{ maxHeight }}
          ref={ref}
        />
        {/* 这个 iframe 是用来动态监听 content 高度的变化的 */}
        <iframe title={IFRAME_ID} id={IFRAME_ID} ref={ifr} />
      </div>
      {isFolded && showMore && (
        <>
          <div
            className="show-more"
            onClick={() => {
              setMaxHeight(isFolded ? "none" : MAX_HEIGHT);
            }}
          >
            查看全部
            <IconArrowDown className="icon" />
          </div>
          <div className="mask" />
        </>
      )}
    </section>
  );
};

这种方式实际上就是对 ResizeObserver 的一种 hack,经过多次实践,符合功能要求。

七、总结

  1. 解决问题要尽可能的考虑多种情况,对比多种方案,采取最为可靠的一种方案。
  2. 不断学习,多查询资料,你所遇到的问题基本上前人都已经踩过坑了。
  3. 监听 DOM 元素的高度变化,可以采用内嵌 iframe 的方式来解决。

相关阅读:Vue 实现文本溢出展开收起详情功能

文章来源:腾讯 IMWeb 前端团队 ,作者 hope

标签:iframe