jQuery使って闇を生み出すのはいやだけどjQueryでやったほうが幸せになることだってあるんじゃよ……
今回はjQueryのattr
とporp
の使い分けの話です。
本題:ここ
jQueryあるあるのattrとprop問題
WebをやるのにjQueryは2016年の終わろうとしている今でも世界中で重宝されています。罪ですね。
最近はjQueryでは手に負えなくなって、あるいはjQueryの「コレジャナイ」間に気付いて……まあ様々な理由の上でReactやVue.jsのようなFWが使われるようなことが多くなってきましたが、jQueryは適材適所で使われ続けています。FWと併用されることもあります。闇ですね。
※ 新しく使う場合は3.x系を使いましょう……
そんなjQueryですが、1.6よりそれまでのattr
はattr
とprop
に分離されました。(5年半前)
で、これは当時も混乱する人が多かったようですが、今でも後を絶たないことが明白です。(私調べ)
間違えるとattr警察の方々に殺されます、というより動きません。
では、どうやって覚えればいいのでしょうか。
まあ具体例示せよ
dataの扱いは特殊なので基本的にattr使うようにしてるんですが……基本的にはこのQiita記事の言うとおりです。
jqueryのattrとprop(とdataとvalとhtmlとかつまり属性とかの取得)の使い分け一覧 – Qiita
で、肝心のattrとpropのおぼえかた
attr
は名前の通りHTML(DOM)の属性、prop
はプロパティです。
つまり、生DOMを想定してelement.hoge
でアクセスできるプロパティはprop
を、それ以外の属性(生DOMのAPIではelement.attributes.hoge.value
(readonly)だとか element.getAttribute('type')
/element.setAttribute('name', 'value')
のような形でアクセスする必要のある)についてはattr
を使えばいいんです。
生DOMがわからないからjQuery使ってるんだよなんて言わないでください、jQueryはあくまでもDOMを扱いやすくするためのライブラリでしか無いんだから……
といっても分かりにくいかもしれないので1つサンプルを用意しました。
さて、このPenをRunし、「input」の上で右クリック⇒検証(Chromeの場合)でこのinputにフォーカスが当たっている状態のインスペクタを開きましょう。
こんな感じになってるはずです。
ちなみに、ChromeやSafariではこの状態でコンソールを開けば$0
で当該要素にアクセスできます。
たとえば、$0.value
とすれば、"input"
が返ってきます。
また、$0.getAttribute('value')
でも同様の値が得られています。
では、プレビュー画面よりこのinputの値を適当な文字に変えてみましょう。
ここで、もう一度DOMツリーを見てみましょう。
このinputはDOMツリー上では<input value="input">
のままになっています。
では、コンソール上でプロパティをみると……
反映が確認できます。jQueryの$formElement.val()
はこの値ですね。
同様に属性を見てみると……
inputのままです。属性はプロパティではないので状態保存はしていません。
同様に、下にあるチェックボックスをインスペクタでフォーカスしましょう。
ただの<input type="checkbox">
です。
チェックボックスのチェック状態を保持するプロパティはcheckbox.checked
ですね。
$0.checked
はこの場合false
が返ってくるはずです。
また、checked
属性は付けていないので$0.getAttribute('checked')
はnullを返します。
先程同様、こちらもチェックボックスをクリックしチェックします。
DOMツリー上では何も変化はないように見えますが、$0.checked
はtrue
を返すようになります。
なお、属性はnullのままです。
checkboxで$el.attr(‘checked’)をすると死ぬというのはまさにこれですね。
チェックボックスやinputにおける属性は初期値でしか無いのです。既に存在するフォームのvalueはこれでは変わりません。
textarea
の要素内の文字列も同様です。
もちろん、jQueryの:checked
セレクタはこのプロパティを利用していました。(最近はdocument.querySelectorの方使ってたっけ?)
では、DOMに変化がない場合のみがプロパティの使うべきか、とうと残念ながらそういうわけではありません。
再びinputの方にインスペクタのフォーカスを当て、その横の[toggle]をクリックしてみましょう。inputが無効化されます。
この無効化というのはinputのプロパティである$0.disabled
で切り替えているわけなのですが、この操作を行うと当該の要素にdisabled
属性が現れます。なんということでしょう。
いきなり言ってることが真逆で意味がわからないかもしれませんが、これは属性を直接切り替えても操作することが可能です。
とはいえ、DOMの操作にはプロパティを使うようにしましょう。
基本的にbooleanや数値など文字列型以外が現れるならばそれはプロパティです。もちろん、element.className
のように属性に直接影響するプロパティも多々存在しますが、data-*
のようなHTML(DOM)を媒介したデータのやり取りにはattr
,属性を利用することになります。(今時殆どのブラウザがdataset
対応してるんだからそれでいいじゃないのみたいなのも……ウッ)
おまけ: a
要素のhrefを取得する
DOMにおけるA
要素は面白い存在で、 HTMLHyperlinkElementUtilsを継承しているためhref
のURLを詳細に操作することが出来ます。(他にarea
要素も同様)
具体的にはURLと同様の似たインタフェースが実装されています。
具体的には、リンク先のURLについてhrefにセットしたURL以外にも、<base>
を計算した上でのorigin
やhostname
、port
(URIに明示的に含まれている場合のみ)、pathname
等がa要素のプロパティとして取得できます。
もちろん、これらのプロパティはjQueryのprop
で取得が可能です。
たとえば、以下のpenはa要素の持つ全てのプロパティを表示させたものです。(関係ないのも全て表示してるので最悪)
なお、このhref
プロパティも計算済みのフルの値になるため、実際にHTMLにあるURLを取得したければ属性、つまりattr
で取得が必要なわけですね。(メニューのactiveページ判定にこれを使っているコードがあるのを見かけました……)
もちろん入力値以外で相対URLを吐き出す機能なんてものは用意されていないので自力で実装するかなんかしてやる必要がありますが、常識的に考えてそんなシチュはシチュはまずないかと思います。有って堪るか
まとめ
attr
とprop
の使い分けの見抜き方の話でした。ほとんど脱線した……
結論としてはattr
はdata-*
以外殆ど使わない、prop
が使えるならばそっち。
そもそもprop
よりもval
やaddClass
のようなメソッドが用意されているならばそっちを使おう、ということです。