AttributeとProperty

HTMLをDOMで扱う場合、似て非なるAttributeとPropertyという2つの概念を、よく区別しておく必要があります。jQueryでも、これに起因する混乱が起きたことがありました。なお、AttributeとPropertyはどちらも「属性」と訳せますが、混乱を防止するためにこれ以降もアルファベットのまま通して、コードからの取得前のHTMLに書かれているもの、あるいは両方まとめたものを「属性」と呼ぶことにします。

# いきなり まとめ or 概要
* Attribute
 * HTMLの世界の住人
 * HTMLに書いたものがそのまま出る
 * 静的
 * `get/setAttribute()`
 * `$().attr`
* Property
 * JavaScriptの世界の住人
 * 扱いやすいようにパースされている
 * 動的（なこともある）
 * `element.property`
 * `$().prop`

# それぞれの取得・設定の方法
Attributeは「HTMLに書いてあるもの」をDOM経由でやり取りするという形になっていて、DOMオブジェクトの`getAttribute`/`setAttribute`メソッドを使います（jQueryでは`.attr()`メソッドになっています）。

一方で、PropertyはJavaScriptのオブジェクトに直接プロパティとして生えているのですぐ書き換えられますが、jQueryから`.prop()`でアクセスすることもできます。

# 変わらないもの
取得・設定の方法こそ違いますが、`id`や`name`、inputの`type`などといった属性はAttributeにもPropertyにも同じ値で存在しています。また、（対応したブラウザなら）`placeholder`や`max`といったフォームバリデーションまわりの（booleanでない）値も、ものによっては大文字小文字が違うこともありますが、どちらでも操作できます。

# URL属性
`src`や`href`といった属性も、AttributeとPropertyの両方に存在しますが、値が違ってくる場合があります。Attributeは、書いたままの相対パスを返しますが、Propertyは絶対パスに展開した後の値になります（一部のIEでは違う事例もありますが、`.prop`を使う場合にはjQueryが修正してくれます）。

# フォーム属性
## 入力値
フォーム要素へ入力した値の場合、**Propertyのみ**に反映されます。Attributeは、フォームの**初期値**にしか使われません（コードで書き換えても何も起きません）。なお、jQueryの場合は専用の`.val()`があります。

## ブール値
チェックボックスの`checked`などは、上と同様にAttributeだとデフォルト値しか影響しないのですが、それ以外にもうひとつ特徴があって、

| | Attribute | Property|
|----|----|----|
|チェックあり|'checked'|true|
|チェックなし|（値なし）| false|

というように、Attributeでは属性名がそのまま値となっています（XHTML的な書き方の、`checked='checked'`に対応するものです）。一方、Propertyでは、素直にtrue/falseとなっています。これは、それ以外の`selected`や`required`などにも共通します。

# `style`属性
インラインで指定する`style`属性も、AttributeではCSS文字列そのままなのに対して、Propertyでは各CSSプロパティがJavaScriptにも現れて、`elem.style.marginLeft='10px';`のように書くことができます。jQueryでは`.css()`を使えます。

# `class`属性
Attributeとしては`class`ですが、予約語となる言語も多いので、Propertyでは`className`となっています。どちらも、スペース区切りの文字列です。

また、jQueryには`.addClass()`、`.removeClass()`、`.toggleClass()`、`.hasClass()`のような便利な関数がありますが、IE10以降を含めた最近のブラウザには、ネイティブに同様な操作を行える`classList`という機能があります（[MDN](https://developer.mozilla.org/ja/docs/Web/API/Element/classList)）。`elem.classList`に`add`、`remove`、`toggle`、`contains`というメソッドがあって、上のjQueryメソッドと同様に動作します。

# `data-xxx`属性
昔から勝手に属性を拡張することが行われていて、それを公認する形となった`data-`始まりの属性ですが、Attributeとしては通常の属性同様に扱えます。一方で、Propertyとして扱う場合には、IE11以降を含むモダンブラウザでは`elem.dataset`というPropertyで一括され、`data-foo-bar`であれば`elem.dataset.fooBar`として読み書きできます。

一方、jQueryにも似たような`$().data()`がありますが、これは基本的に**`data-`属性とは別系統**で動きます。ただし、最初に続姓として書いてあった値はjQuery側にコピーしてくれますが、**それ以降は全く別個の値として**動作します。HTML側の`data-`属性で何かを制御したいのなら、`$().attr('data-xxx')`とAttributeを取得するのが確実です。

# 非対応の値
モダンブラウザ向けの属性を古いブラウザから見た場合や、そもそもでたらめな属性など、ブラウザ側が対応していない属性をHTMLに書いた場合、Propertyとしてどう変換すればいいのかブラウザ側にはわからないので、Propertyとしての取得は**できません**。逆に、それを生かして、`if('max' in document.createElement('input'))`のように、ブラウザが特定の機能を認識しているかを、それに必要なPropertyの存否を通じて確認することができます。

一方、Attributeとしては、書いてあるそのままを読み書きすればいいだけなので、ブラウザがその属性の意味を認識しているかどうかにかかわらず動作します。

# jQueryの混乱
昔のjQueryは、`.attr()`メソッドでAttributeだけでなくPropertyも操作していたのですが、切り分けがややこしくなってきてしまったので、バージョン1.6で`.prop()`メソッドを独立させることにしました。ところが、チェックボックスなどの操作・取得を`.attr()`で行っていたコードが動かなくなってしまったこと、さらには（上で触れたように）あとからチェックボックスなどのAttributeを操作できたところでほぼ役に立たない、ということもあって大混乱となり、バージョン1.6.1で「ブール値を取る属性だけは`.attr()`でもPropertyを操作する」という救済措置が入って、現在に至っています。

つまり、

* `.val()`や`.css()`のように専用のメソッドがあるもの…それを使う
* フォーム関連の属性…`.prop()`を使う
* URL属性…取得したいもので使い分ける
* `data-`属性…`.attr()`を使う
* ブラウザが非対応な可能性もある属性…`.attr()`を使う

のような使い分けになります。