一、背景
考虑这样一种情况,产品同学希望达到以下功能:
在我们的网页中有一个固定区域,这个区域会用于渲染从后端拉取的含有图片等资源的富文本字符串。
它需要在内容不超过一个最大高度的时候完全显示所有内容,超过最大内容后仅展示最大高度范围内的内容,超出部分隐藏,并通过一个按钮 “展示更多” 来给用户展示更多的选择。

五、监听所有资源的 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,经过多次实践,符合功能要求。
七、总结
- 解决问题要尽可能的考虑多种情况,对比多种方案,采取最为可靠的一种方案。
- 不断学习,多查询资料,你所遇到的问题基本上前人都已经踩过坑了。
- 监听 DOM 元素的高度变化,可以采用内嵌 iframe 的方式来解决。
相关阅读:Vue 实现文本溢出展开收起详情功能
文章来源:腾讯 IMWeb 前端团队 ,作者 hope