如何手動實現(xiàn) DOM 元素的 Resize 事件偵聽器

我們都知道瀏覽器是沒有原生的 DOMResize 事件的凸丸,唯一能監(jiān)測到 Resize 事件的對象就是 window 這個全局對象茸时,但是這個事件只有在窗口大小改變的時候才會觸發(fā)润歉。當我們需要在 DOM 元素尺寸變化時想做一些事情的話就顯得無能為力了马澈,那么我們今天就來聊一聊如何手動實現(xiàn)這個功能罚随,其實也就是在給 DOM 元素增加一個 Resize 事件偵聽器的 polyfill壤靶。

DOM 事件是怎么觸發(fā)的

在實現(xiàn)這個功能之前我們先回顧一下平時給瀏覽器 DOM 注冊各種各樣的事件是怎么觸發(fā)的呢?如果我們把這個問題搞明白了线梗,這對接下來要實現(xiàn)的功能就能打開一個很好的思路椰于。

原生的 DOM 事件是瀏覽器響應用戶的交互行為,然后內(nèi)部會自動派發(fā)這個事件仪搔,當我們給 DOM 注冊的事件被瀏覽器派發(fā)是瘾婿,先前綁定的回調函數(shù)就會執(zhí)行了。首先注冊一個 DOM 事件是很容易的烤咧,就是我們經(jīng)常寫的代碼


document.querySelector('selector').addEventListener('resize', handler)

理想的情況是當我們改變元素尺寸是偏陪,注冊偵聽器函數(shù)的第二個參數(shù) handler 就會被執(zhí)行,當然這也是我們這篇文章最終要實現(xiàn)的目標煮嫌。

如何手動創(chuàng)建一個 DOM 事件

如果我們能自己手動創(chuàng)建一個 DOM 事件笛谦,然后在需要被觸發(fā)的時候去派發(fā)它,這樣我們的目的就達到了昌阿。當然這就要求我們對瀏覽器事件的相關 Api 有一定的了解饥脑,讓我們直接來看下面的代碼吧。


const event = document.createEvent('HTMLEvents')

event.initEvent('resize')

這樣簡單的兩行代碼就創(chuàng)建了一個自定義 DOM 事件宝泵,至于代碼中兩個函數(shù)的可傳參數(shù)都有哪些可以去翻閱文檔具體了解好啰,本文就不在贅述了。

觀測 DOM 元素尺寸變化(功能實現(xiàn)的核心)

我們有了自定義的事件儿奶,至于要我們在適當?shù)臅r候派發(fā)它就可以實現(xiàn)注冊函數(shù)的自動回調框往。那么這個事件派發(fā)的時機我們又要怎么完成。這就引出了本文的重點:如何觀測 DOM 元素的尺寸變化闯捎?幸好我們使用的現(xiàn)代瀏覽器提供了一個觀察元素尺寸變化的 Api(ResizeObserver),有個這個接口椰弊,我們就可以進行代碼實現(xiàn)了


const observer = new ResizeObserver(function elResizeChange(entries) {

  // 每次被觀測的元素尺寸發(fā)生改變這里都會執(zhí)行

})

observer.observe(el) // 觀測DOM元素

從代碼里我們可以看到许溅,我們先是以構造器的方式創(chuàng)建了一個 observer 實例,然后又調用了實例的一個方法去觀測元素秉版,這樣我們就實現(xiàn)了一個 DOM 元素的尺寸觀測方法贤重。接著我們要說到傳遞到構造器里面的那個回調函數(shù)的作用了,其主要功能都是在這個函數(shù)里實現(xiàn)的清焕。這個函數(shù)會在被觀測元素尺寸變化的時候被調用并蝗,接下來我們也會在這里去實現(xiàn)事件的派發(fā)功能

事件的派發(fā)

接著上面已經(jīng)實現(xiàn)的代碼我們繼續(xù)來實現(xiàn) DOM 自定義事件的派發(fā)功能,這也是實現(xiàn) Resize 事件的最后一步秸妥,等實現(xiàn)了這一部滚停,Resize 事件的實現(xiàn)就大功告成了!同樣讓我們直接來看代碼吧


// 這里的代碼截取自上面的回調函數(shù)

function elResizeChange(entries) {

  // entries 是個數(shù)組 每個成員又是一個ResizeObserverEntry 實例

  for (let entry of entries) {

    const event = document.createEvent('HTMLEvents') //創(chuàng)建DOM事件

    event.initEvent('resize') // 初始化事件類型為resize

    entry.target.dispatchEvent(event) // 事件派發(fā)

  }

}

這里的 elResizeChange 函數(shù)就是我們創(chuàng)建元素尺寸觀察者實例時傳入的回調函數(shù),在這里我們實現(xiàn)了元素自定義事件的派發(fā)粥惧。我們在重點講一下這個函數(shù)主要實現(xiàn)的功能键畴。

因為一個 observer 觀察者實例(一個 window 窗口僅需一個 observer 實例)需要觀測多個元素的尺寸變化,所以回調函數(shù)的入?yún)⑹且粋€數(shù)組類型的數(shù)據(jù)結構突雪,每一個元素成 員都是一個 ResizeObserverEntry 實例(包含被觀測元素尺寸變化信息的一個對象)起惕。代碼中的最后一行使用此實例的 target(指向尺寸變化的目標元素)屬性進行事件派發(fā)功能。至此整個 DOM Resize 事件的自定義偵測功能我們就做好了咏删,下面就讓我們把它合并到瀏覽器原生的事件接口中去吧!

封裝事件到瀏覽器內(nèi)置接口

手動實現(xiàn) DOM Resize 事件的功能我們已經(jīng)做出來了惹想,但是我們在給 DOM 元素注冊事件的時候怎么才能知道需要去觀察它的尺寸呢?這就需要我們對注冊事件的程序進行攔截饵婆,在注冊事件之前加入自己的處理邏輯勺馆,然后再包裝成一個全新的事件偵聽器,下面讓我們直接來看代碼吧


const addEventListener = EventTarget.prototype.addEventTarget

HTMLElement.prototype.addEventTarget = function _wrapper(type) {

  if (type === 'resize') {

    registerEvent() //講到這里侨核,聰明的你一定能實現(xiàn)這個函數(shù)草穆,趕快動手去實現(xiàn)吧!

  }

  addEventTarget.apply(this, arguments)

}

首先我們對原有的DOM事件注冊偵聽器做一個緩存搓译,然后我們手動包裝一個事件偵聽器函數(shù)覆蓋到HTMLElement的原型對象上悲柱,這里簡單說明一下為什么是覆蓋到HTMLElement而不是Element,了解原型鏈的同學應該都知道,子類會繼承從父類到原型鏈末梢的所有屬性和方法些己,這里如果把事件偵聽器重寫到Element的原型上的話豌鸡,文檔中的所有節(jié)點類型都會繼承這個方法,就連文本節(jié)點上的原型方法也被覆蓋了段标,這就造成了不必要的功能浪費涯冠,所以這里只需讓HTML元素的節(jié)點注冊偵聽器生效即可。

重寫的事件偵聽器包裝函數(shù)內(nèi)部通過判斷當前注冊的事件類型實現(xiàn)自定義的事件注冊功能即可逼庞,這里以resize 事件攔截為例子蛇更,然后把前面講到的自定義事件功能實現(xiàn)的邏輯放到這里即可,代碼中以registerEvent這個函數(shù)為案例,你還可以實現(xiàn)其他的自定義事件派任。

文章的最后附上本項目的Github倉庫地址點擊查看

備注:本篇文章為作者原創(chuàng)砸逊,轉載請注明出處,謝謝掌逛!

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末师逸,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子豆混,更是在濱河造成了極大的恐慌篓像,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皿伺,死亡現(xiàn)場離奇詭異遗淳,居然都是意外死亡,警方通過查閱死者的電腦和手機心傀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拆讯,“玉大人脂男,你說我怎么就攤上這事≈帜牛” “怎么了宰翅?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長爽室。 經(jīng)常有香客問我汁讼,道長,這世上最難降的妖魔是什么阔墩? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任嘿架,我火速辦了婚禮,結果婚禮上啸箫,老公的妹妹穿的比我還像新娘耸彪。我一直安慰自己,他們只是感情好忘苛,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布蝉娜。 她就那樣靜靜地躺著,像睡著了一般扎唾。 火紅的嫁衣襯著肌膚如雪召川。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天胸遇,我揣著相機與錄音荧呐,去河邊找鬼。 笑死,一個胖子當著我的面吹牛坛增,可吹牛的內(nèi)容都是我干的获雕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼收捣,長吁一口氣:“原來是場噩夢啊……” “哼届案!你這毒婦竟也來了?” 一聲冷哼從身側響起罢艾,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤楣颠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后咐蚯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體童漩,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年春锋,在試婚紗的時候發(fā)現(xiàn)自己被綠了矫膨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡期奔,死狀恐怖侧馅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情呐萌,我是刑警寧澤馁痴,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站肺孤,受9級特大地震影響罗晕,放射性物質發(fā)生泄漏。R本人自食惡果不足惜赠堵,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一小渊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茫叭,春花似錦粤铭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至吗垮,卻和暖如春垛吗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背烁登。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工怯屉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蔚舀,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓锨络,卻偏偏與公主長得像赌躺,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子羡儿,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容

  • ??JavaScript 與 HTML 之間的交互是通過事件實現(xiàn)的掠归。 ??事件缅叠,就是文檔或瀏覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,490評論 1 11
  • 事件流 JavaScript與HTML之間的交互是通過事件實現(xiàn)的。事件虏冻,就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互...
    DHFE閱讀 828評論 0 3
  • JavaScript 與 HTML 之間的交互是通過事件實現(xiàn)的肤粱。事件,就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互瞬...
    LemonnYan閱讀 680評論 0 4
  • ??DOM 1 級主要定義的是 HTML 和 XML 文檔的底層結構。 ??DOM2 和 DOM3 級則在這個結構...
    霜天曉閱讀 1,440評論 1 3
  • 周五的時候蛮穿,同事(一個不了解設計行業(yè)悯森,但熱心腸的Boy)突然看見有附近公司求簡單P圖的消息,就來問我要不要接绪撵,并且...
    拾葉姑娘閱讀 598評論 0 0