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)。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()を使う
のような使い分けになります。