像監聽頁面同樣監聽戈多的動態

不知道各位童鞋有木有看過 《等待戈多》 這部出名的荒誕戲劇 。其劇情大概就是 戈戈 與 狄狄 等待 戈多 的過程當中發生的一些雜事,一共兩幕。等了這麼多年,也不知道 戈多 如今在哪,赴約了沒有。javascript

若是 戈戈 與 狄狄 像咱們監聽頁面元素變化那樣監聽戈多的動態,是否是就不會出現空歡喜的狀態?是否是就不用等得那麼辛苦?是否是甚至能夠主動去尋找戈多?css

wait

提及監聽頁面元素變化,那麼你可知道有哪些方法能夠實現這個功能?html

Object.defineProperty

關於 Object.defineProperty 這個屬性你們應該很熟(畢竟是各種面經中的常客),但仍是要簡單介紹下~java

Object.defineProperty 容許精確添加或修改對象的屬性。經過賦值操做添加的普通屬性是可枚舉的,可以在屬性枚舉期間呈現出來。git

描述符可同時具備的鍵值:github

configurable enumerable value writable get set
數據描述符 Yes Yes Yes Yes No No
存取描述符 Yes Yes No No Yes Yes

因此咱們有如下這種效果:數組

gd1

代碼以下:微信

'use strict'
Object.defineProperty(godot, 'style', {
    get() {
        return this.getAttribute('style')
    },
    set(data) {
        this.setAttribute('style', data)
        const distance = (noLeftTree.offsetLeft - this.offsetLeft)
        console.log(distance >= 51 ? '戈多沒來,咱們先各自幹各自的活吧' : '戈多快到了,走,咱們集合去')
    }
})
const whereIsGodot = start => {
    if (start) {
        let d = 0
        const godotRun = () => {
            if (noLeftTree.offsetLeft - 51 >= d) {
                setTimeout(() => {
                    d++
                    godot.style = `left: ${d}px`
                    godotRun()
                }, 16)
            }
        }
        godotRun()
    }
}
複製代碼

簡單來講就是使用 Object.defineProperty 監聽戈多的位置變化,而後當戈多移動到集合地點附近時,等待戈多的倆哥們就能夠去赴約了。經過上述的代碼,咱們能夠知道 whereIsGodot 函數只負責戈多的位置移動,可是監聽權在等待戈多的兩我的那裏,這樣保證了代碼語義化的同時,耦合度也儘量地小。dom

MutationObserver

Mmmmm,我一直覺得 MutationObserver 是個新屬性,直到我膝蓋中了一箭看了can i use函數

caniuse

原本魚頭我也不知道有這屬性,可是最近在工做上遇到了須要監聽頁面元素變更的場景,而後就瞭解到了這個API。

因而魚頭便看了文檔,發現是個好牛逼的API。

dc

因此這究竟是個啥?

簡單來講就是一個能夠監聽 DOM Tree 變更的API,名字直譯就是 「突變觀察者」

WHATWG的定義,它的執行邏輯以下:

  1. 先執行監聽的微任務隊列;
  2. 執行完微任務隊列以後就把所監聽的記錄封裝成一個數組來處理;
  3. 而後返回處理結果。

因此具體怎麼用?

突變觀察者 是個構造器,它接受一個回調並返回一個 節點記錄列表(sequence <MutationRecord> 以及 構造的參數對象(MutationObersver)

它有如下三個方法:

  1. observe(target, options):監聽對象,接受兩個參數,一個是監聽的對象(target),一個是觀察的選項(options);
  2. disconnect():斷開監聽的功能;
  3. takeRecords():清空監聽的隊列,並返回結果。

options選項可選參數(如下屬性可設置爲true):

  1. childList:監聽目標子節點的變化;
  2. attributes:監聽目標屬性的變化;
  3. characterData:監聽目標數據的變化;
  4. subtree:監聽目標以及其後代的變化;
  5. attributeOldValue:監聽目標屬性變化前的具體值;
  6. characterDataOldValue:監聽目標數據變化前的具體值;
  7. attributeFilter:不須要監聽的屬性列表(此屬性填入過濾的屬性列表)。

如何監聽戈多的位置?

下面咱們就經過實際的代碼來監聽戈多的位置變化。

效果仍是如同上圖。

代碼以下:

const godot = document.querySelector('#godot')
const config = {
    childList: true,
    attributes: true,
    characterData: true,
    subtree: true,
    attributeOldValue: true,
    characterDataOldValue: true
}
const mutationCallback = mutationsList => {
    const [
        {
            target: {
                offsetLeft: godotPos
            }
        }
    ] = mutationsList
    const distance = (noLeftTree.offsetLeft - godotPos)
    console.log(distance >= 51 ? '戈多沒來,咱們先各自幹各自的活吧' : '戈多快到了,走,咱們集合去')
}
const observer = new MutationObserver(mutationCallback)
observer.observe(godot, config)
const whereIsGodot = start => {
    if (start) {
        let d = 0
        const godotRun = () => {
            if (noLeftTree.offsetLeft - 51 >= d) {
                setTimeout(() => {
                    d++
                    godot.style = `left: ${d}px`
                    godotRun()
                }, 16)
            } else {
                observer.disconnect()
            }
        }
        godotRun()
    }
}
複製代碼

由於魚頭在業務須要對某個已經完善的功能在部分操做監聽數據變更,若是對原來的代碼進行改動,也不是一件輕鬆的事,並且這樣子代碼太冗長,耦合度也會較高,因此就選擇了用 突變觀察者 來實現,效果仍是不錯的。

Intersection Observer

除了監聽元素的變更,還有什麼方式能夠知道戈多的位置呢?

若是有那就是 Intersection Observer 了。

這又是個啥?

戈多心想:「又來一個Observer ?別監聽了,我去找大家就是了,嚶嚶嚶。 」

委屈

IntersectionObserver 直譯是 「交叉觀察者」 ,這個API使開發人員可以監聽目標元素與根(祖先或視口)元素交叉狀態的方法。

它的用法跟 MutationObserver 類似,一樣是個構造器,它接受一個 回調函數(callback(entries)) 以及 可選參數對象(options)

因此又怎麼用?

首先 callback 會返回一個 監聽屬性對象(IntersectionObserverEntry) ,其具體屬性以下:

  1. time:可見性發生變化的時間,是個雙精度的毫秒時間戳;
  2. rootBounds:根元素的盒子區域信息,有根元素則返回 getBoundingClientRect() 的值,沒有則返回 null
  3. boundingClientRect:監聽元素的盒子區域信息;
  4. intersectionRect:監聽元素與根元素的交叉區域信息;
  5. isIntersecting:判斷監聽元素是否與根元素相交,返回布爾值;
  6. intersectionRatio:監聽元素的可見比例,即intersectionRect / boundingClientRect 徹底可見時爲1,徹底不可見時小於等於0;
  7. target:監聽的目標元素。

options 可選參數以下:

  1. root:與監聽對象相交的根元素,若是沒有,返回隱式根;
  2. rootMargin:跟CSS的margin同樣,發生交叉的偏移量;
  3. threshold:觸發回調的閾值,填入數組,範圍在0~1之間,決定發生監聽事件的交叉比例。

可選擇方法以下:

  1. IntersectionObserver.observe():開始監聽;
  2. IntersectionObserver.disconnect():中止監聽;
  3. IntersectionObserver.takeRecords():返回全部觀察目標的 IntersectionObserverEntry 對象數組;
  4. IntersectionObserver.unobserve():使IntersectionObserver中止監聽特定目標元素。

戈多,你今晚究竟是來仍是不來?

因此怎麼用這個API來監聽戈多的位置呢?

先看效果(真特麼簡陋)

godot3

代碼以下:

<style> * { margin: 0; padding: 0; box-sizing: border-box; } html, body { width: 100%; height: 200%; } noLeftTree { position: fixed; left: 0; top: 0; width: 100%; height: 100px; background: #FFF; } godot, estragon, vladimir { position: absolute; width: 50px; height: 50px; border-radius: 50%; border: 1px solid; text-align: center; } estragon { top: 0; left: 0; } vladimir { top: 0; right: 0; } godot { left: calc(50% - 25px); top: 1000px; } </style>
<noLeftTree id="noLeftTree">
    <estragon id="estragon">戈戈</estragon>
    <vladimir id="vladimir">狄狄</vladimir>
</noLeftTree>
<godot id="godot">戈多</godot>
<script> 'use strict' const godot = document.querySelector('#godot') const noLeftTree = document.querySelector('#noLeftTree') const ioCallback = entries => { console.log(entries[0].intersectionRatio <= 0 ? '戈多沒來,咱們先各自幹各自的活吧' : '戈多快到了,走,咱們集合去') } const ioConfig = { threshold: [0, 0.25, 0.5, 0.75, 1] } const io = new IntersectionObserver(ioCallback, ioConfig) io.observe(godot) </script>
複製代碼

後記

其實若是肯花時間去研究,利用好上述三個API,是能夠實現不少頗有趣的效果的,上面的只是一個初嘗的DEMO,真正在項目裏是能夠實現不少很重要的功能。

不過戈戈 與 狄狄也等待戈多快70年了,就像癡情的女生等待遠走的渣男同樣,就是不來好歹也給個音信啊。

戈多心想:「我不過是迷路了麼,嚶嚶嚶」

img

若是你、喜歡探討技術,或者對本文有任何的意見或建議,你能夠掃描下方二維碼,關注微信公衆號「 魚頭的Web海洋 」,隨時與魚頭互動。歡迎!衷心但願能夠碰見你。

qrcode-base
相關文章
相關標籤/搜索