event handler(2) onClick属性とonclickプロパティ
「イベントをonclickで付ける」という時次の2つがあり、紛らわしい。
1. HTML内に属性として記述するonClick
<button onclick="alert(1)">push<button>
2. jsファイル、script要素で動的に設定する obj.onclick
btn.onclick = function () { alert(1) }
この2つは結構違いがあるので、詳しく説明する。
2つの違いではない、基本的なことについては前のエントリー参照
目次もどき
目次のような箇条書きのようなリスト。2回目以降は、これだけ見れば十分。
- 呼び方
- 重要なポイント
- 属性
- 大文字と小文字
- 関数のラップ(wrap=つつむ)
- イベントタイプの名前を持つ関数にラップされる
- イベントオブジェクトは「event」という変数名でその関数に渡される
- クロージャとthisの値
- 複製(cloneNode) と イベント
- 属性とプロパティ
- 設定方法と読み
- イベントハンドラーを設定できるオブジェクト
- documentは属性不可
- window
- bodyが代わりに
<body onload>
=body.onload
=window.onload
- イベントとthisの値
<body onload>
と<body onclick>
- bodyが代わりに
- 互換性
- イベントオブジェクト
- イベントハンドラーを設定できるオブジェクト window.onclick と document.onclick
- setAttrubute("onclick", "fn()") はIE8+
呼び方
そもそもこの2つは何と呼び分ければよいのか? HTML5になってできた仕様 によると
HTMLのほうはIDL属性 、 JavaScriptの方はcontent属性 と定義されている。詳しい説明はこの辺参照
- 属性 (HTML) - HTML | MDN
- What does idl attribute mean in the WHATWG html5 standard document? - Stack Overflow
しかし、一般的にはイベント属性/HTML属性とイベントプロパティと呼ばれているので、この記事でもそう呼ぶことにする
重要なポイント
this
,Event
を使うなら属性・プロパティはどちらか一つにしたほうが良い- 属性は使わないが吉
- 基本的に
addEventListener
を使おう
属性
大文字と小文字
属性はよく、<button onClick>のようにcだけ大文字で書かれることがあるが、HTMLでは大文字と小文字は問われない。
全部大文字でONCLICK、混ぜてoNcLiCk でもOK。
onclick
で設定, getAttribute("oNcLicK")
で読み、と一致していなくてもよい。
大文字・小文字は完全に無視される。
関数のラップ
同じイベントハンドラ(関数)を次のように属性とプロパティで設定したとき、function.toString()
でそれぞれの関数を確認してみると
<button id="btn1" onclick="foo()">#btn1<button> <button id="btn2">#btn2<button> <script> function foo() {} btn2.onclick = foo </script>
onClick属性はイベントタイプと同名の関数にラップされる
HTML onClick属性: btn1.onclick.toString()
イベントハンドラがfunction onclick
という関数にラップされる
function onclick(event) { foo() }
JS onclick: btn2.onclick.toString()
こちらはそのまま
function foo() { }
このため属性ではonclick="foo()"
のように中の関数を実行する形式で書くということですね。
イベントオブジェクト
関数に渡されるイベントオブジェクトは上の通り、「event」という変数名に固定される。JSではよくイベントハンドラの引数にe
やev
を使うが、これだと属性で動かない。
var logValue = function(ev) { console.log(ev.target.value) } document.body.setAttribute("onclick", "logValue(ev)") // ev is undefined document.body.setAttribute("onclick", "logValue(event)") // ok
クロージャとthisの値
関数がクロージャになるので、イベント属性ではthis
の値が window(=globalのthis) になる
<button onclick="alert(this)">1</button> <button onclick="foo()">2</button> <button id="b3">3</button> <script> function alertThis() { alert(this) } b3.onclick = alertThis </script>
方法 | thisの値 |
---|---|
属性 + インライン | HTMLButtonElement |
属性 + 関数 | window(Strict modeでundefined) |
プロパティ | HTMLButtonElement |
つまり、this
を使っていると単純な書き換えは出来ない。
- 属性にthis.value, this.hrefとかがあったり
- 関数内でevent.currentTargetの代わりにthisを使っている場合
要素の複製とイベント
cloneNode
, cloneContents
などで複製するとイベントハンドラもコピーされる。理由は単純に属性だから。idやtitleなどと同じ。
属性とプロパティ
設定方法と読み
設定法によってプロパティ・属性での読みに違いが出る。
body.getAttribute("onclick") は 属性で設定時だけ、
body.onclick は 属性・プロパティ両方で反映
document.body.setAttribute("onclick", "console.log(1)") // (A)とすると document.body.getAttribute("onclick") // 反映(A) document.body.onclick // 反映(A) document.body.onclick = function(){} // (B) document.body.getAttribute("onclick") // 反映されない。(A)のまま document.body.onclick // 反映される(B)
また、属性で設定した場合だけHTMLソースに反映される。
基本的にはon[event]
プロパティで読むべき。
イベントハンドラを設定できるオブジェクトの違い
属性を使う方法では、windowとdocumentにイベントを設定できない
- windowのイベントはbodyが代わりになる。詳しくは後述。
- documentのイベントは設定できない。
もっとも(on)readystatechange
しかないけれど。
話が少し脱線するが、DOMContentLoaded
など、イベントハンドラでは設定不可能なイベントもあるので結局イベントハンドラは使わないほうが楽。イベントリスナを使おう。
window
bodyが代わりになる
(a)window.onload
や window.onbeforeprint
などのwindow[on]eventと
(b)body.on[event] が同じになる
window.onload = function(){} // windowだけに設定 document.body.onload === window.onload // true bodyも同じイベントが参照される
これはイベントハンドラに限った話なのでイベントリスナでは関係ない
var fn = function(){ alert(1) } document.body.onload = fn // ok document.body.setAttribute("onload", "fn()") // ok document.body.addEventListener("load", fn) // 何も起きない
イベントとthisの値
bodyがwindowの代わりになるときだけ、thisの参照はwindowになる
<body onload="alert(this)" onclick="alert(this)"> <!-- この時の結果 onload - window onclick - body -->
互換性
IE8以下の話
イベントオブジェクト
attachEventの時と同じ。
window.event
, event.srcElement
などを使う。
IE8以下での関数のラップ: eventが渡されていないのが分かる。なのでwindow.eventで捕捉する。
<button onclick="foo()">
// btn.onclickの結果 function onclick() { foo() }
イベントハンドラーを設定できるオブジェクト
windowにDOMイベントが設定不可
例)window.onclick, window.onkeydown
document以下に設定可
setAttributeでの設定
IE7以下で不可
setAttribute
で設定できる属性の種類に制限がある。
setAttribute("class", "foo")
が出来ないのと同じ。