MutationObserver

  • 版本网址缩略名: DOM/MutationObserver
  • 版本标题: MutationObserver
  • 版本 id: 349499
  • 创建于:
  • 创建者: ziyunfei
  • 是否是当前版本?
  • 评论

修订内容

MutationObserver给开发者们提供了一种能在某个范围内的DOM树发生变化时作出适当反应的能力.该API设计用来替换掉在DOM3事件规范中引入的Mutation事件.

构造函数

MutationObserver()

该构造函数用来实例化一个新的Mutation观察者对象.

MutationObserver(
  function callback
);
参数
callback
该回调函数会在指定的DOM节点(目标节点)发生变化时被调用.在调用时,观察者对象会传给该函数两个参数,第一个参数是个包含了若干个MutationRecord对象的数组,第二个参数则是这个观察者对象本身.

实例方法

void observe( {{domxref("Node")}} target, optional MutationObserverInit options );
void disconnect();
Array takeRecords();

observe()

给当前观察者对象注册需要观察的目标节点,在目标节点(还可以同时观察其后代节点)发生DOM变化时收到通知.

void observe(
  {{domxref("Node")}} target,
  optional MutationObserverInit options
);
参数
target
观察该节点是否会发生DOM变化.
options
一个MutationObserverInit对象,指定要观察的DOM变化类型.

disconnect()

让该观察者对象停止观察指定目标的DOM变化.直到再次调用其observe()方法,该观察者对象包含的回调函数都不会再被调用.

void disconnect();

takeRecords()

清空观察者对象的记录队列,并返回里面的内容.

Array takeRecords();
返回值

返回一个包含了MutationRecords对象的数组.

MutationObserverInit

MutationObserverInit是一个用来配置观察者对象行为的对象,该对象可以拥有下面这些属性:

注:  childList, attributes, 或者characterData三个属性中必须至少有一个为true.否则,会抛出异常"An invalid or illegal string was specified".
属性 描述
childList 如果需要观察目标节点的子节点(新增了某个子节点,或者移除了某个子节点),则设置为true.
attributes 如果需要观察目标节点的属性节点(新增或删除了某个属性,以及某个属性的属性值发生了变化),则设置为true.
characterData 如果目标节点为characterData节点(一种抽象接口,具体可以为文本节点,注释节点,以及处理指令节点)时,也要观察该节点的文本内容是否发生变化,则设置为true.
subtree 除了目标节点,如果还需要观察目标节点的所有后代节点(观察目标节点所包含的整棵DOM树上的上述三种节点变化),则设置为true.
attributeOldValue

attributes属性已经设为true的前提下,如果需要将发生变化的属性节点之前的属性值记录下来(记录到下面MutationRecord对象的oldValue属性中),则设置为true.

characterDataOldValue characterData属性已经设为true的前提下,如果需要将发生变化的characterData节点之前的文本内容记录下来(记录到下面MutationRecord对象的oldValue属性中),则设置为true.
attributeFilter 一个属性名数组(不需要指定命名空间),只有该数组中包含的属性名发生变化时才会被观察到,其他名称的属性发生变化后会被忽略.

MutationRecord

MutationRecord对象会作为第一个参数传递给观察者对象包含的回调函数,该对象有下面这些属性:

属性 类型 描述
type String 如果是属性发生变化,则返回attributes.如果是一个CharacterData节点发生变化,则返回characterData,如果是目标节点的某个子节点发生了变化,则返回childList.
target {{domxref("Node")}} 返回此次变化影响到的节点,具体返回那种节点类型是根据type值的不同而不同的. 如果typeattributes,则返回发生变化的属性节点所在的元素节点,如果type值为characterData,则返回发生变化的这个characterData节点.如果typechildList,则返回发生变化的子节点的父节点.
addedNodes {{domxref("NodeList")}} 返回被添加的节点,或者为null.
removedNodes {{domxref("NodeList")}} 返回被删除的节点,或者为null.
previousSibling {{domxref("Node")}} 返回被添加或被删除的节点的前一个兄弟节点,或者为null.
nextSibling {{domxref("Node")}} 返回被添加或被删除的节点的后一个兄弟节点,或者为null.
attributeName String 返回变更属性的本地名称,或者为null.
attributeNamespace String 返回变更属性的命名空间,或者为null.
oldValue String

根据type值的不同,返回的值也会不同.如果type为 attributes,则返回该属性变化之前的属性值.如果type为characterData,则返回该节点变化之前的文本数据.如果type为childList,则返回null.

例子

下面的例子来自这篇博文.

// Firefox和Chrome早期版本中带有前缀
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver

// 选择目标节点
var target = document.querySelector('#some-id');
 
// 创建观察者对象
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log(mutation.type);
  });    
});
 
// 配置观察选项:
var config = { attributes: true, childList: true, characterData: true }
 
// 传入目标节点和观察选项
observer.observe(target, config);
 
// 随后,你还可以停止观察
observer.disconnect();

假设target为当前文档中某个已知的节点,observer为某个已经实例化的MutationObserver观察者对象,则:

observer.observe(target, {childList:true})                              //childList属性只观察子节点的新建与删除,子节点本身的任何变化都不会去理会  
target.appendChild(document.createElement("div"))                       //添加了一个元素子节点,触发回调函数.
target.appendChild(document.createTextNode("foo"))                      //添加了一个文本子节点,触发回调函数.
target.removeChild(target.childNodes[0])                                //移除第一个子节点,触发回调函数.
target.childNodes[0].appendChild(document.createElement("div"))         //为第一个子节点添加一个子节点,不会触发回调函数,如果需要触发,则需要设置subtree属性为true.


observer.observe(target, {childList:true,subtree:true})                 //subtree属性让观察行为进行"递归",这时,以target节点为根节点的整棵DOM树发生的变化都可能会被观察到 
observer.observe(document, {childList:true,subtree:true})               //如果target为document或者document.documentElement,则当前文档中所有的节点添加与删除操作都会被观察到
observer.observe(document, {childList:true,attributes:true,characterData:true,subtree:true})   //当前文档中几乎所有类型的节点变化都会被观察到(包括属性节点的变化和文本节点的变化等)


observer.observe(target, {childList:true})                              //假设此时target的outHTML内容为<div>foo<div>,则: 
target.childNodes[0].data = "bar"                                       //不会触发回调函数,因为childList只观察节点的新建与删除,而这里target节点的子节点仍然只有一个,没有多,没有少
observer.observe(target, {childList:true,characterData:true})           //加上characterData属性,允许观察文本节点的变化,行不行? 
target.childNodes[0].data = "bar"                                       //还是不会触发回调函数,因为发生变化的是target节点的子节点,我们目前的目标节点只有一个,就是target.
observer.observe(target, {childList:true,characterData:true,subtree:true})  //加上subtree属性,观察所有后代节点 
target.childNodes[0].data = "bar"                                       //触发了回调函数,发生变化的是target节点的文本子节点(必须同时有characterDatasubtree属性,才能观察到一个元素目标节点里的文本内容的变化)


observer.observe(target, {attributes:true})                             //只观察目标节点的属性节点
target.setAttribute("foo","bar")                                        //不管foo属性存在不存在,都会触发回调函数
target.setAttribute("foo","bar")                                        //即使前后两次的属性值一样,还是会触发回调函数
target.removeAttribute("foo")                                           //移除foo属性节点,触发回调函数
target.removeAttribute("foo")                                           //不会触发回调函数,因为已经没有属性节点可移除了
observer.observe(target, {attributes:true,attributeFilter:["bar"]})     //指定要观察的属性名
target.setAttribute("foo","bar")                                        //不会触发回调函数,因为attributeFilter数组不包含"foo"
target.setAttribute("bar","foo")                                        //触发了回调函数,因为attributeFilter数组包含了"bar"

外部链接

浏览器兼容性

{{ CompatibilityTable() }}

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
Basic support 18 {{ property_prefix("webkit") }} {{ CompatGeckoDesktop(14) }} {{ CompatUnknown() }} {{ CompatUnknown() }} {{ CompatVersionUnknown() }}
Feature Android Chrome for Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Basic support {{ CompatUnknown() }} {{ CompatUnknown() }} {{ CompatGeckoMobile(14) }} {{ CompatUnknown() }} {{ CompatUnknown() }} {{ CompatUnknown() }}

 

修订版来源

<p><code>MutationObserver</code>给开发者们提供了一种能在某个范围内的<a href="/zh-CN/docs/DOM">DOM</a>树发生变化时作出适当反应的能力.该API设计用来替换掉在DOM3事件规范中引入的<a href="/zh-CN/docs/DOM/Mutation_events">Mutation事件</a>.</p>
<h2 id="Constructor" name="Constructor">构造函数</h2>
<h3 id="MutationObserver()" name="MutationObserver()"><code>MutationObserver()</code></h3>
<p>该构造函数用来实例化一个新的Mutation观察者对象.</p>
<pre class="eval">
MutationObserver(
  function callback
);
</pre>
<h6 id="Parameters" name="Parameters">参数</h6>
<dl>
  <dt>
    <code>callback</code></dt>
  <dd>
    该回调函数会在指定的DOM节点(目标节点)发生变化时被调用.在调用时,观察者对象会传给该函数两个参数,第一个参数是个包含了若干个<a href="#MutationRecord"><code>MutationRecord</code></a>对象的数组,第二个参数则是这个观察者对象本身.</dd>
</dl>
<h2 id="Instance_methods" name="Instance_methods">实例方法</h2>
<table class="standard-table">
  <tbody>
    <tr>
      <td><code>void <a href="#observe()">observe</a>( {{domxref("Node")}} target, optional <a href="#MutationObserverInit">MutationObserverInit</a> options );</code></td>
    </tr>
    <tr>
      <td><code>void <a href="#disconnect()">disconnect</a>();</code></td>
    </tr>
    <tr>
      <td><code>Array <a href="#takeRecords()">takeRecords</a>();</code></td>
    </tr>
  </tbody>
</table>
<h3 id="observe()" name="observe()"><code>observe()</code></h3>
<p>给当前观察者对象注册需要观察的目标节点,在目标节点(还可以同时观察其后代节点)发生DOM变化时收到通知.</p>
<pre class="eval">
void observe(
  {{domxref("Node")}} target,
  optional <a href="#MutationObserverInit"><code>MutationObserverInit</code></a> options
);
</pre>
<h6 id="Parameters" name="Parameters">参数</h6>
<dl>
  <dt>
    <code>target</code></dt>
  <dd>
    观察该节点是否会发生DOM变化.</dd>
  <dt>
    <code>options</code></dt>
  <dd>
    一个<a href="#MutationObserverInit"><code>MutationObserverInit</code></a>对象,指定要观察的DOM变化类型.</dd>
</dl>
<h3 id="disconnect()" name="disconnect()"><code>disconnect()</code></h3>
<p>让该观察者对象停止观察指定目标的DOM变化.直到再次调用其<a href="#observe()"><code>observe()</code></a>方法,该观察者对象包含的回调函数都不会再被调用.</p>
<pre class="eval">
void disconnect();
</pre>
<h3 id="takeRecords()" name="takeRecords()"><code>takeRecords()</code></h3>
<p>清空<code>观察者对象的</code>记录队列,并返回里面的内容.</p>
<pre class="eval">
Array takeRecords();
</pre>
<h6 id="Return_value" name="Return_value">返回值</h6>
<p>返回一个包含了<a href="#MutationRecord"><code>MutationRecord</code>s</a>对象的数组.</p>
<h2 id="MutationObserverInit" name="MutationObserverInit"><code>MutationObserverInit</code></h2>
<p><code>MutationObserverInit是一个用来配置观察者对象行为的对象,该对象可以拥有下面这些属性</code>:</p>
<div class="note">
  注:&nbsp; <code>childList</code>, <code>attributes</code>, 或者<code>characterData</code>三个属性中必须至少有一个为<code>true</code>.否则,会抛出异常"An invalid or illegal string was specified".</div>
<table class="standard-table">
  <tbody>
    <tr>
      <td class="header">属性</td>
      <td class="header">描述</td>
    </tr>
    <tr>
      <td><code>childList</code></td>
      <td>如果需要观察目标节点的子节点(新增了某个子节点,或者移除了某个子节点),则设置为<code>true</code>.</td>
    </tr>
    <tr>
      <td><code>attributes</code></td>
      <td>如果需要观察目标节点的属性节点(新增或删除了某个属性,以及某个属性的属性值发生了变化),则设置为<code>true</code>.</td>
    </tr>
    <tr>
      <td><code>characterData</code></td>
      <td>如果目标节点为<code>characterData节点(一种抽象接口,具体可以为文本节点</code>,注释节点,以及处理指令节点<code>)</code>时,也要观察该节点的文本内容是否发生变化,则设置为<code>true</code>.</td>
    </tr>
    <tr>
      <td><code>subtree</code></td>
      <td>除了目标节点,如果还需要观察目标节点的所有后代节点(观察目标节点所包含的整棵DOM树上的上述三种节点变化),则设置为<code>true</code>.</td>
    </tr>
    <tr>
      <td><code>attributeOldValue</code></td>
      <td>
        <p>在<code>attributes</code>属性已经设为<code>true的前提下,如果需要将发生变化的</code>属性节点之前的属性值记录下来(记录到下面MutationRecord对象的oldValue属性中),则设置为<code>true</code>.</p>
      </td>
    </tr>
    <tr>
      <td><code>characterDataOldValue</code></td>
      <td>在<code>characterData</code>属性已经设为<code>true的前提下,如果需要将发生变化的</code><code>characterData</code><code>节点之前的</code>文本内容记录下来(记录到下面MutationRecord对象的oldValue属性中),则设置为<code>true</code>.</td>
    </tr>
    <tr>
      <td><code>attributeFilter</code></td>
      <td>一个属性名数组(不需要指定命名空间),只有该数组中包含的属性名发生变化时才会被观察到,其他名称的属性发生变化后会被忽略.</td>
    </tr>
  </tbody>
</table>
<h2 id="MutationRecord" name="MutationRecord"><code>MutationRecord</code></h2>
<p><code>MutationRecord</code>对象会作为第一个参数传递给观察者对象包含的回调函数,该对象有下面这些属性:</p>
<table class="standard-table">
  <tbody>
    <tr>
      <td class="header">属性</td>
      <td class="header">类型</td>
      <td class="header">描述</td>
    </tr>
    <tr>
      <td><code>type</code></td>
      <td><code>String</code></td>
      <td>如果是属性发生变化,则返回<code>attributes</code>.如果是<code>一个CharacterData</code>节点发生变化,则返回<code>characterData</code>,如果是目标节点的某个子节点发生了变化,则返回<code>childList.</code></td>
    </tr>
    <tr>
      <td><code>target</code></td>
      <td><code>{{domxref("Node")}}</code></td>
      <td>返回此次变化影响到的节点,具体返回那种节点类型是根据<code>type值的不同而不同的</code>. 如果<code>type</code>为<code>attributes</code>,则返回发生变化的属性节点所在的元素节点,如果<code>type</code>值为<code>characterData</code>,则返回发生变化的这个<code>characterData</code>节点.如果<code>type</code>为<code>childList</code>,则返回发生变化的子节点的父节点.</td>
    </tr>
    <tr>
      <td><code>addedNodes</code></td>
      <td><code>{{domxref("NodeList")}}</code></td>
      <td>返回被添加的节点,或者为<code>null</code>.</td>
    </tr>
    <tr>
      <td><code>removedNodes</code></td>
      <td><code>{{domxref("NodeList")}}</code></td>
      <td>返回被删除的节点,或者为<code>null</code>.</td>
    </tr>
    <tr>
      <td><code>previousSibling</code></td>
      <td><code>{{domxref("Node")}}</code></td>
      <td>返回被添加或被删除的节点的前一个兄弟节点,或者为<code>null</code>.</td>
    </tr>
    <tr>
      <td><code>nextSibling</code></td>
      <td><code>{{domxref("Node")}}</code></td>
      <td>返回被添加或被删除的节点的后一个兄弟节点,或者为<code>null</code>.</td>
    </tr>
    <tr>
      <td><code>attributeName</code></td>
      <td><code>String</code></td>
      <td>返回变更属性的本地名称,或者为<code>null</code>.</td>
    </tr>
    <tr>
      <td><code>attributeNamespace</code></td>
      <td><code>String</code></td>
      <td>返回变更属性的命名空间,或者为<code>null</code>.</td>
    </tr>
    <tr>
      <td><code>oldValue</code></td>
      <td><code>String</code></td>
      <td>
        <p>根据<code>type值的不同,返回的值也会不同</code>.如果type为 <code>attributes</code>,则返回该属性变化之前的属性值.如果type为<code>characterData</code>,则返回该节点变化之前的文本数据.如果type为<code>childList</code>,则返回<code>null</code>.</p>
      </td>
    </tr>
  </tbody>
</table>
<h2 id="Example_usage" name="Example_usage">例子</h2>
<p>下面的例子来自<a class="external" href="http://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/" rel="freelink">这篇博文</a>.</p>
<pre class="brush: js">
// Firefox和Chrome早期版本中带有前缀
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver

// 选择目标节点
var target = document.querySelector('#some-id');
 
// 创建观察者对象
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log(mutation.type);
  });    
});
 
// 配置观察选项:
var config = { attributes: true, childList: true, characterData: true }
 
// 传入目标节点和观察选项
observer.observe(target, config);
 
// 随后,你还可以停止观察
observer.disconnect();
</pre>
<p>假设<code>target</code>为当前文档中某个已知的节点,<code>observer</code>为某个已经实例化的<code>MutationObserver</code>观察者对象,则:</p>
<pre class="brush: js">
observer.observe(target, {<code>childList:true</code>})                              //<code>childList属性只观察子节点的新建与删除,子节点本身的任何变化都不会去理会</code>  
target.appendChild(document.createElement("div"))                       //添加了一个元素子节点,触发回调函数.
target.appendChild(document.createTextNode("foo"))                      //添加了一个文本子节点,触发回调函数.
target.removeChild(target.childNodes[0])                                //移除第一个子节点,触发回调函数.
target.childNodes[0].appendChild(document.createElement("div"))         //为第一个子节点添加一个子节点,不会触发回调函数,如果需要触发,则需要设置<code>subtree属性为true</code>.


observer.observe(target, {<code>childList:true</code>,<code>subtree:true</code>})                 //<code>subtree属性让观察行为进行"递归",这时,以target节点为根节点的整棵DOM树发生的变化都可能会被观察到</code> 
observer.observe(document, {<code>childList:true</code>,<code>subtree:true</code>})               //<code>如果target为document或者document.documentElement,则当前文档中所有的节点添加与删除操作都会被观察到</code>
observer.observe(document, {<code>childList:true</code>,<code>attributes:true,</code><code>characterData:true</code>,<code>subtree:true</code>})   //<code>当前文档中几乎所有类型的节点变化都会被观察到(包括属性节点的变化和文本节点的变化等)

</code>
observer.observe(target, {<code>childList:true</code>})                              //<code>假设此时</code>target的outHTML内容为&lt;div&gt;foo&lt;div&gt;,则: 
target.childNodes[0].data = "bar"                                       //不会触发回调函数,因为childList只观察节点的新建与删除,而这里target节点的子节点仍然只有一个,没有多,没有少
observer.observe(target, {<code>childList:true</code>,<code>characterData:true</code>})           //加上<code>characterData属性,允许观察文本节点的变化,行不行?</code> 
target.childNodes[0].data = "bar"                                       //还是不会触发回调函数,因为发生变化的是target节点的子节点,我们目前的目标节点只有一个,就是target.
observer.observe(target, {<code>childList:true</code>,<code>characterData:true</code>,<code>subtree:true</code>})  //加上<code>subtree</code><code>属性,观察所有后代节点</code> 
target.childNodes[0].data = "bar"                                       //触发了回调函数,发生变化的是target节点的文本子节点(必须同时有<code>characterData</code><code>和</code><code>subtree属性,才能观察到一个元素目标节点里的文本内容的变化</code>)


observer.observe(target, {<code>attributes</code><code>:true</code>})                             //只观察目标节点的属性节点
target.setAttribute("foo","bar")                                        //不管foo属性存在不存在,都会触发回调函数
target.setAttribute("foo","bar")                                        //即使前后两次的属性值一样,还是会触发回调函数
target.removeAttribute("foo")                                           //移除foo属性节点,触发回调函数
target.removeAttribute("foo")                                           //不会触发回调函数,因为已经没有属性节点可移除了
observer.observe(target, {<code>attributes</code><code>:true,</code><code>attributeFilter:["bar"]</code>})     //指定要观察的属性名
target.setAttribute("foo","bar")                                        //不会触发回调函数,因为<code>attributeFilter数组不包含"foo"
</code>target.setAttribute("bar","foo")                                        //触发了回调函数,因为<code>attributeFilter数组包含了"bar"</code>
</pre>
<h2 id="Additional_reading" name="Additional_reading">外部链接</h2>
<ul>
  <li><a class="external" href="http://updates.html5rocks.com/2012/02/Detect-DOM-changes-with-Mutation-Observers" rel="freelink">A brief overview</a></li>
  <li><a class="external" href="http://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/" rel="freelink">A more in-depth discussion</a></li>
  <li><a class="external" href="http://www.youtube.com/watch?v=eRZ4pO0gVWw" rel="freelink">A screencast by Chromium developer Rafael Weinstein</a></li>
  <li><a class="external" href="http://code.google.com/p/mutation-summary/" rel="freelink">The mutation summary library</a></li>
  <li><a class="external" href="http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#mutation-observers" rel="freelink">The DOM4 specification</a> which defines the <code>MutationObserver</code> interface</li>
</ul>
<h2 id=".E6.B5.8F.E8.A7.88.E5.99.A8.E5.85.BC.E5.AE.B9.E6.80.A7">浏览器兼容性</h2>
<p>{{ CompatibilityTable() }}</p>
<div id="compat-desktop">
  <table class="compat-table">
    <tbody>
      <tr>
        <th>Feature</th>
        <th>Chrome</th>
        <th>Firefox (Gecko)</th>
        <th>Internet Explorer</th>
        <th>Opera</th>
        <th>Safari</th>
      </tr>
      <tr>
        <td>Basic support</td>
        <td>18 {{ property_prefix("webkit") }}</td>
        <td>{{ CompatGeckoDesktop(14) }}</td>
        <td>{{ CompatUnknown() }}</td>
        <td>{{ CompatUnknown() }}</td>
        <td>{{ CompatVersionUnknown() }}</td>
      </tr>
    </tbody>
  </table>
</div>
<div id="compat-mobile">
  <table class="compat-table">
    <tbody>
      <tr>
        <th>Feature</th>
        <th>Android</th>
        <th>Chrome for Android</th>
        <th>Firefox Mobile (Gecko)</th>
        <th>IE Mobile</th>
        <th>Opera Mobile</th>
        <th>Safari Mobile</th>
      </tr>
      <tr>
        <td>Basic support</td>
        <td>{{ CompatUnknown() }}</td>
        <td>{{ CompatUnknown() }}</td>
        <td>{{ CompatGeckoMobile(14) }}</td>
        <td>{{ CompatUnknown() }}</td>
        <td>{{ CompatUnknown() }}</td>
        <td>{{ CompatUnknown() }}</td>
      </tr>
    </tbody>
  </table>
</div>
<p>&nbsp;</p>
恢复到这个版本