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

溫馨提示×

溫馨提示×

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

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

javascript中如何監聽頁面DOM變動并高效響應

發布時間:2021-11-15 15:22:25 來源:億速云 閱讀:129 作者:iii 欄目:web開發

本篇內容介紹了“javascript中如何監聽頁面DOM變動并高效響應”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

從 DOM 變動事件監聽說起

首先假設大家已經知道 JavaScript 中事件的發生階段(捕獲-***-冒泡),附上一張圖帶過這個內容,我們直接進入尋找解決方法的過程。

javascript中如何監聽頁面DOM變動并高效響應

Graphical representation of an event dispatched in a DOM tree using the DOM  event flow

開始的時候我一直在 window 狀態改變涉及到的事件中尋找,一圈搜尋下來發現也就 onload 事件最接近了,所以我們看看 MDN  對該事件的定義:

The load event is fired when a resource and its dependent resources have  finished loading.

怎么理解資源及其依賴資源已加載完畢呢?簡單來說,如果一個頁面涉及到圖片資源,那么 onload  事件會在頁面完全載入(包括圖片、css文件等等)后觸發。一個簡單的監聽事件用 JavaScript 應該這樣書寫(注意不同環境下 load 和 onload  的差異):

<script>    window.addEventListener("load", function(event) {      console.log("All resources finished loading!");    });        // or    window.onload=function(){      console.log("All resources finished loading!");    };        // HTML  < body onload="SomeJavaScriptCode">        // jQuery    $( window ).on( "load", handler )  </script>

當然,說到 onload 事件,有一個 jQuery 中相似的事件一定會被提及&mdash;&mdash; ready 事件。jQuery 中這樣定義這個事件:

Specify a function to execute when the DOM is fully loaded.

需要知道的是 jQuery 定義的 ready 事件實質上是為 DOMContentLoaded 事件設計的,所以當我們談論加載時應該區分的事件其實是  onload(接口 UIEvent) 以及 DOMContentLoaded(接口 Event),MDN 這樣描述 DOMContentLoaded:

當初始HTML文檔被完全加載和解析時,DOMContentLoaded 事件被觸發,而無需等待樣式表、圖像和子框架完成加載。另一個不同的事件 load  應該僅用于檢測一個完全加載的頁面。

所以可以知道,當一個頁面加載時應先觸發 DOMContentLoaded 然后才是 onload. 類似的事件及區別包括以下幾類:

  • DOMContentLoaded: 當初始HTML文檔被完全加載和解析時,DOMContentLoaded  事件被觸發,而無需等待樣式表、圖像和子框架完成加載;

  • readystatechange: 一個document 的 Document.readyState  屬性描述了文檔的加載狀態,當這個狀態發生了變化,就會觸發該事件;

  • load: 當一個資源及其依賴資源已完成加載時,將觸發load事件;

  • beforeunload: 當瀏覽器窗口,文檔或其資源將要卸載時,會觸發beforeunload事件。

  • unload: 當文檔或一個子資源正在被卸載時, 觸發 unload事件。

細心點會發現上面在介紹事件時提到了 UIEvent 以及 Event,這是什么呢?這些都是事件&mdash;&mdash;可以被 JavaScript  偵測到的行為。其他的事件接口還包括 KeyboardEvent / VRDisplayEvent (是的,沒錯,這就是你感興趣且熟知的那個  VR)等等;如果在搜索引擎中稍加搜索,你會發現有些資料里寫到事件可以分為以下幾類:

  • UI事件

  • 焦點事件

  • 鼠標與滾輪事件

  • 鍵盤與文本事件

  • 復合事件

  • 變動事件

  • HTML5 事件

  • 設備事件

  • 觸摸與手勢事件

但這樣寫實在有些凌亂,其中一些是 DOM3 定義的事件,有一些是單獨列出的事件,如果你覺得熟悉那么你會發現這是 JavaScript  高級程序設計里的敘述模式,在我看來,理解這些事件可以按照 DOM3 事件以及其他事件來做區分:其中,DOM3 級事件規定了以下幾類事件 &ndash; UI 事件,  焦點事件, 鼠標事件, 滾輪事件, 文本事件, 鍵盤事件, 合成事件, 變動事件, 變動名稱事件; 而剩下的例如 HTML5 事件可以單獨做了解。而剛開始提到的  Event 作為一個主要接口,是很多事件的實現父類。有關 Web API 接口可以在這里查到,里面可以看到有很多 Event 字眼。

好吧,事件說了這么多,我們還是沒有解決剛開始提出的問題,如果監聽頁面中動態生成的元素呢?想到動態生成的元素都是需要通過網絡請求獲取資源的,那么是否可以監聽所有  HTTP 請求呢?查看 jQuery 文檔可以知道每當一個Ajax請求完成,jQuery 就會觸發 ajaxComplete  事件,在這個時間點所有處理函數會使用 .ajaxComplete() 方法注冊并執行。但是誰能保證所有 ajax 都從 jQuery  走呢?所以應該在變動事件中做出選擇,我們來看看 DOM2 定義的如下變動事件:

  • DOMSubtreeModified: 在DOM結構發生任何變化的時候。這個事件在其他事件觸發后都會觸發;

  • DOMNodeInserted: 當一個節點作為子節點被插入到另一個節點中時觸發;

  • DOMNodeRemoved: 在節點從其父節點中移除時觸發;

  • DOMNodeInsertedIntoDocument: 在一個節點被直接插入文檔或通過子樹間接插入文檔之后觸發。這個事件在  DOMNodeInserted 之后觸發;

  • DOMNodeRemovedFromDocument: 在一個節點被直接從文檔移除或通過子樹間接從文檔移除之前觸發。這個事件在  DOMNodeRemoved 之后觸發;

  • DOMAttrModified: 在特性被修改之后觸發;

  • DOMCharacterDataModified: 在文本節點的值發生變化時觸發;

所以,用 DOMSubtreeModified 好像沒錯。師兄旁邊提醒,用 MutationObserver, 于是又搜到了一個新大陸。MDN 這樣描述  MutationObserver:

MutationObserver給開發者們提供了一種能在某個范圍內的DOM樹發生變化時作出適當反應的能力.該API設計用來替換掉在DOM3事件規范中引入的Mutation事件.

DOM3 事件規范中的 Mutation 事件可以被簡單看成是 DOM2 事件規范中定義的 Mutation  事件的一個擴展,但是這些都不重要了,因為他們都要被 MutationObserver 替代了。好了,那么來詳細介紹一下 MutationObserver  吧。文章《Mutation Observer API》對 MutationObserver  的用法介紹的比較詳細,所以我挑幾點能直接解決我們需求的說一說。

既然要監聽 DOM 的變化,我們來看看 Observer 的作用都有哪些:

它等待所有腳本任務完成后,才會運行,即采用異步方式。

它把 DOM 變動記錄封裝成一個數組進行處理,而不是一條條地個別處理 DOM 變動。

它既可以觀察發生在 DOM 的所有類型變動,也可以觀察某一類變動。

MutationObserver 的構造函數比較簡單,傳入一個回調函數即可(回調函數接受兩個參數,***個是變動數組,第二個是觀察器實例):

let observer = new MutationObserver(callback);

觀察器實例使用 observe 方法來監聽, disconnect 方法停止監聽,takeRecords 方法來清除變動記錄。

let article = document.body;     let  options = {    'childList': true,    'attributes':true  } ;     observer.observe(article, options);

observe 方法中***個參數是所要觀察的變動 DOM 元素,第二個參數則接收所要觀察的變動類型(子節點變動和屬性變動)。變動類型包括以下幾種:

  • childList:子節點的變動。

  • attributes:屬性的變動。

  • characterData:節點內容或節點文本的變動。

  • subtree:所有后代節點的變動。

想要觀察哪一種變動類型,就在 option 對象中指定它的值為 true。需要注意的是,如果設置觀察 subtree 的變動,必須同時指定  childList、attributes 和 characterData 中的一種或多種。disconnect 方法和 takeRecords  方法則直接調用即可,無傳入參數。

好的,我們已經搞定了 DOM 變動的監聽,將代碼刷新一下看下效果吧,因為頁面由很多動態生成的商品組成,那么我應該在 body 上添加變動監聽,所以  options 應該這樣設置:

var options = {    'attributes': true,    'subtree': true  }

咦?頁面往下拉一小點就觸發了 observer 幾十次?這樣 DOM 哪吃得消啊,查看了頁面的變動記錄發現每次新進的資源底層都調用了  Node.insertBefore() 方法&hellip;

再聊聊 JavaScript 中的截流/節流函數

現在遇到的一個麻煩是, DOM 變動太頻繁了,如果每次變動都監聽那真是太耗費資源了。一個簡單的解決辦法是我就放棄監聽了,而采用 setInterval  方法定時執行更新邏輯。是的,雖然方法原始了一點,但是性能上比 Observer “改進”了不少。

這個時候,又來了師兄的助攻:“用用截流函數”。記起之前看《JavaScript 語言精粹》的時候看到是用 setTimeout 方法自調用來解決  setInteval 的頻繁執行吃資源的現象,不知道兩者是不是有關聯。網上一查發現有兩個“jie流函數”。需求來自于這里:

在前端開發中,頁面有時會綁定scroll或resize事件等頻繁觸發的事件,也就意味著在正常的操作之內,會多次調用綁定的程序,然而有些時候javascript需要處理的事情特別多,頻繁出發就會導致性能下降、成頁面卡頓甚至是瀏覽器奔潰。

如果重復利用 setTimeout 和 clearTimeout 方法,我們好像可以解決這個頻繁觸發的執行。每次事件觸發的時候我首先判斷一下當前有沒有一個  setTimeout 定時器,如果有的話我們先將它清除,然后再新建一個 setTimeout  定時器來延遲我的響應行為。這樣聽上去還不錯,因為我們每次都不立即執行我們的響應,而頻繁觸發過程我們又能保持響應函數一直存在(且只存在一個),除了會有些延遲響應外,沒什么不好的。是的這就是截流函數(debounce),有一篇博客用這個小故事介紹它:

形像的比喻是橡皮球。如果手指按住橡皮球不放,它就一直受力,不能反彈起來,直到松手。debounce 的關注點是空閑的間隔時間。

在我的業務中,在 observer 實例中調用下面寫的這個截流函數就可以啦

/**  * fn 執行函數  * context 綁定上下文  * timeout 延時數值  **/  let debounce = function(fn, context, timeout) {  let timer;            // 利用閉包將內容傳遞出去  return function() {    if (timer) {      // 清除定時器      clearTimeout(timer);    }    // 設置一個新的定時器    timer = setTimeout(function(){    fn.apply(context, arguments)    }, timeout);   }  }

當然,解決了自己的問題,但還有一個概念沒有說到&mdash;&mdash;“節流函數”。同一篇博文里也使用了一個例子來說明它:

形像的比喻是水龍頭或機槍,你可以控制它的流量或頻率。throttle 的關注點是連續的執行間隔時間。

函數節流的原理也挺簡單,一樣還是定時器。當我觸發一個時間時,先setTimout讓這個事件延遲一會再執行,如果在這個時間間隔內又觸發了事件,那我們就清除原來的定時器,再setTimeout一個新的定時器延遲一會執行。函數節流的出發點,就是讓一個函數不要執行得太頻繁,減少一些過快的調用來節流。這里用  AlloyTeam 的節流代碼實現來解釋:

// 參數同上  var throttle = function(fn, delay, mustRunDelay){   var timer = null;   var t_start;   return function(){      var context = this, args = arguments, t_curr = +new Date();            // 清除定時器      clearTimeout(timer);            // 函數初始化判斷      if(!t_start){          t_start = t_curr;      }            // 超時(指定的時間間隔)判斷      if(t_curr - t_start >= mustRunDelay){          fn.apply(context, args);          t_start = t_curr;      }      else {          timer = setTimeout(function(){              fn.apply(context, args);          }, delay);      }   };  };

“javascript中如何監聽頁面DOM變動并高效響應”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

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

AI

盐津县| 黄石市| 杭锦旗| 扎赉特旗| 唐山市| 都安| 曲靖市| 无为县| 准格尔旗| 手游| 长寿区| 珲春市| 右玉县| 南漳县| 嘉义市| 乌拉特前旗| 茌平县| 大理市| 太白县| 车致| 巴里| 泰州市| 鱼台县| 炉霍县| 马龙县| 老河口市| 德昌县| 遂溪县| 滨州市| 灌云县| 浦江县| 天峻县| 齐河县| 内丘县| 仁怀市| 阿城市| 成都市| 江阴市| 康马县| 石泉县| 焉耆|