ホームページ  >  記事  >  ウェブフロントエンド  >  最良の addEvent イベント バインディングはどのようにして誕生したか_javascript スキル

最良の addEvent イベント バインディングはどのようにして誕生したか_javascript スキル

WBOY
WBOYオリジナル
2016-05-16 18:00:34963ブラウズ

相互参照は、スクリプトを作成するときに作成されます。たとえば、次のコードです:

コードをコピーします コードは次のとおりです:

window.onload = function () {
var x = document.getElementsByTagName('H3');
for (var i=0;i{
x[i].onclick = openClose;
x[i].popularElement = x[i].nextSibling; // 簡略化された状況
x[i].関連付けられている要素 = x[ i];
}
}

または、スクリプト言語で最も一般的なクロージャであるクロージャを関数で使用する場合、IE はメモリを再利用できません。閉じた文は、DOM オブジェクトのイベント ハンドラーを登録するときに最も一般的に使用されます。 Novellborn は、このバグを実行して実際に感じることができるいくつかの例を提供しています。
私のお気に入りの QuirkMode は、昨年初めにこのバグに大きな危険が潜んでいることに気づき、Web 開発者に注意を払い、この問題を回避するために最善を尽くす必要があると感じ、全員を奨励するためにチャリティー招待コンテストを開催しました。独自の addEvent/removeEvent ソリューションを送信します。そして昨年 10 月下旬、最終的に彼らが検討した勝者を発表しました。John Resig です。John が勝者となるコードは次のとおりです:
コードをコピー コードは次のとおりです。

function addEvent( obj, type, fn ) {
if ( obj.attachEvent ) {
obj['e' type fn] = fn;
obj[type fn] = function(){obj['e' type fn]( window.event );}
obj.attachEvent( 'on' type, obj[type fn] ); 🎜>} else
obj.addEventListener( type, fn, false );
}
function RemoveEvent( obj, type, fn ) {
if ( obj.detachEvent ) {
obj. detachEvent( 'on ' type, obj[type fn] );
obj[type fn] = null;
obj.removeEventListener( type, fn, false ); >

John を勝者として選択するための QuirkMode の説明を要約すると、上記のコードが最も簡潔で効果的であると同時に、メモリの問題を回避しながら、このキーワードが IE のattachEvent で正常に動作することも賢明に保証されています。もちろん、欠点はまだ存在します。

Netscape 4 および Explorer 5 Mac はサポートされていません。 (国内のプログラマは冷笑するかもしれませんが、海外は広範な互換性を重視しています)removeEventでは
remove obj["e" type fn]が省略されています。
要するに、何があってもシンプルなものが勝つのです。
結果が出るやいなや、多くの出場者やコメンテーターは納得せず、すぐに John のコードのいくつかの問題点を指摘しました:

AddEvent 自体は閉じた文を使用しているため、IE の問題を根本的に解決するものではありませんメモリリークの問題。
IEで同じ種類のイベントが繰り返し登録され、繰り返し実行される場合がある問題は解決しておりません。
その後、数人の専門家が改善された解決策を提案しました:




コードをコピーします
コードは次のとおりです: /* John Resig によるオリジナルのアイデア
Scott Andrew LePera、Dean Edwards、Peter-Paul Koch によって調整
Tino Zijdel によって IE 用に修正 (鮮明)
IE ではこれに注意してくださいメモリ リークが発生し、W3C イベント モデルをサポートするブラウザとまったく同じように機能しません。
- イベントの実行順序が同じではありません (FIFO に対する IE の LIFO)
- に関連付けられた関数同じ要素の同じイベントが複数回実行されると、IE でも複数回実行されます。
*/
function addEvent( obj, type, fn ) {
if (obj.addEventListener)
obj.addEventListener( type, fn, false );
else if (obj.attachEvent) {
obj["e" type fn] = fn;
obj.attachEvent( "on" type, function() { obj[ "e " type fn](); } );
}
}
function RemoveEvent( obj, type, fn ) {
if (obj.removeEventListener)
obj.removeEventListener( type , fn , false );
else if (obj.detachEvent) {
obj.detachEvent( "on" type, obj["e" type fn] ); = null ;
}
}


明らかに、John のコードのいくつかの欠点は修正されています。ただし、メモリリークは依然として存在し、一部のブラウザはまだサポートしていないため、IE の重複登録は避けられません。さらに、コメントによると、複数のイベント ハンドラーが同じオブジェクトに登録されている場合、IE と他のブラウザーの実行順序が異なり、これも隠れた危険です。

数日後、最も厳格と考えられる計画がディーン・エドワーズによって提案されました。 Dean の解決策は異なります。
オブジェクト検出を実行しません
addeventListener/attachEvent メソッドを呼び出しません
このキーワードを正しいコンテキストで実行し続けます
イベント オブジェクト パラメーターを正しく渡します
完全にこれまでのところクロスブラウザー (IE4 と NS4 を含む)
メモリ リークはありません
ディーンのコードは次のとおりです:




コードをコピー
コードは次のとおりです:

// Dean Edwards 著、2005
// http://dean.edwards.name/function ;addEvent(element, type, handler) {
// 各イベント ハンドラにunique ID
// イベント ハンドラー関数に一意の値を設定します
if (!handler.$$guid) handler.$$guid = addEvent.guid
// イベント タイプのハッシュ テーブルを作成します。要素の
if (!element.events) element.events = {};
// 各要素/イベントのペアのイベント ハンドラーのハッシュ テーブルを作成します
var handlers = element.events[type] ;
if (!handlers) {
handlers = element.events[type] = {};
// 既存のイベント ハンドラーを保存します (存在する場合)
// オブジェクトがすでに登録されているイベント ハンドラーがある場合は、それを保持し、最初のイベント ハンドラーとして保存します。
if (element["on" type]) {
handlers[0] = element["on" type]; >}
}
// イベント ハンドラーをハッシュ テーブルに保存します
handlers[handler.$$guid] = handler;
// すべての作業を実行するグ​​ローバル イベント ハンドラーを割り当てます
// 重複した登録を避けながら、統一されたイベント処理のためのグローバル関数を割り当てます。
element["on" type] = handleEvent
}// 一意の ID を作成するために使用されるカウンター
addEvent; . guid = 1;function removeEvent(element, type, handler) {
// ハッシュ テーブルからイベント ハンドラーを削除します
if (element.events && element.events[type]) {
要素を削除. events[type][handler.$$guid];
}
};function handleEvent(event) {
// イベント オブジェクトを取得します (IE はグローバル イベント オブジェクトを使用します)
event =イベント || window.event;
// イベント ハンドラーのハッシュ テーブルへの参照を取得します
// これは、handlerEvent 関数がトリガーされるソース要素によって変化します
var handlers = this.events[ event.type];
// 各イベント ハンドラを実行します
for (var i in handlers) {
//このように書くことで、登録されたイベント ハンドラ関数内の this が正しく参照されるようになります。 handlers[ i]() は機能しません
this.$$handleEvent = handlers[i]
}
}; >このコードは以前よりもはるかに大きくなっていますが、確かに非常に微妙です。ただし、このコードは、イベント ハンドラー関数の戻り値を処理できない、(Object.prototype) の不正な適用により for..in ループが中断される可能性があるなど、他の問題を引き起こしました...すぐに Dean が立ち上げられました。 「更新版」。

一番になるのは本当に難しいです。

現時点では、Dean の最終バージョンが最も包括的なソリューションであるようです。ただ、私の個人的な意見としては、少しうるさいような気がします。私は常に、ブラウザー独自の実装を使用し、それをシンプルに保つことにこだわっています。しかし、私は今でも外国人の厳格な態度に深く感心しています。
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。