デル株式会社

attachEvent() と addEventListener()

イベントハンドラのつけ方もインラインから離れてどんどん変ってきてます。
メソッドによるイベントハンドラの付加は、個人レベルだとあまり多く使われていないようですね。 インラインやプロパティ化してしまう方法に比べたら、初心者には少し使い辛いのかな?

でも、個人レベルのHPでは下位ブラウザ互換とかを真剣に考えずに済みますし、 有名ライブラリなどを利用する人も多いと思います。 そういう場合は、逆にインラインや従来のJavaScriptによるイベントハンドラの付加よりも、 メソッドによるイベントハンドラの付加の方が重宝する場合もあります。

その辺の理由や、従来の方法と違ってるめんなどを少し。
(初心者のイベントのページと内容が少しダブります)

メソッドの利点


■従来の方法
・インラインのイベントハンドラ
<body onload=" 処理や関数() " >

・JavaScriptによるイベントハンドラ
<script type="text/javascript">
window.onload = function(){ 処理 }
</script>

メソッドを使わない方法の場合は従来のこのような書き方になります。
で、このような書き方でもページ全体のスクリプトを把握していれば特に問題はありません。 が、一つ問題になりやすいのが、従来の方法だと同一オブジェクトに同一イベントを付加できないと言う点です。

例えば HEAD内に window.onload = function(){ } があり、BODYタグに onload = "" があると、 同じウィンドウのオンロードイベントを使ってしまってるので、後から書かれたBODYタグのonload が上書きをしてしまい、window.onloadの方が無効になります。 (Operaに関しては共存可能です)

また別々のスクリプトなどで同じ記述 window.onload = が使われていても同じです。(この場合はOperaも共存は無理)

■メソッドによるイベントハンドラの付加
IE、Operaで使える Object.attachEvent()
IE以外で有効な Object.addEventListener()
を使う事で、同一オブジェクトに同一イベントをいくつでも付加できます。
また、従来の方法でつけられているイベントを上書きする事もありませんし、 従来の方法で後から書かれても上書きされる事もありません。

これを使うように心がければ、複数の製作者の違うスクリプトライブラリを使ったり、 新たにスクリプトを付加する時にイベントの干渉を気にしなくて済みます。

window.onload や document.onclick の様な 多くのスクリプトで使われがちなイベントの場合は干渉(上書き)に注意しなくてはいけません。 ライブラリを利用する時も、この点に注意しないと他のスクリプトと共存できないなんて事が起きます。
実際問題としてはイベントだけでなくグローバル変数を乱用してる事が共存を難しくする為のもう一つの大きな要因ではありますが・・・それはまた別に書きます。

現実問題としてまだメソッドを使わないライブラリなども多いですし、 ページ全体を把握しているのであれば、必ずメソッドにしないと駄目と言うわけではありません。 『 必要に応じて 』変更していけば良いかと。

■ 共通で使える関数にする
単純に理解出来るブラウザで実行処理を分けて
function addEv(obj, type, func){
if(obj.addEventListener){ obj.addEventListener(type, func, false); }
else{ if(obj.attachEvent) obj.attachEvent('on' + type, func); }
}

function remEv(obj, type, func){
if(obj.removeEventListener){ obj.removeEventListener(type, func, false); }
else{ if(obj.detachEvent) obj.detachEvent('on' + type, func); }
}
のような物を、document.getElementById()などを短くする関数などと一緒に 常用関数用の共通ライブラリに入れておけば使いやすくなると思います。
ま、イベント削除の方はあまり使う物じゃないですが。 自作関数化する時は、addEvent のような他のスクリプトでも使われてしまいそうな名前は避けましょう。

使い方
addEv(オブジェクト、イベントタイプ、処理関数)
で、オブジェクトに対してイベント = 処理関数 を埋め込む形になります。

このまとめた関数は、イベントを付けるオブジェクト、イベントタイプ、処理関数を渡す事で、 ブラウザ(メソッドがあるかないか)を判別して、使えるほうを使ってイベントを付加します。
IEのattachEvent()はイベントタイプはイベントハンドラその物、Netscape系のaddEventListener()は イベントハンドラから『 on 』を取ったイベントタイプで指定しますので、その処理も入っています。
Operaは どちらのメソッドも使えて、attachEvent()でも イベントタイプだけでもいけますが・・。



気をつける点

■ 順番の不規則
IEでは、attachEvent() で同一オブジェクトに同一イベントを付加していく場合、 必ずしも付加した順序で実行してくれません。
Operaは attachEvent()でも付加した順番に実行されます。 IE以外で使える addEventListener() では NetscpaeもFireFoxもSafariもOperaも 付加した順番通りに実行してくれます。
普段はそれほど順番が重要になる場面が無いとしても、 実行順序が重要になるケースであれば、使い方を工夫しないといけなくなります。

1個飛ばしでカウントアップと言う微妙な規則性があるのですが、開始位置が何処になるのか?などやはり解明できませんでした。

■ 処理関数内でのイベントオブジェクトの取り方
function addEv(obj, type, func){
if(obj.addEventListener){ obj.addEventListener(type, func, false); }
else{ if(obj.attachEvent) obj.attachEvent('on' + type, func); }
}

メソッドを扱うのと同じなのでこのaddEvで説明。
//window に onload イベントを付ける
addEv(window,'load',function(e){ 処理 });

//または
addEv(window,'load',SetAll);
function SetAll(e){

処理

}
イベント発生で呼ばれる関数は、上記の様に無名関数を渡しても良いですし、 ハンドラー(関数名)を渡す事で埋め込む事もできます。
関数側の引数0には勝手にイベントオブジェクトが入ります。
Netscape系ではお馴染みのイベントオブジェクトの受け取り方ですが、 このメソッドを使うイベント付加方法の場合はIEでもこの方法でイベントオブジェクトを受け取る事ができます。

■ 処理関数内での this
従来の方法として、イベントハンドラから貰う時は
<tag onclick = "関数(event, this)" >
の様にインラインのイベントハンドラ部分から、this の形で貰っていました。

document.onclick = function(){

この中のthisは document

}
この場合は windowのonloadイベント(プロパティ?)に関数をセットしていますので、 this は、このイベントがつけられたオブジェクトをそのまま指します。

で、メソッドの場合はイベントが引数0として取れる方法が同じになったのは良いのですが、 今度はthisがIEではwindowを指すようになってしまったようですね。
まぁthisは実際はイベントを付加したオブジェクトを指す物として固定されていて、 イベント発生エレメントを指すものとイコールではないので構わないと言えば構いませんが・・。
例えば
<div>
<a href="#"> jump </a>
</div>
この場合DIVにonlickをつけて
document.getElementById('test').onclick = function(){
//ここでのthisは id=test のDIV
}
この場合アンカーをクリックした場合でも this は DIVを指しますが、イベント発生エレメントは Aタグになります。

今までは処理関数内で
document.onclick = aaa(e){
var ev = e || window.event; //イベントオブジェクト
this //this はそのままで この場合 documentを指す
}
だったのが
addEv(document,'click',function(e){
e // eはそのままイベントオブジェクト
this //thisは IEはwindow、IE以外はdocumentを指す
});
と言う事になります。

■ 処理関数内での return false
フォームのonsubmitやアンカータグに付けるonclickなど、 タグ本来の動きを制御する場合、通常はtrue または false を返す事で、有効無効を指定します。
Aタグのhrefを無効化
<a href="" onclick = "関数(); return false;" >

本来は
<a href="" onclick = "return 関数(); " >
ですが、無効化に決まってる場合は前者で良い

送信前処理で送る・送らないをコントロール
<form action="" method="post" onsubmit="return 関数()">
これらの場合は普通は関数で明確にfalseが戻るようにする事で中止させる事ができます。
Netscpae系のaddEventListener()メソッドを使った場合は、指定された関数内でreturn falseなどの戻り値を返しても、 それが通用しなくなってしまいました。
この場合は
(event).preventDefault()
とする事で、タグの元の動き(1つ前のイベント?)を打ち消す事ができます。
IEは今までどおりreturn falseでOKですし、preventDefault()を理解できないので、 処理関数の中で
function func(e){

処理

/* 従来のreturn false と同じ役目 */
if(e.preventDefault){ e.preventDefault(); }
return false;
}
の様にする必要があります。

■書換えにあたって
既存のスクリプトのイベント付加方法をメソッドに変える時は、 ただイベント付加部分だけを書き換えるだけでは駄目で、
処理関数の中にthisが使われているか?のチェックが必要になります。

イベントの取り方も従来の方法でも別に構わないのですが、
document.onclick = aaa(e){
var ev = e? e.target || event.srcElement; //イベント発生エレメント
}
の様に引数0があるかないか?を指標にしてるような式があれば
document.onclick = aaa(e){
var ev = e.target? e.target || e.srcElement; //イベント発生エレメント
}
の様に書換えを行う必要もでてきます。

また、最後にタグの動きを制御する為に return false を戻してる関数なら、 return falseの部分を
if(e.preventDefault){ e.preventDefault(); }
return false;
と書き換えてやらねばならない事になります。

従って、従来の方法を使ってるライブラリや配布スクリプトなどで、 初心者にメソッドの書換えだけを推奨しても、これらの細かい点に配慮できる知識が無い人にはちょっと無理があると思います。 新たなスクリプトを配布した方が良いです。

また、どのイベントの呼び出しパターンで使われても良い関数を書くとすると、 thisは使わないようにして、丁寧に書く必要が出てきますね。 (他のエレメントを内包しないエレメントを利用するケースならthisもOK)
function aaa(e){
var ev = window.event || e; //イベントオブジェクト
var el = ev.srcElement || ev.target; //イベント発生エレメント
}
の様な書き方をしておく必要もあったりと・・・

■従来の方法で、干渉するイベントを吸収
メソッドとは直接関係無いですが、例えばwindow.onloadが2つの別のスクリプトにある場合は、 一つにまとめたり、メソッドを使う方法に書き換えちゃうのが手ですが、 複雑な処理関数を書きなおしたりするのも面倒だったりする場合、 後から読まれる方、または両方に イベントを吸収する書き方をしてしまう方法もありです。 とりあえずの対処としてはこれで両方が実行されます。
スクリプト1
window.onload = function(e){

処理

}
スクリプト2 (後から読まれる方)
var WinLoad2 = window.onload; //変数に既存のwindow.onload の内容を入れる
window.onload = function(e){
if(WinLoad2){ WinLoad2(e); } //あれば実行(eventオブジェクトも一応渡す)

処理

}
ただ、この例ではBODYタグに onload= "" を書かれてしまったら終わりです。 ライブラリが出回って、目に付く機能を何でもつけたいって言う初心者の人も多いようで・・・。 イベントやグローバル変数とかの干渉を考えて無い人の場合は、少なからずトラブルが起きるようですね。 配布スクリプトを単体でテストしたりせずに、いきなり既存のページに付ける人も結構いますしね。

また元々何らかの戻り値を必要とする処理だった場合は
var ObjClick = Object.onclick;
Object.onclick = function(e){
if(ObjClick){ return ObjClick(e); }

処理

}
の様に、関数に関数を内包するため、return をつけてやら無いと、吸収した元のObject.onclickの戻り値を返す事ができません。
これがreturn falseなどの場合は、必要に応じて処理の後にしておかないと外側の関数の処理がそこで中断されますのでこれも注意。

イベントの削除メソッド

あまり削除する事は無いですが・・・

■従来の方法だとイベントを削除する場合は、処理関数でreturn falseで無効にしたり、 window.onload = null; のような書き方をして消してしまってたりしました。 ただ window.onload = null; の方法だと、同一オブジェクトに同一イベントが1個の前提です。 全て消してしまいますからね。

・メソッドによるイベントハンドラの削除は
IE、Operaで使える Object.detachEvent()
IE以外で有効な Object.removeEventListener()
を使う事で、指定したオブジェクトから指定したイベント処理だけ削除できます。

まとめてしまえば
function remEv(obj, type, funchandle){
if(obj.removeEventListener){ obj.removeEventListener(type, funchandle, false); }
else{ if(obj.detachEvent) obj.detachEvent('on' + type, funchandle); }
}
こんな感じです。
remEv(window, 'load', test);
と言うように、addEv()と同じ形で使います。
ただし、
イベントを削除する為には、イベントを付加する時に 処理関数をハンドラーで渡しておかないと駄目なようです。
function addEv(obj, type, func){
if(obj.addEventListener){ obj.addEventListener(type, func, false); }
else{ if(obj.attachEvent) obj.attachEvent('on' + type, func); }
}
function remEv(obj, type, funchandle){
if(obj.removeEventListener){ obj.removeEventListener(type, funchandle, false); }
else{ if(obj.detachEvent) obj.detachEvent('on' + type, funchandle); }
}

addEv(window, 'load' , function(){ 処理}); //無名関数を渡してるので削除できない

function test(){処理 }
addEv(window, 'load', test); // ハンドラーで渡してるので削除可能
remEv(window, 'load', test); // これで削除
スクリプトの流れで削除する必要がある物は無名関数ではなく、ハンドラーで渡しておきましょう。 多分処理関数がローカル関数であっても、ハンドラー名まで一緒でマッチすれば削除できると思います。 (あまりテストしていません)

イベントはJavaScriptの起動としてとても重要な物なので、 IEとMozillaで何とか調整をはかって欲しいですね。 あと、イベントリストなども簡単に参照(あるのかな?)できて、無名関数でも何でも指定して、削除や変更を行えるように。。


専用ページから申し込むと
So-netより高い3万円CB

案ずるより産むが易し
使ってみれば疑問も解決

XREA+ (plus) 206円/月
( お試し7日間 )

CORE SERVER 428円/月
( お試し15日間 )

ロリポップ 270円/月
( お試し期間10日間 )

ヘテムル 1620円/月
( お試し期間15日間 )

さくら 129円/月
( お試し期間2週間 )

無駄な高額ドメイン管理料金払ってませんか?

バリュードメイン
ムームードメイン