91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JavaScript怎么動態監聽DOM元素高度

發布時間:2022-07-21 13:47:10 來源:億速云 閱讀:135 作者:iii 欄目:開發技術

這篇文章主要介紹“JavaScript怎么動態監聽DOM元素高度”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“JavaScript怎么動態監聽DOM元素高度”文章能幫助大家解決問題。

背景

考慮這樣一種情況,產品同學希望達到以下功能:

在我們的網頁中有一個固定區域,這個區域會用于渲染從后端拉取的含有圖片等資源的富文本字符串。

他需要在內容不超過一個最大高度的時候完全顯示所有內容,超過最大內容后僅展示最大高度范圍內的內容,超出部分隱藏,并通過一個按鈕 “展示更多” 來給用戶展示更多的選擇。

JavaScript怎么動態監聽DOM元素高度

在這看似簡單的需求當中,其實涉及到了一個難點,那就是怎樣動態的監聽到內容區域的高度變化?

因為在這里面會含有圖片資源,他們在渲染的時候會發起網絡請求,等待圖片加載完成后觸發瀏覽器重排,該區域的高度被撐開。

因此,內容區域的高度是動態變化,且變化的時間點是未知的,那么怎樣知道我們的內容區高度發生了變化呢?

為此我做了以下幾種嘗試:

  • MutationObserver

  • IntersectionObserver

  • ResizeObserver

  • 監聽所有資源的 onload 事件

  • iframe(推薦)

MutationObserver

MutationObserver 接口提供了監視對 DOM 樹所做更改的能力。它被設計為舊的 Mutation Events 功能的替代品,該功能是 DOM3 Events 規范的一部分。

observe(target, options)

這個方法會根據傳入的 options 配置,觀察 DOM 樹中的單個 Node 或者所有的子孫節點的變化。

他一共有七個屬性,這里就不一一介紹了,可以通過 MutationObserverInit 來獲取相應的介紹.

那么我們要怎么使用這個 API 來監聽目標區域的高度變化呢?

首先我們要創建對該區域的 dom 根結點引用:

	const Details = () => {
	    // useRef創建引用
	    const contentRef = useRef();
	    const [height, setHeight] = useState(-1);
	    const [observer, setObserver] = useState<MutationObserver>(null!);
	    useEffect(() => {
	          const observer = new MutationObserver((mutationList) => {
	            if (height !== contentRef.current?.clientHeight) {
	                console.log('高度變化了!');
	                setHeight(contentRef.current.clientHeight);
	            }
	          });
	          setObserver(observer);
	    }, []);
	    useEffect(() => {
	          if (!observer || !contentRef.current) return
	          observer.observe(contentRef, {
	            childList: true, // 子節點的變動(新增、刪除或者更改)
	            attributes: true, // 屬性的變動
	            characterData: true, // 節點內容或節點文本的變動
	            subtree: true// 是否將觀察器應用于該節點的所有后代節點
	          });
	    }, [contentRef.current, observer]);
	    // 綁定ref
	    return<div className="content" dangerouslySetInnerHTML={{ __html: details }} style={{ maxHeight }} ref={contentRef} />
	}

經過上面的一番操作之后,發現根本達不到效果,因為我們的 css 屬性根本沒有發生變化(我們是通過 maxHeight 來約束容器的高度的), 但是資源加載完畢之后,瀏覽器重排根本沒有產生 css 屬性的變化,它的高度是自動計算的

因此這個方案無濟于事!但是它確實可以監聽到認為修改容器的高度產生的變化,比如:contentRef.current.style.height = &lsquo;1000px&rsquo;,這個 api 是可以監聽到這一操作的,但是并不符合我們的場景

此外,它的瀏覽器兼容性也還行:

JavaScript怎么動態監聽DOM元素高度

IntersectionObserver

經過激情編碼,最后發現 MutationObserver 根本達不到我們想要的效果之后,其實我的心態已經產生了一些變化,不過不要緊!

我們可以換一種思路,既然我們無法通過監聽容器的高度變化來展示相應的 “展開更多” 操作,那么我們可不可以將這個 “展開更多” 固定到一個位置上,然后超出部分隱藏,

當我們的內容自動撐開,達到指定高度后,我們這個 “展開更多” 的操作的按鈕就顯示出來了,聽上去不錯,能達到要求!廢話不多說,開擼!

因為這里只涉及到相應的 css 樣式的書寫,就不做展示了。

經過處理之后,確實在容器高度小于指定高度的時候,“展示更多” 按鈕不會展示,超過最大值之后,會將該按鈕展示出來,

但是也遇到了一個問題,操作按鈕是有高度的,如果我們的內容高度介于最大高度 - 按鈕高度 到 容器的最大高度之間, 按鈕會產生顯示一部分,同時又隱藏一部分的效果,這可不是我們想要的!

顯然這種效果是不符合要求的,我們的 “展示更多” 按鈕,只有兩種狀態,要么全部展示,要么不展示,沒有這種部分展示的效果

它使用起來和 MutationObserver 幾乎一樣,只是名字不一樣而已

它監聽的值里面有一個比較重要的屬性:intersectionRatio

JavaScript怎么動態監聽DOM元素高度

借助這個 API,我的設計思路是這樣的:

當用戶滾動網頁的時候(或者不滾動,此時目標區域已經出現在屏幕中),可以得到 intersectionRatio 的值,通過判斷這個值是否等于 1 來決定要不要展示 “展示更多” 按鈕

但經過我的編碼實現后,發現滾動事件發生的時候,intersectionRatio 的變化是不可靠的,有時候完全可見了,但是它并不等于 1。經過多輪實驗,結果依然如此。但是它確實可以用來判斷一個元素是否進入用戶視野

由于使用上結果的不可靠,我放棄這個方案(可能是我使用方式上出了問題)

它的各瀏覽器兼容性如下:

JavaScript怎么動態監聽DOM元素高度

ResizeObserver

顧名思義,這個 API 就是專門監聽 DOM 尺寸變化的,只不過它還處于試驗階段,各瀏覽器的兼容性很差,所以基本不考慮

它現階段各瀏覽器的兼容性情況:

JavaScript怎么動態監聽DOM元素高度

監聽所有資源的 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 的方式來解決。

關于“JavaScript怎么動態監聽DOM元素高度”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

株洲市| 宁强县| 资讯| 上林县| 木里| 新晃| 五大连池市| 阿拉尔市| 平陆县| 黄石市| 赣榆县| 南城县| 汝南县| 镇沅| 宣威市| 蓬安县| 襄樊市| 台北市| 随州市| 吴桥县| 湄潭县| 泾川县| 锡林浩特市| 宁晋县| 宣城市| 安义县| 衡山县| 宜丰县| 苏州市| 禹州市| 凭祥市| 银川市| 九台市| 翼城县| 辽源市| 沙雅县| 东兴市| 乌拉特后旗| 东至县| 张家口市| 赞皇县|