JavaScriptのMutationObserverでDOMの変化を監視する方法

JavaScriptの[MutationObserver](https://developer.mozilla.org/ja/docs/Web/API/MutationObserver)を用いて、ウェブページにおけるDOMの変化を監視する方法を紹介します。

## 基本的な流れ
どの変化を監視する場合も、基本的には次のような流れとなります。

### 監視ターゲットの取得
```js
const target = document.getElementById('target-id')
```

### オブザーバーの作成
```js
const observer = new MutationObserver(records => {
  // 変化が発生したときの処理を記述
})
```

### 監視の開始
```js
observer.observe(target, {
  // オプションを指定
})
```
### （必要に応じて）監視の停止
```js
observer.disconnect()
```

## 属性の変化を監視
属性の変化を監視するには、オプションの`attributes`を`true`にします。

```js
// 監視ターゲットの取得
const target = document.getElementById('target-id')

// オブザーバーの作成
const observer = new MutationObserver(records => {
  // 変化が発生したときの処理を記述
})

// 監視の開始
observer.observe(target, {
  attributes: true
})
```

### 特定属性の変化を監視
特定の属性の変化を監視するには、オプションの`attributeFilter`に監視対象の属性を指定します。

```js

// 監視ターゲットの取得
const target = document.getElementById('target-id')

// オブザーバーの作成
const observer = new MutationObserver(records => {
  // 変化が発生したときの処理を記述
})

// 監視の開始
observer.observe(target, {
  attributes: true,
  attributeFilter: ['class', 'style']
})
```

## 文字データの変化を監視
文字データの変化を監視するには、オプションの`characterData`を`true`にします。

```js
// 監視ターゲットの取得
const target = document.getElementById('target-id')

// オブザーバーの作成
const observer = new MutationObserver(records => {
  // 変化が発生したときの処理を記述
})

// 監視の開始
observer.observe(target, {
  characterData: true
})
```

## 子要素リストの変化を監視
子要素リストの変化を監視するには、オプションの`childList`を`true`にします。

```js
// 監視ターゲットの取得
const target = document.getElementById('target-id')

// オブザーバーの作成
const observer = new MutationObserver(records => {
  // 変化が発生したときの処理を記述
})

// 監視の開始
observer.observe(target, {
  childList: true
})
```


## オプション
`observe`メソッドの第二引数のオプションで、監視方法を細かく指定できます。

<table>
<tr><th>プロパティ</th><th>型</th><th>指定</th><th>内容</th></tr>
<tr><td>attributes</td><td>Boolean</td><td rowspan="3">いずれか必須</td><td>属性の変化を監視</td></tr>
<tr><td>characterData</td><td>Boolean</td><td>文字データの変化を監視</td></tr>
<tr><td>childList</td><td>Boolean</td><td>子要素リストの変化を監視</td></tr>
<tr><td>attributeOldValue</td><td>Boolean</td><td>任意</td><td>変更前の属性を使用</td></tr>
<tr><td>characterDataOldValue</td><td>Boolean</td><td>任意</td><td>変更前の文字データを使用</td></tr>
<tr><td>attributeFilter</td><td>String[]</td><td>任意</td><td>監視対象とする属性名リスト</td></tr>
<tr><td>subtree</td><td>Boolean</td><td>任意</td><td>子孫ノードを監視対象に含める</td></tr>
</table>

## 例
MutationObserverを用いて、Twitterのタイムライン上に新着ツイートバーが現れた際、自動的にクリックして表示するユーザースクリプトの例です。

```js:Twitterの新着ツイートを自動表示.user.js
// ==UserScript==
// @name         Twitterの新着ツイートを自動表示
// @namespace    https://munieru.jp
// @version      0.1
// @description  Twitterの新着ツイートを自動表示します。
// @author       @munieru_jp
// @match       https://twitter.com/
// @grant        none
// ==/UserScript==

(function () {
  'use strict'

  const CLASS_NEW_TWEETS_BAR = 'new-tweets-bar'

  // 監視ターゲットの取得
  const timeline = document.getElementById('timeline')
  const streamContainer = timeline.querySelector('.stream-container')
  const streamItem = streamContainer.querySelector('.stream-item')

  // オブザーバーの作成
  const observer = new MutationObserver(records => {
    records.forEach(record => {
      Array.from(record.addedNodes)
        .filter(node => isNewTweetsBarElement(node))
        .forEach(newTweetsBar => newTweetsBar.click())
    })
  })

  // 監視の開始
  observer.observe(streamItem, {
    childList: true
  })

  function isNewTweetsBarElement (element) {
    return element.classList.contains(CLASS_NEW_TWEETS_BAR)
  }
})()
```

## 参考リンク
* [MutationObserver - Web API インターフェイス | MDN](https://developer.mozilla.org/ja/docs/Web/API/MutationObserver)
* [MutationRecord - Web API インターフェイス | MDN](https://developer.mozilla.org/ja/docs/Web/API/MutationRecord)