JS里关于事件的常被考察的知识点:事件派发、事件流、事件广播、原生JS实现事件代理

事件派发dispatchEvent

  dispatchEvent 向指定目标事件派发一个事件;一般的事件触发是用户进行某些操作时才会触发,而使用dispatchEvent可以在代码中手动触发事件。

  定义事件的目的就是为了执行某一方法,手动触发事件的目的也是执行该事件下的方法。

window.onload = function(){
  var btn = document.querySelector('#click');
  btn.addEventListener('click', function(e){  //为元素绑定事件监听
  }, false);
  var event = new Event('click');  //创建一个click事件
  btn.dispatchEvent(event);  //派发事件
}

事件派发的使用场景

1、触发自定义事件:浏览器自带事件一般由浏览器接收某些操作之后触发,而自定义事件的触发需要使用dispatchEvent来进行手动触发。

2、触发浏览器标准事件:根据需求决定,某些操作如果正好与某个元素事件的触发一致,且该事件很好模拟,就可以触发该事件来达到某些需要的执行结果。

3、使用Jquery来兼容浏览器:Jquery中的trigger方法就是用来触发事件的。

事件派发的作用

1、派发数据:将封闭在模块中的数据传递给另一个封闭模块

2、完成较为复杂的解耦

事件和回调函数区别

1、如果需要完成内容后执行函数,回调函数就只能在一个地方调用;而事件却可以在任何地方接收到数据。

2、回调函数与当前的代码紧密关联,如果修改一个地方,可能会造成错误;但事件机制通过事件的侦听获取,不管发送还是接收、删除,修改后都不会引起任何相关联的错误。

实例代码解析

1、封装的 JS 文件(实现加载图片为例)
// 回调函数版
var Method=(function () {
    return {
        loadImage:function (arr,callback) {
            var img=new Image();
            img.arr=arr;
            img.list=[];
            img.num=0;
            img.callback=callback;
            img.addEventListener("load",this.loadHandler);
            img.self=this;
            img.src=arr[img.num];
        },
        loadHandler:function (e) {
            this.list.push(this.cloneNode(false));
            this.num++;
            if(this.num>this.arr.length-1){
                this.removeEventListener("load",this.self.loadHandler);
                //全部加载完成后通过回调函数将list回调抛出
                this.callback(this.list);
                return;
            }
            this.src=this.arr[this.num];
        },
    }
})();
// 派发事件版
var Method=(function () {
    return {
        EVENT_ID:"event_id",
        loadImage:function (arr) {
            var img=new Image();
            img.arr=arr;
            img.list=[];
            img.num=0;
            img.addEventListener("load",this.loadHandler);
            img.self=this;
            img.src=arr[img.num];
        },
        loadHandler:function (e) {
            this.list.push(this.cloneNode(false));
            this.num++;
            if(this.num>this.arr.length-1){
                this.removeEventListener("load",this.self.loadHandler);
                //事件派发类型必须与事件侦听接收类型一致,这样事件才会收到
                //创建一个事件对象
                var evt=new Event(Method.EVENT_ID)
               //将list作为事件对象的属性
                evt.list=this.list;
                //派发事件
                document.dispatchEvent(evt);
                return;
            }
            this.src=this.arr[this.num];
        },
    }
})();
<script> var arr=["img/a.jpeg","img/b.jpeg","img/c.jpeg","img/d.jpeg","img/e.jpeg"]; Method.loadImage(arr); // 此处Method为上面封装的方法,将arr图片地址数组放入 // 给document添加监听自定义的那个事件 document.addEventListener(Method.EVENT_ID,loadFinishHandler); function loadFinishHandler(e) {   // 图片加载完就可以获取到图片list,从event对象上拿到list   console.log(e.list); } </script>
// 另一个封装js文件里:
(function () {
    document.addEventListener(Method.EVENT_ID,loadFinishHandler);
    function loadFinishHandler(e) {
      //这里仍然能获取到图片list
        console.log(e.list);
    }
})();

  通过使用事件派发机制完成了较为复杂的解耦,代码之间耦合度降低,事件机制通过事件的侦听获取,因此不管发送还是接收,删除修改后都不会引起任何相关联的错误,提供了一种将一个封闭模块中的数据传递给另一个封闭模块方法

JS里面的事件流

  DOM2级事件模型中规定了事件流的三个阶段:捕获阶段、目标阶段、冒泡阶段,低版本IE(IE8及以下版本)不支持捕获阶段

  捕获事件流:Netscape提出的事件流,即事件由页面元素接收,逐级向下,传播到最具体的元素。

  冒泡事件流:IE提出的事件流,即事件由最具体的元素接收,逐级向上,传播到页面。

  关于js事件,这里有一篇非常详细的介绍,可以看下:http://www.cnblogs.com/hyaaon/p/4630128.html

IE和W3C不同绑定事件解绑事件的方法有什么区别,参数分别是什么,以及事件对象e有什么区别

  绑定事件:

  W3C:target.addEventListener(event, listener, useCapture);

  event —— 事件类型;

  listener —— 事件触发时执行的函数;

  useCapture —— 指定事件是否在捕获或冒泡阶段执行,为true时事件句柄在捕获阶段执行,为false(默认false)时,事件句柄在冒泡阶段执行。

btn.addEventListener('click',function(){
    //do something...
},false)

  对应的事件移除:

removeEventListener(event,function,capture/bubble);

  IE:target.attachEvent(type, listener);

  type - 字符串,事件名称,含“on”,比如“onclick”、“onmouseover”、“onkeydown”等。

  listener —— 实现了 EventListener 接口或者是 JavaScript 中的函数。

btn.attachEvent('onclick',function(){
    //do something...
})

  对应的事件移除:

detachEvent(event,function);

事件的委托(代理 Delegated Events)的原理以及优缺点

  委托(代理)事件是那些被绑定到父级元素的事件,但是只有当满足一定匹配条件时才会被挪。这是靠事件的冒泡机制来实现的,

  优点是:

  (1)可以大量节省内存占用,减少事件注册,比如在table上代理所有td的click事件就非常棒 

  (2)可以实现当新增子对象时无需再次对其绑定事件,对于动态内容部分尤为合适

  缺点是:

  事件代理的应用常用应该仅限于上述需求下,如果把所有事件都用代理就可能会出现事件误判,即本不应用触发事件的被绑上了事件。

var toolbar = document.querySelector(".toolbar");
toolbar.addEventListener("click", function(e) {
  var button = e.target;
  if(!button.classList.contains("active"))
    button.classList.add("active");
  else
    button.classList.remove("active");
});

手写原生js实现事件代理,并要求兼容浏览器

// 简单的事件委托
function delegateEvent(interfaceEle, selector, type, fn) {
    if(interfaceEle.addEventListener){
        interfaceEle.addEventListener(type, eventfn);
    }else{
        interfaceEle.attachEvent("on"+type, eventfn);
    }
      
    function eventfn(e){
        var e = e || window.event;   
        var target = e.target || e.srcElement;
        if (matchSelector(target, selector)) {
            if(fn) {
                fn.call(target, e);
            }
        }
    }
}
/**
 * only support #id, tagName, .className
 * and it's simple single, no combination
 */
function matchSelector(ele, selector) {
    // if use id
    if (selector.charAt(0) === "#") {
        return ele.id === selector.slice(1);
    }
    // if use class
    if (selector.charAt(0) === ".") {
        return (" " + ele.className + " ").indexOf(" " + selector.slice(1) + " ") != -1;
    }
    // if use tagName
    return ele.tagName.toLowerCase() === selector.toLowerCase();
}

//调用
var odiv = document.getElementById("oDiv");
delegateEvent(odiv,"a","click",function(){
    alert("1");
})

 

posted @ 2018-07-26 23:25  古兰精  阅读(1830)  评论(0编辑  收藏  举报