欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript如何動(dòng)態(tài)監(jiān)聽DOM元素高度詳解

 更新時(shí)間:2022年07月21日 10:10:19   作者:大蓮芒  
這篇文章主要為大家詳細(xì)介紹了JavaScript如何動(dòng)態(tài)監(jiān)聽DOM元素高度,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

背景

考慮這樣一種情況,產(chǎn)品同學(xué)希望達(dá)到以下功能:

在我們的網(wǎng)頁中有一個(gè)固定區(qū)域,這個(gè)區(qū)域會(huì)用于渲染從后端拉取的含有圖片等資源的富文本字符串。

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

在這看似簡(jiǎn)單的需求當(dāng)中,其實(shí)涉及到了一個(gè)難點(diǎn),那就是怎樣動(dòng)態(tài)的監(jiān)聽到內(nèi)容區(qū)域的高度變化?

因?yàn)樵谶@里面會(huì)含有圖片資源,他們?cè)阡秩镜臅r(shí)候會(huì)發(fā)起網(wǎng)絡(luò)請(qǐng)求,等待圖片加載完成后觸發(fā)瀏覽器重排,該區(qū)域的高度被撐開。

因此,內(nèi)容區(qū)域的高度是動(dòng)態(tài)變化,且變化的時(shí)間點(diǎn)是未知的,那么怎樣知道我們的內(nèi)容區(qū)高度發(fā)生了變化呢?

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

  • MutationObserver
  • IntersectionObserver
  • ResizeObserver
  • 監(jiān)聽所有資源的 onload 事件
  • iframe(推薦)

MutationObserver

MutationObserver 接口提供了監(jiān)視對(duì) DOM 樹所做更改的能力。它被設(shè)計(jì)為舊的 Mutation Events 功能的替代品,該功能是 DOM3 Events 規(guī)范的一部分。

observe(target, options)

這個(gè)方法會(huì)根據(jù)傳入的 options 配置,觀察 DOM 樹中的單個(gè) Node 或者所有的子孫節(jié)點(diǎn)的變化。

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

那么我們要怎么使用這個(gè) API 來監(jiān)聽目標(biāo)區(qū)域的高度變化呢?

首先我們要?jiǎng)?chuàng)建對(duì)該區(qū)域的 dom 根結(jié)點(diǎn)引用:

	const Details = () => {
	    // useRef創(chuàng)建引用
	    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, // 子節(jié)點(diǎn)的變動(dòng)(新增、刪除或者更改)
	            attributes: true, // 屬性的變動(dòng)
	            characterData: true, // 節(jié)點(diǎn)內(nèi)容或節(jié)點(diǎn)文本的變動(dòng)
	            subtree: true// 是否將觀察器應(yīng)用于該節(jié)點(diǎn)的所有后代節(jié)點(diǎn)
	          });
	    }, [contentRef.current, observer]);
	    // 綁定ref
	    return<div className="content" dangerouslySetInnerHTML={{ __html: details }} style={{ maxHeight }} ref={contentRef} />
	}

經(jīng)過上面的一番操作之后,發(fā)現(xiàn)根本達(dá)不到效果,因?yàn)槲覀兊?css 屬性根本沒有發(fā)生變化(我們是通過 maxHeight 來約束容器的高度的), 但是資源加載完畢之后,瀏覽器重排根本沒有產(chǎn)生 css 屬性的變化,它的高度是自動(dòng)計(jì)算的

因此這個(gè)方案無濟(jì)于事!但是它確實(shí)可以監(jiān)聽到認(rèn)為修改容器的高度產(chǎn)生的變化,比如:contentRef.current.style.height = ‘1000px’,這個(gè) api 是可以監(jiān)聽到這一操作的,但是并不符合我們的場(chǎng)景

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

IntersectionObserver

經(jīng)過激情編碼,最后發(fā)現(xiàn) MutationObserver 根本達(dá)不到我們想要的效果之后,其實(shí)我的心態(tài)已經(jīng)產(chǎn)生了一些變化,不過不要緊!

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

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

因?yàn)檫@里只涉及到相應(yīng)的 css 樣式的書寫,就不做展示了。

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

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

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

因此我查閱了相關(guān)資料,了解到了 IntersectionObserver 這個(gè) API,它可以監(jiān)聽一個(gè)元素是否進(jìn)入用戶視野,它的相關(guān)使用方法可以參考這篇文章:IntersectionObserver API 使用教程

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

它監(jiān)聽的值里面有一個(gè)比較重要的屬性:intersectionRatio

借助這個(gè) API,我的設(shè)計(jì)思路是這樣的:

當(dāng)用戶滾動(dòng)網(wǎng)頁的時(shí)候(或者不滾動(dòng),此時(shí)目標(biāo)區(qū)域已經(jīng)出現(xiàn)在屏幕中),可以得到 intersectionRatio 的值,通過判斷這個(gè)值是否等于 1 來決定要不要展示 “展示更多” 按鈕

但經(jīng)過我的編碼實(shí)現(xiàn)后,發(fā)現(xiàn)滾動(dòng)事件發(fā)生的時(shí)候,intersectionRatio 的變化是不可靠的,有時(shí)候完全可見了,但是它并不等于 1。經(jīng)過多輪實(shí)驗(yàn),結(jié)果依然如此。但是它確實(shí)可以用來判斷一個(gè)元素是否進(jìn)入用戶視野

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

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

ResizeObserver

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

具體使用方法可以參考這篇文章:檢測(cè) DOM 尺寸變化 JS API ResizeObserver 簡(jiǎn)介

它現(xiàn)階段各瀏覽器的兼容性情況:

監(jiān)聽所有資源的 onload 事件

既然上述方法都不行,那么我絞盡腦汁,又想出了另外一種方法:監(jiān)聽所有帶有 src 屬性的 DOM 元素的 onload 事件,通過他的回調(diào)來判斷當(dāng)前容器的高度情況

這種實(shí)現(xiàn)方式,在思路上是完全符合目的的,具體做法參考如下:

const [height, setHeight] = useState(-1);
	const [showMore, setShowMore] = useState(false);
	// contentRef 的定義見 MutationObserver 一節(jié)
	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);
	  };
	}, []);

通過這種方式可以實(shí)現(xiàn)對(duì)富文本中的圖片進(jìn)行加載后,對(duì)容器高度進(jìn)行相應(yīng)的判斷。

但是這種方式,存在不確定性,即無法判斷是否找齊了所有高度由內(nèi)容撐開的資源。

Iframe

這是終極方案,也是在此背景中所采用的方案。

既然 window 可以監(jiān)聽到 resize 事件,那么我們就可以利用 iframe 來達(dá)到同樣的效果,具體做法就是在容器里面嵌套一個(gè)隱藏的高度為 100% 的 iframe,通過監(jiān)聽他的 resize 事件,來判斷當(dāng)前容器的高度。

話不多說,具體實(shí)現(xiàn)方式如下:

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}
	        />
	        {/* 這個(gè)iframe是用來動(dòng)態(tài)監(jiān)聽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>
	  );
	};

這種方式實(shí)際上就是對(duì) ResizeObserver 的一種 hack,經(jīng)過多次實(shí)踐,符合功能要求。

總結(jié):

解決問題要盡可能的考慮多種情況,對(duì)比多種方案,采取最為可靠的一種方案。

注: 監(jiān)聽 DOM 元素的高度變化,可以采用內(nèi)嵌 iframe 的方式來解決。

到此這篇關(guān)于JavaScript如何動(dòng)態(tài)監(jiān)聽DOM元素高度詳解的文章就介紹到這了,更多相關(guān)JS動(dòng)態(tài)監(jiān)聽DOM 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 27個(gè)JavaScript數(shù)組常見方法匯總與實(shí)例說明

    27個(gè)JavaScript數(shù)組常見方法匯總與實(shí)例說明

    這篇文章主要介紹了JavaScript數(shù)組常見方法匯總與實(shí)例說明包括數(shù)組修改,數(shù)組增加,數(shù)組遍歷,數(shù)組排序等操作,需要的朋友可以參考下
    2022-12-12
  • JS中實(shí)現(xiàn)replaceAll的方法(實(shí)例代碼)

    JS中實(shí)現(xiàn)replaceAll的方法(實(shí)例代碼)

    本文是對(duì)JS中實(shí)現(xiàn)replaceAll的方法進(jìn)行了詳細(xì)的總結(jié)介紹,需要的朋友可以過來參考下,希望對(duì)大家有所幫助
    2013-11-11
  • JavaScript中var與let的區(qū)別

    JavaScript中var與let的區(qū)別

    這篇文章主要介紹了JavaScript中var與let的區(qū)別,var是JavaScript剛出現(xiàn)時(shí)就存在的變量聲明關(guān)鍵字,而let作為ES6才出現(xiàn)的變量聲明關(guān)鍵字,無疑兩者之間存在著很大的區(qū)別,下面來看看兩者之間到底存在什么
    2021-12-12
  • javascript父子通信

    javascript父子通信

    javascript父子通信...
    2007-05-05
  • 小程序外賣訂單界面的示例代碼

    小程序外賣訂單界面的示例代碼

    這篇文章主要介紹了小程序外賣訂單界面的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • 靈活應(yīng)用js調(diào)試技巧解決樣式問題的步驟分享

    靈活應(yīng)用js調(diào)試技巧解決樣式問題的步驟分享

    在很多時(shí)候,前端開發(fā)人員使用JS腳本,對(duì)頁面Dom結(jié)構(gòu)或者是樣式做出了修改,會(huì)造成一些意想不到的bug
    2012-03-03
  • JavaScript實(shí)現(xiàn)隨機(jī)生成驗(yàn)證碼及校驗(yàn)

    JavaScript實(shí)現(xiàn)隨機(jī)生成驗(yàn)證碼及校驗(yàn)

    這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)隨機(jī)生成驗(yàn)證碼及校驗(yàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • ES6中promise詳解及用法實(shí)例

    ES6中promise詳解及用法實(shí)例

    Promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大,下面這篇文章主要給大家介紹了關(guān)于ES6中promise詳解及用法的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • document.documentElement的一些使用技巧

    document.documentElement的一些使用技巧

    documentElement 屬性可返回文檔的根節(jié)點(diǎn),接下來為大家詳細(xì)介紹下document.documentElement的一些使用技巧,感興趣的朋友可以參考下哈
    2013-04-04
  • JS取數(shù)字小數(shù)點(diǎn)后兩位或n位的簡(jiǎn)單方法

    JS取數(shù)字小數(shù)點(diǎn)后兩位或n位的簡(jiǎn)單方法

    下面小編就為大家?guī)硪黄狫S取數(shù)字小數(shù)點(diǎn)后兩位或n位的簡(jiǎn)單方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-10-10

最新評(píng)論