JavaScript中MutationObServer监听DOM元素详情

一、基本使用

可以通过MutationObserver构造函数实例化,参数是一个回调函数。

let observer = new MutationObserver(() => console.log("change"));

console.log(observer);

observer对象原型链如下:

MutationObserver实例:

可以看到有disconnectobservertakeRecords方法。

1. observer方法监听

observer方法用于关联DOM元素,并根据相关设置进行监听。

语法如下:

// 接收两个参数

observer(DOM元素, MutationObserverInit对象);

其中:

  • 第一个参数DOM元素就是页面元素,比如:body、div等。
  • 第二个参数就是设置要监听的范围。比如:属性、文本、子节点等,是一个键值对数组。

示例1,监听body元素class的变化:

let observer = new MutationObserver(() => console.log("change"));

// 监听body元素的属性变化

observer.observe(document.body, {

    attributes: true

});

// 更改body元素的class,会异步执行创建MutationObserver对象时传入的回调函数

document.body.className = "main";

console.log("修改了body属性");

// 控制台输出:

//    修改了body属性

//    change

上面 change 的输出是在 修改了body属性 之后,可见注册的回调函数是异步执行的,是在后面执行的。

2. 回调函数增加MutationRecord实例数组参数

现在回调函数非常简单,就是输出一个字符串,看不出到底发生了什么变化。

其实回调函数接收一个 MutationRecord 实例数组,实务中可以通过这个查看详细的信息。

let observer = new MutationObserver(

    // 回调函数是一个 MutationRecord 实例数组。格式如下:

    //     [MutationRecord, MutationRecord, MutationRecord, ...]

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true

});

document.body.className = "main";

console.log("修改了body属性");

// 控制台输出:

//    修改了body属性

//     (1) [MutationRecord]

其中 mutationRecords信息 如下:

MutationRecord实例

其中几个比较关键的信息:

  • attributeName 表示修改的属性名称
  • target 修改的目标
  • type 类型

如果多次修改body的属性,那么会有多条记录:

// MutationRecord

let observer = new MutationObserver(

    // 回调函数接收一个 MutationRecord 实例,是一个数组。

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true

});

// 修改三次

document.body.className = "main";

document.body.className = "container";

document.body.className = "box";

// 控制台打印如下:

//     (3) [MutationRecord, MutationRecord, MutationRecord]

注意:

这里不是修改一次就执行一次回调,而是每修改一次就往 mutationRecords 参数加入一个 MutationRecord 实例,最后执行一次回调打印出来。

如果修改一次就执行一次回调,那么性能就会比较差。

3. disconnect方法终止回调

如果要终止回调,可以使用disconnect方法。

let observer = new MutationObserver(

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true

});

// 第一次修改

document.body.className = "main";

// 终止

observer.disconnect();

// 第二次修改

document.body.className = "container";

// 没有日志输出

这里没有日志输出,包括第一次修改也没有日志输出,因为回调函数的执行是异步的,是在最后执行的。后面把observer终止了,所以就不会执行了。

可以用setTimeout控制最后才终止,这样回调就会正常执行。

let observer = new MutationObserver(

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true

});

// 第一次修改

document.body.className = "main";

// 终止

setTimeout(() => {

    observer.disconnect();

    // 第三次修改,下面修改不会回调了

    document.body.className = "container";

}, 0);

// 第二次修改

document.body.className = "container";

// 页面输出:

//    (2) [MutationRecord, MutationRecord]

终止之后再启用

终止了之后可以再次启动,请看下面示例:

let observer = new MutationObserver(

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true

});

// 第一次修改,会入 mutationRecords 数组

document.body.className = "main";

// 终止

setTimeout(() => {

    observer.disconnect();

    // 第二次修改,因为终止了,下面修改不会入 mutationRecords 数组

    document.body.className = "container";

}, 0);

setTimeout(() => {

    // 再次启用

    observer.observe(document.body, {

        attributes: true

    });

    // 修改body属性,会入 mutationRecords 数组

    document.body.className = "container";

}, 0);

// 控制台输出:

//    [MutationRecord]

//    [MutationRecord]

这边回调函数是执行了两次,打印了两个,其中:

  • 第一个输出是在第一次修改,后面没有同步代码了,就执行了回调。
  • 第二个输出是在第三次修改,因为重新启用了,所以就正常执行了回调。

第二次修改,因为observer被终止了,所以修改body的属性不会入 mutationRecords 数组。

4. takeRecords方法获取修改记录

如果希望在终止observer之前,对已有的 mutationRecords 记录进行处理,可以用takeRecords方法获取。

let observer = new MutationObserver(

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true

});

// 第一次修改,会入 mutationRecords 数组

document.body.className = "main";

// 第二次修改,会入 mutationRecords 数组

document.body.className = "container";

// 第三次修改,会入 mutationRecords 数组

document.body.className = "box";

// 取到修改记录,可以对其进行处理

let mutationRecords =  observer.takeRecords();

console.log(mutationRecords);

// 控制台打印:

//     (3) [MutationRecord, MutationRecord, MutationRecord]

console.log(observer.takeRecords());

// 控制台打印:

//    []

// 终止

observer.disconnect();

二、监听多个元素

上面监听都是只有一个元素,如果要监听多个元素可以复用MutationObserver实例

let observer = new MutationObserver(

    (mutationRecords) => console.log(mutationRecords)

);

// 创建 div1 元素,并监听

let div1 = document.createElement("div");

observer.observe(div1, {

    attributes: true

});

div1.id = "box1";

// 创建div2并监听

let div2 = document.createElement("div");

observer.observe(div2, {

    attributes: true

});

div2.id = "box2";

// 控制台打印:

//    (2) [MutationRecord, MutationRecord]

控制台打印了两个MutationRecord,其中:

  • 第一个 MutationRecord 就是 div1 的id属性修改记录。
  • 第二个 MutationRecord 就是 div2 的id属性修改记录。

其他使用方式和上面的类似。

三、监听范围MutationObserverInit对象

上面的监听都是监听属性,当然也可以监听其他的东西,比如:文本、子节点等。

1. 观察属性

上面的例子都是观察元素自有的属性,这里再举一个自定义属性的例子。

let observer = new MutationObserver(

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true

});

// 修改自定义的属性

document.body.setAttribute("data-id", 1);

// 控制台打印:

//    [MutationRecord]

修改自定义的属性一样会加入到 mutationRecords 数组。

另外值的一提的是 data-id 经常用来给元素标记一些数据啥的,如果发生变化,程序就可以监听到,就可以处理一些相应的逻辑。

attributeFilter过滤:

如果要监听指定的属性变化,可以用 attributeFilter 过滤。

let observer = new MutationObserver(

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true,

    // 设置白名单

    attributeFilter: ["data-id"]

});

// 修改白名单 attributeFilter 内的属性,会入 mutationRecords

document.body.setAttribute("data-id", 1);

// 修改不在白名单 attributeFilter 内的属性,不会入 mutationRecords

document.body.setAttribute("class", "main");

// 控制台打印:

//    [MutationRecord]

attributeOldValue记录旧值

如果要记录旧值,可以设置 attributeOldValue true

let observer = new MutationObserver(

    // MutationRecord对象中oldValue表示旧值

    (mutationRecords) => console.log(mutationRecords.map((x) => x.oldValue))

);

observer.observe(document.body, {

    attributes: true,

    attributeOldValue: true,

});

// 第一次修改,因为原来没有值,所以旧值 oldValue = null

document.body.setAttribute("class", "main");

// 第二次修改,因为前面有改了一次,所以旧值 oldValue = main

document.body.setAttribute("class", "container");

// 控制台打印:

//    (2) [null, 'main']

2. 观察文本

观察文本设置 characterDatatrue 即可,不过只能观察文本节点。

请看如下示例:

<!-- 一个性感的div -->

<div id="box">Hello</div>

<script type="text/javascript">

    let observer = new MutationObserver(

        (mutationRecords) => console.log(mutationRecords)

    );

    // 获取文本节点

    let textNode = document.getElementById("box").childNodes[0];

    observer.observe(textNode, {

        // 观察文本变化

        characterData: true

    });

    // 修改文本

    textNode.textContent = "Hi";

    // 控制台打印:

    //    [MutationRecord]

</script>

如果直接监听div元素,那么是不生效的:

<!-- 一个性感的div -->

<div id="box">Hello</div>

<script type="text/javascript">

    let observer = new MutationObserver(

        (mutationRecords) => console.log(mutationRecords)

    );

    // 监听div不会生效

    let box = document.getElementById("box");

    observer.observe(box, {

        characterData: true

    });

    box.textContent = "Hi";

    // 控制台无输出

</script>

characterDataOldValue记录旧值:

如果要记录文本旧值,可以设置 characterDataOldValuetrue

<!-- 一个性感的div -->

<div id="box">Hello</div>

<script type="text/javascript">

    let observer = new MutationObserver(

        (mutationRecords) => console.log(mutationRecords.map((x) => x.oldValue))

    );

    // 获取文本节点

    let textNode = document.getElementById("box").childNodes[0];

    observer.observe(textNode, {

        // 观察文本变化

        characterData: true,

        // 保留旧数据

        characterDataOldValue: true,

    });

    // 修改文本两次

    textNode.textContent = "Hi";

    textNode.textContent = "Nice";

    // 控制台打印:

    //    (2) ['Hello', 'Hi']

</script>

因为div内的内容原本为Hello,先修改为Hi,又修改为Nice,所以两次修改的旧值就为:Hello 和 Hi 了。

3. 观察子节点

MutationObserver 实例也可以观察目标节点子节点的变化。

<!-- 一个性感的div -->

<div id="box">Hello</div>

<script type="text/javascript">

    let observer = new MutationObserver(

        (mutationRecords) => console.log(mutationRecords)

    );

    // 获取div

    let box = document.getElementById("box");

    observer.observe(box, {

        // 观察子节点变化

        childList: true,

    });

    // 添加元素

    let span = document.createElement("span")

    span.textContent = "world";

    box.appendChild(span);

    // 控制台打印:

    //    [MutationRecord]

</script>

MutationRecord中的addedNodes属性记录了增加的节点。

移除节点:

<!-- 一个性感的div -->

<div id="box">Hello</div>

<script type="text/javascript">

    let observer = new MutationObserver(

        (mutationRecords) => console.log(mutationRecords)

    );

    // 获取div

    let box = document.getElementById("box");

    observer.observe(box, {

        // 观察子节点变化

        childList: true,

    });

    // 移除第一个子节点,就是Hello文本节点

    box.removeChild(box.childNodes[0]);

    // 控制台打印:

    //    [MutationRecord]

</script>

MutationRecord中的removedNodes属性记录了移除的节点。

移动节点:

对于已有的节点进行移动,那么会记录两条MutationRecord记录,因为移动现有的节点是先删除,后添加。

<!-- 一个性感的div -->

<div id="box">Hello<span>world</span></div>

<script type="text/javascript">

    let observer = new MutationObserver(

        (mutationRecords) => console.log(mutationRecords)

    );

    // 获取div

    let box = document.getElementById("box");

    observer.observe(box, {

        // 观察子节点变化

        childList: true,

    });

    // 将span节点移动到Hello节点前面

    box.insertBefore(box.childNodes[1], box.childNodes[0]);

    // 移动节点,实际是先删除,后添加。

    // 控制台打印:

    //    (2) [MutationRecord, MutationRecord]

</script>

4. 观察子树

上面观察的节点都是当前设置的目标节点,比如body,就只能观察body元素和其子节点的变化。

如果要观察body及其所有后代节点的变化,那么可以设置subtree属性为true

<!-- 一个性感的div -->

<div id="box">Hello<span>world</span></div>

<script type="text/javascript">

    let observer = new MutationObserver(

        (mutationRecords) => console.log(mutationRecords)

    );

    let box = document.getElementById("box");

    observer.observe(box, {

        attributes: true,

        // 观察子树的变化

        subtree: true

    });

    // span元素的id属性变化就可以观察到

    box.childNodes[1].id = "text";

    // 控制台打印:

    //    [MutationRecord]

</script>

subtree设置为true后,不光div元素本身,span元素也可以观察到了。

总结:

  • 1. MutationObserver实例可以用来观察对象。
  • 2. MutationRecord实例记录了每一次的变化。
  • 3. 回调函数需要所有脚本任务完成后,才会执行,即采用异步方式。
  • 4. 可以观察的访问有属性、文本、子节点、子树。

到此这篇关于JavaScriptMutationObServer监听DOM元素详情的文章就介绍到这了,更多相关JavaScript中MutationObServer监听DOM元素内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JavaScript监听一个DOM元素大小变化

    1.需求场景 开发过程中经常遇到的一个问题就是如何监听一个 div 的size变化. 比如我用canvas绘制了一个chart,当canvas的size发生变化的时候,需要重新绘制里面的内容,这个时候就需要监听resize事件做处理.window上虽然有resize事件监听,但这并不能满足我们的需求,因为很多时候,div的size发生了变化,实际 window.resize 事件并未触发. 对于div的resize事件的监听,实现方式有很多,比如定时器检查,通过scroll事件等等,本文主要介绍

  • JavaScript WebAPI、DOM、事件和操作元素实例详解

    目录 WebAPI DOM DOM树 DOM获取元素方式 document对象属性 事件 事件的使用步骤 事件的类型 操作元素 操作元素内容 操作元素属性 操作元素样式 排他思想 H5自定义属性 总结 WebAPI API:应用程序编程接口,是一些预先定义的函数,由某个软件开放给开发人员使用的,帮助开发者实现某种功能,开发人员无须访问源码.无须理解其内部工作机制细节,只需知道如何使用即可 简单理解: API 是给程序员提供的一种工具,以便能更轻松的实现想要完成的功能 WebAPI:主要针对浏览器

  • JavaScript中MutationObServer监听DOM元素详情

    一.基本使用 可以通过MutationObserver构造函数实例化,参数是一个回调函数. let observer = new MutationObserver(() => console.log("change")); console.log(observer); observer对象原型链如下: MutationObserver实例: 可以看到有disconnect.observer.takeRecords方法. 1. observer方法监听 observer方法用于关联

  • Javascript DOM的简介,节点和获取元素详解

    目录 DOM 节点 元素节点: 文本节点: 属性节点: 获取元素 getElementById() getElementsByTagName() getElementsByClassName() 总结: DOM 文档:DOM中的"D",当创建一个网页并把它加载到Web浏览器中时,它把编写的网页文档转换为一个文档对象. 对象:DOM中的"O",对象是一种自给自足的数据集合.与某个特定对象相关联的变量被称为这个对象的属性,只能通过某个特定对象去调用的函数被称为这个对象的

  • JavaScript如何动态监听DOM元素高度详解

    背景 考虑这样一种情况,产品同学希望达到以下功能: 在我们的网页中有一个固定区域,这个区域会用于渲染从后端拉取的含有图片等资源的富文本字符串. 他需要在内容不超过一个最大高度的时候完全显示所有内容,超过最大内容后仅展示最大高度范围内的内容,超出部分隐藏,并通过一个按钮 “展示更多” 来给用户展示更多的选择. 在这看似简单的需求当中,其实涉及到了一个难点,那就是怎样动态的监听到内容区域的高度变化? 因为在这里面会含有图片资源,他们在渲染的时候会发起网络请求,等待图片加载完成后触发浏览器重排,该区域

  • JavaScript中添加监听句柄的方式

    目录 一.效果展示 二.句柄合集 三.添加监听的方式 1.将事件与函数绑定在一起 2.先获取元素再添加事件 前言: 监听就是触发某事件之后做出的响应,监听句柄是触发某相应的条件 一.效果展示 鼠标聚焦.鼠标移开.鼠标点击等等都可以作为监听句柄 二.句柄合集 onchange    HTML 元素改变 onclick    用户点击 HTML 元素 onmouseover    用户在一个HTML元素上移动鼠标 onmouseout    用户从一个HTML元素上移开鼠标 onkeydown  

  • 使用jQuery监听DOM元素大小变化

    起因 今天写页面的时候突然有这么个需求,由于父元素(一个DIV)的height是由javascript计算出来的固定的值,而在其中增加了一个多说插件,在用户评论后,子元素(DIV)的height属性增加,导致子元素溢出.但是又不知道如何为多说的评论按钮增加回调函数,于是乎就想到了根据子元素的大小变化来重新计算父元素的height. onresize? 平常,都是在整个浏览器窗口变化时触发一个修改布局的回调函数.使用的是window对象的resize事件,利用: window.onresize =

  • vue监听dom大小改变案例

    需求描述:layout左边菜单栏收缩,右边的content区域的swiper宽度没有改变(没有图,朋友的问题,大体画一下) 类似于点击折叠左边目录会变小,右边内容区域会变大,但是swiper在刚开始的时候就确定了宽度,所以我的想法是监听右边宽度大小去updata一下.但是我用vue的watch监听$refs.swiper.offsetwidth失败了!!!!但是宽度确实是在改变的很费解,先说一下解决方法把 1.使用element-resize-detector var elementResize

  • Vue实现监听某个元素滚动,亲测有效

    目录 监听某个元素滚动,亲测有效 监听dom元素滚动到了可视区? 监听某个元素滚动,亲测有效 Vue 开发,有时候只需要监听某个元素是否滚动就行了,不需要去监听整个页面. Vue 有自带的 @scroll 但是并没有什么用,给某个滚动元素加上,滚动该元素并不会调用,加上 CSS 支持滚动样式也一样. 怎么监听呢?通过 addEventListener 与 @mousewheel 配合实现 addEventListener: 增加的是拖拽滚动条也能监听到滚动 @mousewheel:添加的是非拖拽

  • Android 中的监听和按键处理详情

    目录 各种监听 按键处理 onKeyDown() onBackPressed() Fragment中监听Back返回键 各种监听 我们来练习下各种监听.我们在 TextView 上添加了触摸监听,在 Button 上添加了长按监听,在 Spinner 下拉框选项发生变化的时候添加了监听,在 ListView 选中选项时增加了监听. xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  • JavaScript 中的文档对象模型 DOM

    目录 1.什么是DOM 2.选择元素 3.getElementById() 4.querySelector() 5.querySelectorAll() 6.添加新元素 7.更改CSS样式 8.如何监听事件 1.什么是DOM DOM 文档对象模型,是 HTML 和 XML 文档的编程接口,用一个逻辑树来表示一个文档,树的每个分支的终点都是一个节点(node),每个节点都包含着对象(objects),允许从文档中创建.更改或删除元素,还可以向这些元素添加事件,使页面更加动态. DOM 将 HTML

  • AngularJS中watch监听用法分析

    本文实例讲述了AngularJS中watch监听用法.分享给大家供大家参考,具体如下: ANGULAR 监听使用: 当angular数据模型发生变化时,我们需要如果需要根据他的变化触发其他的事件. $watch是一个scope函数,用于监听模型变化,当你的模型部分发生变化时它会通知你. $watch(watchExpression, listener, objectEquality); watchExpression 需要监控的表达式 listener 处理函数,函数参数如下  function

  • vue点击input弹出带搜索键盘并监听该元素的方法

    1.遇到问题: 需要做一个点击input弹出带搜索的键盘. 解决: input的type="search",可弹出带搜索的键盘.并监听搜索按钮,请求数据 <input @keyup.13="show()" type="search"> 2.但是又遇到一个新的问题: 点击搜索之后键盘没有收回. 解决: 通过$refs获取input 监听搜索按钮,添加.blur() <input @keyup.13=show() type=&quo

随机推荐