ホームページ ウェブフロントエンド jsチュートリアル JavaScript イベント メカニズムの互換性ソリューションを詳細に説明するコード グラフィックスとテキスト

JavaScript イベント メカニズムの互換性ソリューションを詳細に説明するコード グラフィックスとテキスト

Mar 30, 2017 pm 04:34 PM

この記事のソリューションは、以下を通じて JavaScript ネイティブオブジェクト およびホスト オブジェクト (dom 要素) に使用できます。メソッド events をバインドしてトリガーするには:

または

var input = document.getElementsByTagName('input')[0];
var form = document.getElementsByTagName('form')[0];
Evt.on(input, 'click', function(evt){
    console.log('input click1');
    console.log(evt.target === input);
    console.log(evt.modified);
    //evt.stopPropagation();
    console.log(evt.modified);
});
var handle2 = Evt.on(input, 'click', function(evt){
    console.log('input click2');
    console.log(evt.target === input);
    console.log(evt.modified);
});
Evt.on(form, 'click', function(evt){
    console.log('form click');
    console.log(evt.currentTarget === input);
    console.log(evt.target === input);
    console.log(evt.currentTarget === form);
    console.log(evt.modified);
});
Evt.emit(input, 'click');
Evt.emit(input, 'click', {bubbles: true});
handle2.remove();
Evt.emit(input, 'click');
ログイン後にコピー

After 関数

ネイティブ オブジェクトにイベントを追加するプロセスは主に after 関数で行われます。この関数は主に次のことを行います。

  1. obj に応答関数がある場合は、それをディスパッチャー関数に置き換えます

  2. チェーン構造を使用して、複数のバインドされたイベント関数を順次実行できるようにします

  3. ハンドル オブジェクトを返します。これは次のように削除できます。 Remove メソッドの呼び出し このイベント バインディング

下の図は、after 関数が呼び出される前と後の onlog 関数の

引用を示しています

(呼び出し前)

(呼び出し後) )

詳細な説明については、

noteを参照してください。読者がそれに従って、再度実行できることを願っています

var after = function(target, method, cb, originalArgs){
    var existing = target[method];
    var dispatcher = existing;
    if (!existing || existing.target !== target) {
        //如果target中没有method方法,则为他添加一个方法method方法
        //如果target已经拥有method方法,但target[method]中target不符合要求则将method方法他替换
        dispatcher = target[method] = function(){
            //由于js是此法作用域:通过阅读包括变量定义在内的数行源码就能知道变量的作用域。
            //局部变量在声明它的函数体内以及其所嵌套的函数内始终是有定义的
            //所以在这个函数中可以访问到dispatcher变量
            var results = null;
            var args = arguments;
            if (dispatcher.around) {//如果原先拥有method方法,先调用原始method方法
                //此时this关键字指向target所以不用target
                results = dispatcher.around.advice.apply(this, args);
            }

            if (dispatcher.after) {//如果存在after链则依次访问其中的advice方法
                var _after = dispatcher.after;
                while(_after && _after.advice) {
                    //如果需要原始参数则传入arguments否则使用上次执行结果作为参数
                    args = _after.originalArgs ? arguments : results;
                    results = _after.advice.apply(this, args);
                    _after = _after.next;
                }
            }
        }

        if (existing) {
        //函数也是对象,也可以拥有属性跟方法
        //这里将原有的method方法放到dispatcher中
            dispatcher.around = {
                advice: function(){
                    return existing.apply(target, arguments);
                }
            }
        }
        dispatcher.target = target;
    }

    var signal = {
        originalArgs: originalArgs,//对于每个cb的参数是否使用最初的arguments
        advice: cb,
        remove: function() {
            if (!signal.advice) {
                return;
            }
            //remove的本质是将cb从函数链中移除,删除所有指向他的链接
            var previous = signal.previous;
            var next = signal.next;
            if (!previous && !next) {
                dispatcher.after = signal.advice = null;
                dispatcher.target = null;
                delete dispatcher.after;
            } else if (!next){
                signal.advice = null;
                previous.next = null;
                signal.previous = null;
            } else if (!previous){
                signal.advice = null;
                dispatcher.after = next;
                next.previous = null;
                signal.next = null;
            } else {
                signal.advice = null;
                previous.next = next;
                next.previous = previous;
                signal.previous = null;
                signal.next = null;
            }
        }
    }

    var previous = dispatcher.after;
    if (previous) {//将signal加入到链式结构中,处理指针关系
        while(previous && previous.next && (previous = previous.next)){};
        previous.next = signal;
        signal.previous = previous;
    } else {//如果是第一次使用调用after方法,则dispatcher的after属性指向signal
        dispatcher.after = signal;
    }

    cb = null;//防止内存泄露
    return signal;
}
ログイン後にコピー

互換性を解決するために

IEブラウザIE9以降、DOM2イベント処理プログラムをサポートしています。ただし、古いバージョンの IE ブラウザでは、dom 要素 Event を追加するために、attachEvent メソッドが依然として使用されています。幸いなことに、Microsoft は 2016 年に IE8 のメンテナンスを終了すると発表しました。これは間違いなく、大多数のフロントエンド開発者にとって良いニュースです。ただし、夜明けが来る前に、DOM2 レベルのイベント ハンドラー をサポートしていないブラウザーは、通常、次の点に対処する必要があります:

  1. イベントを複数回バインドする、イベント処理関数の呼び出し順序

  2. イベント処理関数における this キーワードの指摘問題

  3. 標準化イベント イベント オブジェクトは、一般的に使用されるイベント属性をサポートします

attachEvent メソッドを使用してイベント処理関数を追加することはできないため、イベント処理関数の呼び出し順序を保証するために、attachEvent を放棄し、代わりに上記の after によって生成された正のシーケンス チェーン構造を使用しました。

//1、统一事件触发顺序
    function fixAttach(target, type, listener) {
    debugger;
        var listener = fixListener(listener);
        var method = 'on' + type;
        return after(target, method, listener, true);
    };
ログイン後にコピー

イベント処理関数の this キーワードについては、クロージャを通じて解決できます。 (ソース)、例:

この記事もこの方法でこの問題を解決します

//1、统一事件触发顺序
    function fixAttach(target, type, listener) {
    debugger;
        var listener = fixListener(listener);
        var method = 'on' + type;
        return after(target, method, listener, true);
    };

    function fixListener(listener) {
        return function(evt){
            //每次调用listenser之前都会调用fixEvent
            debugger;
            var e = _fixEvent(evt, this);//this作为currentTarget
            if (e && e.cancelBubble && (e.currentTarget !== e.target)){
                return;
            }
            var results =  listener.call(this, e);

            if (e && e.modified) {
                // 在整个函数链执行完成后将lastEvent回归到原始状态,
                //利用异步队列,在主程序执行完后再执行事件队列中的程序代码
                //常规的做法是在emit中判断lastEvent并设为null
                //这充分体现了js异步编程的优势,把变量赋值跟清除代码放在一起,避免逻辑分散,缺点是不符合程序员正常思维方式
                if(!lastEvent){
                    setTimeout(function(){
                        lastEvent = null;
                    });
                }
                lastEvent = e;
            }
            return results;
        }
    }
ログイン後にコピー

イベントの場合 オブジェクトの標準化のために、ie によって提供される既存の属性を標準のイベント属性

function _fixEvent(evt, sender){
        if (!evt) {
            evt = window.event;
        }
        if (!evt) { // emit没有传递事件参数,或者通过input.onclick方式调用
            return evt;
        }
        if(lastEvent && lastEvent.type && evt.type == lastEvent.type){
        //使用一个全局对象来保证在冒泡过程中访问的是同一个event对象
        //chrome中整个事件处理过程event是唯一的
            evt = lastEvent;
        }
        var fixEvent = evt;
        // bubbles 和cancelable根据每次emit时手动传入参数设置
        fixEvent.bubbles = typeof evt.bubbles !== 'undefined' ? evt.bubbles : false;
        fixEvent.cancelable = typeof evt.cancelable !== 'undefined' ? evt.cancelable : true;
        fixEvent.currentTarget = sender;
        if (!fixEvent.target){ // 多次绑定统一事件,只fix一次
            fixEvent.target = fixEvent.srcElement || sender;

            fixEvent.eventPhase = fixEvent.target === sender ? 2 : 3;
            if (!fixEvent.preventDefault) {
                fixEvent.preventDefault = _preventDefault;
                fixEvent.stopPropagation = _stopPropagation;
                fixEvent.stopImmediatePropagation = _stopImmediatePropagation;
            }
            //参考:http://www.php.cn/
            if( fixEvent.pageX == null && fixEvent.clientX != null ) {
                var doc = document.documentElement, body = document.body;
                fixEvent.pageX = fixEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - 
                (doc && doc.clientLeft || body && body.clientLeft || 0);
                fixEvent.pageY = fixEvent.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - 
                (doc && doc.clientTop  || body && body.clientTop  || 0);
            }
            if (!fixEvent.relatedTarget && fixEvent.fromEvent) {
                fixEvent.relatedTarget = fixEvent.fromEvent === fixEvent.target ? fixEvent.toElement : fixEvent.fromElement;
            }
            // 参考: http://www.php.cn/
            if (!fixEvent.which && fixEvent.keyCode) {
                fixEvent.which = fixEvent.keyCode;
            }
        }

        return fixEvent;
    }

    function _preventDefault(){
        this.defaultPrevented = true;
        this.returnValue = false;

        this.modified = true;
    }

    function _stopPropagation(){
        this.cancelBubble = true;

        this.modified = true;
    }

    function _stopImmediatePropagation(){
        this.isStopImmediatePropagation = true;
        this.modified = true;
    }
ログイン後にコピー

に変換する必要があります。 _preventDefault、_stopPropagation、および _stopImmediatePropagation の 3 つの関数では、呼び出された場合、リスナーは実行後に変数に保存されるため、後続のイベント ハンドラーはイベントに基づいて次の処理ステップを実行できます。この関数のシミュレーションでは、クロージャを通じて解決します。

上記の fixListener についても同様で、この形式に直接記述することはできないことに注意してください。

注意すべき点は、イベントを別の目的で標準化していることです。

Evt.emit(input, 'click');// のように、イベント プロセスを制御するパラメータを設定できます。バブリングなし

Evt.emit(input, 'click', {bubbles: true});//Bubbles

私のテストによると、イベントをトリガーするために fireEvent を使用してバブリングを防ぐことはできません。そこで、ここでは Javascript を使用してバブリングプロセスをシミュレートします。同時に、このプロセスではイベント オブジェクトの一意性も保証する必要があります。

// 模拟冒泡事件
    var sythenticBubble = function(target, type, evt){
        var method = 'on' + type;
        var args = Array.prototype.slice.call(arguments, 2);
        // 保证使用emit触发dom事件时,event的有效性
        if ('parentNode' in target) {
            var newEvent = args[0] = {};
            for (var p in evt) {
                newEvent[p] = evt[p];
            }

            newEvent.preventDefault = _preventDefault;
            newEvent.stopPropagation = _stopPropagation;
            newEvent.stopImmediatePropagation = _stopImmediatePropagation;
            newEvent.target = target;
            newEvent.type = type;
        }

        do{
            if (target && target[method]) {
                target[method].apply(target, args);
            }
        }while(target && (target = target.parentNode) && target[method] && newEvent && newEvent.bubbles);
    }

    var emit = function(target, type, evt){
        if (target.dispatchEvent && document.createEvent){
            var newEvent = document.createEvent('HTMLEvents');
            newEvent.initEvent(type, evt && !!evt.bubbles, evt && !!evt.cancelable);
            if (evt) {
                for (var p in evt){
                    if (!(p in newEvent)){
                        newEvent[p] = evt[p];
                    }
                }
            }

            target.dispatchEvent(newEvent);
        } /*else if (target.fireEvent) {
            target.fireEvent('on' + type);// 使用fireEvent在evt参数中设置bubbles:false无效,所以弃用
        } */else {
            return sythenticBubble.apply(on, arguments);
        }
    }
ログイン後にコピー

完全なコードを添付します:






Writing to Same Doc




  
ログイン後にコピー

BSD ライセンスに基づく

KityMinder . Powered by f-cube, FEX Source Bug |

JavaScript イベントの詳細な説明メカニズム互換性ソリューション グラフィックとテキストのコンテンツについては、PHP の中国語 Web サイト (www.php.cn) に注目してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Bluetooth 5.3 と 5.2 バージョンの比較と差異分析 Bluetooth 5.3 と 5.2 バージョンの比較と差異分析 Dec 28, 2023 pm 06:08 PM

現在、多くの携帯電話が Bluetooth バージョン 5.3 をサポートしていると主張していますが、Bluetooth 5.3 と 5.2 の違いは何ですか? 実際、これらは基本的に Bluetooth 5 のその後の更新バージョンであり、ほとんどの性能と機能に大きな違いはありません。 Bluetooth 5.3 と 5.2 の違い: 1. データ レート 1 と 5.3 は、最大 2Mbps のより高いデータ レートをサポートできます。 2. 5.2 は最大 1Mbps しか到達できませんが、5.3 はより高速かつ安定してデータを送信できることを意味します。 2. 暗号化制御の強化 2. Bluetooth 5.3 では、暗号化キーの長さの制御オプションが向上し、セキュリティが向上し、アクセス制御やその他のデバイスへの接続が向上します。 3. 同時に、管理者の制御がよりシンプルになるため、5.2 では不可能だった接続がより便利かつ高速になります。

i7-7700 が Windows 11 にアップグレードできない場合の解決策 i7-7700 が Windows 11 にアップグレードできない場合の解決策 Dec 26, 2023 pm 06:52 PM

i77700 のパフォーマンスは win11 を実行するのに完全に十分ですが、ユーザーは i77700 を win11 にアップグレードできないことがわかります。これは主に Microsoft によって課された制限が原因であるため、この制限をスキップする限りインストールできます。 i77700 は win11 にアップグレードできません: 1. Microsoft が CPU バージョンを制限しているためです。 2. win11 に直接アップグレードできるのは、Intel の第 8 世代以降のバージョンのみです 3. i77700 は第 7 世代として、win11 のアップグレードのニーズを満たすことができません。 4. ただし、i77700はパフォーマンス的にはwin11を快適に使用するのに完全に可能です。 5. したがって、このサイトの win11 直接インストール システムを使用できます。 6. ダウンロードが完了したら、ファイルを右クリックして「ロード」します。 7. ダブルクリックして「ワンクリック」を実行します。

Go 言語は Linux システム上でどの程度互換性がありますか? Go 言語は Linux システム上でどの程度互換性がありますか? Mar 22, 2024 am 10:36 AM

Go 言語は Linux システムとの互換性が非常に優れており、さまざまな Linux ディストリビューション上でシームレスに実行でき、さまざまなアーキテクチャのプロセッサをサポートします。この記事では、Linux システムにおける Go 言語の互換性を紹介し、具体的なコード例を通じてその強力な適用性を示します。 1. Go 言語環境をインストールする Linux システムへの Go 言語環境のインストールは非常に簡単で、対応する Go バイナリ パッケージをダウンロードし、関連する環境変数を設定するだけです。 Ubuntu システムに Go 言語をインストールする手順は次のとおりです。

機内モードで Bluetooth ヘッドフォンを使用できますか? 機内モードで Bluetooth ヘッドフォンを使用できますか? Feb 19, 2024 pm 10:56 PM

現代技術の継続的な発展により、ワイヤレス Bluetooth ヘッドセットは人々の日常生活に欠かせないものになりました。ワイヤレスヘッドホンの登場により両手が解放され、音楽や通話などのエンターテインメントをより自由に楽しめるようになりました。ただし、飛行機に乗るときは、携帯電話を機内モードにするように求められることがよくあります。そこで質問は、機内モードで Bluetooth ヘッドフォンを使用できるかということです。この記事では、この疑問について考察していきます。まず、機内モードの機能と意味を理解しましょう。機内モードは携帯電話の特別なモードです

win10 ソフトウェアとの win11 互換性の問題の詳細な説明 win10 ソフトウェアとの win11 互換性の問題の詳細な説明 Jan 05, 2024 am 11:18 AM

win10システムのソフトウェアは完璧に最適化されていますが、最新のwin11ユーザーにとって、このシステムがサポートされるかどうかは誰もが気になるはずですので、以下ではwin10をサポートしていないwin11ソフトウェアについて詳しく紹介します。一緒に出かけます。 win11 は win10 ソフトウェアをサポートしていますか: 1. Win10 システム ソフトウェア、さらには Win7 システム アプリケーションにも十分な互換性があります。 2. Win11 システムを使用する専門家からのフィードバックによると、現時点ではアプリケーションの非互換性の問題はありません。 3. したがって、自信を持って大胆にアップグレードできますが、一般ユーザーは、Win11 の正式バージョンがリリースされるまでアップグレードする前に待つことをお勧めします。 4. Win11 は互換性が良いだけでなく、Windo も備えています

簡単な JavaScript チュートリアル: HTTP ステータス コードを取得する方法 簡単な JavaScript チュートリアル: HTTP ステータス コードを取得する方法 Jan 05, 2024 pm 06:08 PM

JavaScript チュートリアル: HTTP ステータス コードを取得する方法、特定のコード例が必要です 序文: Web 開発では、サーバーとのデータ対話が頻繁に発生します。サーバーと通信するとき、多くの場合、返された HTTP ステータス コードを取得して操作が成功したかどうかを判断し、さまざまなステータス コードに基づいて対応する処理を実行する必要があります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法を説明し、いくつかの実用的なコード例を示します。 XMLHttpRequestの使用

WIN10の互換性が失われた、それを回復する手順 WIN10の互換性が失われた、それを回復する手順 Mar 27, 2024 am 11:36 AM

1. プログラムを右クリックし、開いたプロパティウィンドウに [互換性] タブがないことを確認します。 2. Win10 デスクトップで、デスクトップ左下のスタートボタンを右クリックし、ポップアップメニューの[ファイル名を指定して実行]を選択します。 3. Win10 の実行ウィンドウが開きます。ウィンドウに「gpedit.msc」と入力し、[OK] ボタンをクリックします。 4. [ローカル グループ ポリシー エディター] ウィンドウが開くので、[コンピューターの構成/管理用テンプレート/Windows コンポーネント] メニュー項目をクリックします。 5. 開いた Windows コンポーネント メニューで [アプリケーション互換性] メニュー項目を見つけ、右側のウィンドウで [プログラム互換性プロパティ ページの削除] 設定項目を見つけます。 6. 設定項目を右クリックし、ポップアップメニューで

PHP 関数の互換性問題が解決できるベストプラクティス PHP 関数の互換性問題が解決できるベストプラクティス May 01, 2024 pm 02:42 PM

PHP 関数の互換性の問題を解決するためのベスト プラクティス: バージョン管理された関数名を使用する (例: array_map_recursive()) 関数のエイリアス (例: functionarray_map($callback,$array){...}) を利用して関数の可用性を確認する (例: if (function_exists('array_map_recursive')){...}) ネームスペースを使用します (例: namespaceMyNamespace{...})

See all articles