Maison  >  Article  >  interface Web  >  Graphiques de code et texte expliquant en détail la solution de compatibilité du mécanisme d'événements JavaScript

Graphiques de code et texte expliquant en détail la solution de compatibilité du mécanisme d'événements JavaScript

黄舟
黄舟original
2017-03-30 16:34:561112parcourir

La solution de cet article peut être utilisée pour Javascriptnatifobjet Et l'objet hôte (élément dom), liez et déclenchez l'événement des manières suivantes :

ou

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');

Fonction After

Le processus d'ajout d'événements aux objets natifs est principalement complété dans la fonction after. Cette fonction fait principalement les choses suivantes :

    <.>
  1. S'il existe une fonction de réponse dans obj, remplacez-la par la fonction de répartiteur

  2. Utilisez une structure de chaîne pour assurer l'exécution séquentielle de plusieurs fonctions d'événements liées

  3. Renvoie un objet handle, l'appel de la méthode Remove peut supprimer cette liaison d'événement

L'image ci-dessous montre la

référence de la fonction onlog avant et après la fonction après est appelée

(avant l'appel)

(après l'appel)

Pour une explication détaillée, veuillez consulter

Commentaires, j'espère que les lecteurs pourront le suivre et l'exécuter à nouveau

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;
}
Résolution de la compatibilité

Navigateur IEDOM2 est pris en charge depuis le programme IE9 , mais pour les anciennes versions des navigateurs IE, la méthode attachEvent est toujours utilisée pour ajouter des événements aux éléments DOM. Heureusement, Microsoft a annoncé qu'il ne maintiendrait plus IE8. en 2016, ce qui pose sans aucun doute un problème pour la majorité des développeurs front-end. Une bonne nouvelle. Cependant, avant l'aube, la compatibilité des navigateurs qui ne prennent pas en charge les gestionnaires d'événements au niveau DOM2 doit encore être assurée. Les points suivants doivent généralement être traités :

    Liaison d'un événement plusieurs fois, l'ordre d'appel de la fonction de traitement des événements est un problème
  1. Le mot-clé this dans la fonction de traitement des événements pointe vers le problème
  2. Objet d'événement standardisé, prenant en charge les attributs d'événement courants
  3. Depuis l'utilisation du La méthode attachEvent pour ajouter des fonctions de traitement d'événements ne peut pas garantir l'ordre d'appel des fonctions de traitement d'événements, nous avons abandonné attachEvent et utilisé la structure de chaîne de séquence positive générée par after dans l'article

Pour cela. mot-clé dans la fonction de traitement d'événements, il peut être résolu par fermeture (source), tel que :
//1、统一事件触发顺序
    function fixAttach(target, type, listener) {
    debugger;
        var listener = fixListener(listener);
        var method = &#39;on&#39; + type;
        return after(target, method, listener, true);
    };

Cet article résout également ce problème de cette manière

Pour la standardisation des objets événementiels, nous devons convertir les attributs existants qui nous sont fournis par ie en standards
//1、统一事件触发顺序
    function fixAttach(target, type, listener) {
    debugger;
        var listener = fixListener(listener);
        var method = &#39;on&#39; + 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;
        }
    }

Dans les trois fonctions _preventDefault, _stopPropagation et _stopImmediatePropagation, s'il est appelé, l'écouteur le fera utilisez une variable pour enregistrer l'objet événement après l'exécution (voir fixListener) pour le traitement ultérieur de l'événement. Le programme effectue l'étape suivante du traitement en fonction des attributs de l'objet événement. Fonction stopImmediatePropagation, pour la simulation de cette fonction, nous la résolvons également par fermeture.
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 !== &#39;undefined&#39; ? evt.bubbles : false;
        fixEvent.cancelable = typeof evt.cancelable !== &#39;undefined&#39; ? 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;
    }

Notez qu'il ne peut pas être écrit directement sous cette forme. La même chose est vraie pour fixListener ci-dessus.

Il convient de noter que nous standardisons les événements dans un autre but. Vous pouvez définir des paramètres dans la méthode d'émission pour contrôler le processus événementiel, tels que :

. Evt.emit(input, 'click');//Pas de bulles

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

Selon mon Le test utilise fireEvent pour déclencher des événements. {bubbles:false} ne peut pas être défini pour empêcher le bouillonnement, nous utilisons donc ici Javascript pour simuler le processus de bouillonnement. Dans le même temps, l'unicité de l'objet événementiel doit également être garantie dans ce processus.

Ci-joint le code complet :
// 模拟冒泡事件
    var sythenticBubble = function(target, type, evt){
        var method = &#39;on&#39; + type;
        var args = Array.prototype.slice.call(arguments, 2);
        // 保证使用emit触发dom事件时,event的有效性
        if (&#39;parentNode&#39; 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(&#39;HTMLEvents&#39;);
            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(&#39;on&#39; + type);// 使用fireEvent在evt参数中设置bubbles:false无效,所以弃用
        } */else {
            return sythenticBubble.apply(on, arguments);
        }
    }

Carte cérébrale :





Writing to Same Doc




  

KityMinder sous licence BSD . Propulsé par f-cube, FEX Source Bug Contact | Nous

Ce qui précède est le contenu des images de code et du texte

qui expliquent en détail la solution de compatibilité du mécanisme d'événement JavaScript. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (www. php.cn) !

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn