AttributeとPropertyの怪奇現象

こんにちは。新卒で入社後、新規事業への配属を経て、8月から[Unipos](https://unipos.me/ja/)のWeb開発チームに所属しているシンバです。Web開発の世界に入門しながらElmを書いてます。

これまでGolangとAndroidをやっていることが多かった自分が、Web開発に入門して出会った謎を追った話をまとめました。（Elmコードがちょこっと話に出ますが本筋には関係ないです）

# この記事を3行で
- Webの世界には、一口に属性といっても`Attribute`と`Property`がある
- 例えば`muted`属性について、propertyとして付けるのと`setAttribute()`を使うのとでは挙動が違う
- Chromeでvideoタグをつかった動画の自動再生がうまくいかなかったら、`muted`属性をpropertyとして付けることを検討してみてください

# 本題

## 怪奇現象に出会った

先日、画面に自動再生動画を仕込むタスクを担当しました。

videoタグを使い実装することになったのですが、GoogleChromeでは自動再生される条件として、`autoplay`属性に付随して`muted`属性もついていなければならないとのことでした。([この他にも条件はあります](https://developers.google.com/web/updates/2017/09/autoplay-policy-changes)が割愛)

OKつけよう、とはいったもののElm(elm/html1.0.0)には`autoplay`属性をつける`autoplay`関数はあっても、同じように`muted`属性をつけるものが存在しません。代わりに`attribute`関数があります。これで任意の属性をつけられます。

```Main.elm
view : () -> Html ()
view _ =
    video [ autoplay True, attribute "muted" "true", src "./video.mp4", width 640, height 360 ] []
```

ですがどうしたことか、全く自動再生されないわけです。

<img width="303" alt="スクリーンショット 2018-12-06 22.44.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/98265/b9ef5bfc-cbdc-e900-5f3b-93ba269c05d0.png">

devToolsからDOMを見ると`autoplay`も`muted="true"`もついているのに...。

困ってドキュメントをあさっていると、同じように属性をつける`property`関数を発見しました。

```Main.elm
view : () -> Html ()
view _ =
    video [ autoplay True, property "muted" (Encode.bool True), src "./video.mp4", width 640, height 360 ] []
```

先ほど`attribute "muted" "true"`としていた部分を`property "muted" (Json.Encode.bool True)`に置き換えると、見事自動再生されました！

<img width="305" alt="スクリーンショット 2018-12-06 22.48.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/98265/a20b824e-f52c-b3ca-3e96-64250a629957.png">

`muted`がついていないのに...。

## AttributeとProperty

調べていると、一口に属性といっても、`Attribute`と`Property`の2つが存在するとわかりました。

ではAttributeとPropertyはどんな関係なのかと調べてわかったのは、

- HTMLからDOMが生成されるとき、`Attribute`を元にDOMオブジェクトの`Property`が生成される
- `id`や`type`など、AttributeとPropertyの変更が相互に同期されるものもあれば、そうでないものもある
- `muted`は初期化に使われるものであり、DOM生成後にAttributeを変更してもPropertyは変更されない

先ほどはDOMのAttributeのみを確認していたので、Propertyも確認してみると発見がありそうです。

## DOMはPropertyの値で動く

検証です。冒頭のコードを実行したときのpropertyをdevToolsで見てみたところ、

||`attribute`関数を使用|`property`関数を使用|
|---|---|---|
|propertyのmuted|`false`|`true`|

こんな結果になっていました。おお、propertyの`muted`に沿って動いていると言えそうです。

また、HTMLとjsで冒頭のコードと同じように振る舞うコードを用意して、違いを見てみました。

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<video id="videoMuted" autoplay muted src="./video.mp4" width="640" height="360"></video>
<video id="videoMutedSetAttr" autoplay src="./video.mp4" width="640" height="360"></video>
<video id="videoMutedProp" autoplay src="./video.mp4" width="640" height="360"></video>

<script>
  videoMutedSetAttr.setAttribute('muted', true);
  videoMutedProp.muted = true;
</script>
</body>
</html>
```

Chromeで動かした結果はこのとおりでした。

|videoのid|自動再生されたか|propertyのmuted|attributeのmuted|
|---|---|---|---|
|videoMuted|○|true|ついている|
|videoMutedSetAttr|☓|false|ついている|
|videoMutedProp|○|true|ついていない|

<img width="299" alt="スクリーンショット 2018-12-06 22.52.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/98265/9c26ac07-af6c-eaca-3213-2048e66cd3f6.png">

たしかに、DOMはPropertyの値を元に挙動が決定されているようですね！

DOMツリーとAttributeを見ているだけでは分からなかったわけです。

# おわりに
Elmでの開発中に出会ったAttributeとPropertyの違いについて追いかけてみました。Media系はコンポーネント・ライブラリが充実しているためか事例が少なく、なかなか調べるのが難しかったです。
あれこれ試行錯誤して全く見当違いなものも追いかけましたが、歴史の長い、進化の多い分野は調べているだけでも楽しいですね〜〜〜

# 参考

https://javascript.info/dom-attributes-and-properties

https://qiita.com/jkr_2255/items/66a16bd969454ee8b114

https://stackoverflow.com/questions/41656297/html5-video-muted-attribute-not-applied-using-property-but-applied-using-setattr