像监听页面同样监听戈多的动态

不知道各位童鞋有木有看过 《等待戈多》 这部出名的荒诞戏剧 。其剧情大概就是 戈戈 与 狄狄 等待 戈多 的过程当中发生的一些杂事,一共两幕。等了这么多年,也不知道 戈多 如今在哪,赴约了没有。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
相关文章
相关标签/搜索