模拟事件和自定义事件

事件

  • 事件类型 :表示事件的类型。如:MouseEvent(鼠标事件)、KeyboardEvent(键盘事件)
  • 事件名称 :表示事件的名称。如:click(单击)、dblclick(双击)。
  • 事件目标 : 表示与发生事件相关的目标对象。
  • 事件处理程序(event handler):指处理事件的函数,即发生事件时,需调用的函数。

常用方法

  • addEventListener()
  • removeEventListener()
  • dispatchEvent()
  • detachEvent()

Event对象

属性:

bubbles、cancelable、currentTarget、eventPhase、target、timeStamp、type

方法:

preventDefault()
阻止默认事件的方法,调用此方法是,连接不会被打开,但是会发生冒泡,冒泡会传递到上一层的父元素;

stopPropagation():

阻止事件的冒泡方法,不让事件向documen上蔓延,但是默认事件任然会执行,当你掉用这个方法的时候,如果点击一个连接,这个连接仍然会被打开

stopImmediatePropagation():

阻止事件捕获


另外:
return false ;

同时阻止事件冒泡也会阻止默认事件;连接不会被打开,事件也不会传递到上一层的父元素;可以理解为return false就等于同时调用了event.stopPropagation()和event.preventDefault()

currentTarget 与 target 属性的区别

currentTarget :获取正在处理此事件的对象(也可以理解为绑定此事件的对象)

target :获取触发此事件的对象。

冒泡阶段时两者的区别:

假设body和Button元素都注册了click事件;当点击Button元素时,body的click事件也会触发,此时在body的click事件内,currentTarget指向body元素,target指向Button元素。

示例:点击Button元素时的显示结果

document.body.onclick = function (e) {
    console.log(e.currentTarget); // 指向body元素
    console.log(e.target); // 若是冒泡事件时,指向最开始触发的元素。否则为元素自身。
};

document.getElementById('btn').onclick = function (e) {
    console.log(e.currentTarget);
    console.log(e.target);
};

事件的注册与注销

注册事件:
addEventListener(),attachEvent()

注销事件:
removeEventListener()、detachEvent()

注册事件的方式大致有2种:一种属性注册,另一种通过方法注册。

  • 属性注册方式又可分为在HTML元素内的事件属性赋值和通过JS指定元素对象的事件属性

    <button onclick="sayHello()">点击</button>
    
  • 方法注册方式可通过addEventListener()或attachEvent()方法进行事件的注册

通过JS设置元素对象的属性为事件处理程序

示例1:事件属性注册的演示

// 注册body的click事件
document.body.onclick = function (e) {
    alert(1);
};

示例2:事件属性注册的唯一性

document.body.onclick=function(e){
    console.log(1);
}

// 会覆盖前面注册的事件处理程序
document.body.onclick=function(e){
    console.log(2);
}

document.body.click(); // => 2 :只输出后面属性注册的

addEventListener()方法注册事件

示例1:多次注册同一事件,按注册顺序执行,先输出1,再输出2

document.body.addEventListener('click',function(e){
     console.log('1');
});

document.body.addEventListener('click',function(e){
    console.log('2');
});

document.body.click(); // => 1,2

示例2:使用函数对象多次注册同一事件:只当注册一次

function sayHello(){
    console.log('hello');
}

document.body.addEventListener('click',sayHello);
// 使用处理程序多次注册同一事件,只当注册一次
document.body.addEventListener('click',sayHello);

document.body.click(); // => hello :只输出一遍

IE9之前的IE版本可通过attachEvent()方法注册事件。

注销事件

示例1:通过removeEventListener()注销事件

function sayHello(e) {
    console.log('1');
}

// 注册body click事件
document.body.addEventListener('click', sayHello);

// 注销body click事件的sayHello函数
document.body.removeEventListener('click',sayHello);

document.body.click(); // 触发click事件,不输出任何结果

若第二个参数为函数体,将不会注销

detachEvent(eventName, function Object)

说明:注销通过attachEvent()注册的事件处理程序。

语法:EventTarget.detachEvent(eventName, eventHandlerObj)

参数:

  • eventName {string} :所要注销的事件名称,区分大小写。这里的名称跟事件属性一样,以”on”开头,后面跟着事件名称。如:onclick、onload。
  • eventHandlerObj {function Object} ::函数对象。传入一个函数体是没有效果的。
通过detachEvent()注销事件
function sayHello() {
    console.log('1');
}
document.body.attachEvent('onclick', sayHello);

document.body.detachEvent('onclick', sayHello); // 注销事件

document.body.click(); // 不输出结果     

若第二个参数为函数体,将不会注销

多次注册与多次注销

因为attachEvent()可以把一个函数对象多次注册到元素同一个事件上,所以调用一次detachEvent()只能注销掉一次。

function sayHello() {
    console.log('1');
}
document.body.attachEvent('onclick', sayHello);
document.body.attachEvent('onclick', sayHello); // 注册了2次

document.body.click(); // => 1 1:输出2次

document.body.detachEvent('onclick', sayHello); // 注销1次

document.body.click(); // => 1 :输出结果为1,只注销了1次
给对象的事件属性赋值为null,可取消此事件的所有注册过的处理事件程序。

示例:

document.body.addEventListener('onclick', function(e){
    console.log(1);
});
document.body.addEventListener('onclick', function(e){
    console.log(2);
});

document.body.onclick=null; // onclick属性赋值为null,相当于注销了onclick事件

document.body.click(); // 无操作

事件流与事件委托

三个阶段

  • 捕获阶段(Capture Phase):事件从最外层的window对象到目标节点的父节点依次触发的阶段。(从外到内)
  • 目标阶段(Target Phase):事件在目标节点上触发时的阶段。
  • 冒泡阶段(Bubbing Phase):事件从目标节点的父节点到最外层的window对象依次触发的阶段。(从内到外)

阻止事件流的传播

Event 事件对象的stopPropagation()、stopImmediatePropagation()方法可阻止事件流的后续传播。

1.在捕获阶段调用

说明:在捕获阶段调用stopPropagation()方法时,此元素后续的事件流都会阻止,包括捕获阶段、目标阶段、冒泡阶段。

document.body.addEventListener('click',function(e){
    console.log("事件阶段:"+e.eventPhase+';target:'+e.target+';currentTarget:'+e.currentTarget)
    e.stopPropagation();
},true);

结果:事件流在body的捕获阶段就截至了,后续的阶段都没有执行

2.在目标阶段调用

说明:在目标段调用stopPropagation()方法时,捕获阶段和目标阶段会执行完毕,冒泡阶段不会被执行。

document.getElementById('btn').addEventListener('click',function(e){
console.log("捕获阶段注册:事件阶段:"+e.eventPhase+';target:'+e.target+';currentTarget:'+e.currentTarget)
e.stopPropagation();
    },false);
    document.getElementById('btn').addEventListener('click',function(e){
console.log("冒泡阶段注册:事件阶段:"+e.eventPhase+';target:'+e.target+';currentTarget:'+e.currentTarget)
e.stopPropagation();
    },true);

结果:捕获阶段和目标阶段执行完毕,冒泡阶段未被执行。

3.在冒泡阶段调用

说明:在冒泡段调用stopPropagation()方法时,捕获阶段和目标阶段会执行完毕,元素后续的冒泡阶段不会被执行。

document.body.addEventListener('click',function(e){
    console.log("事件阶段:"+e.eventPhase+';target:'+e.target+';currentTarget:'+e.currentTarget)
    e.stopPropagation();
},false);

结果:捕获阶段和目标阶段执行完毕,body后续的冒泡阶段未被执行

事件委托(Event Delegate)

HTML元素含有嵌套关系,并且事件流含有冒泡阶段。子元素的触发事件会冒泡到父元素的相同事件上。

一般情况只需给子元素注册特定的事件处理程序即可,但当子元素过多或频繁的进行增减操作怎么办?

比如一个ul包含了几十个li元素,对每个li元素进行单独的事件注册会影响性能。而现只要在父元素注册事件监听器,等待li事件触发后的冒泡阶段即可。

简单来说事件委托就是父元素监听子元素的冒泡事件。

Div容器包含了多个li子元素,在Div容器注册事件委托。

HTML代码:

<div id="div">
    <ul id="ul" >
        <li data-key="北京">北京</li>
        <li data-key="上海">上海</li>
        <li data-key="杭州">杭州</li>
    </ul>
</div>

JS代码:

document.getElementById('div').addEventListener('click',function(e){
            var value=e.target.attributes['data-key'].value; // 获取目标阶段元素的'data-key'属性的值
        console.log(value);
            });

顺便附上jquery的方法

$('#div').on('click','li',function(e) {
    var v = $(this).data('key');
    console.log(v);
});

模拟事件

模拟事件,即非实际操作去触发元素的事件。如按钮的点击,不需要实际用鼠标去点击此按钮,而是采用模拟触发此按钮的点击事件。

触发元素的事件可以直接调用事件方法(如:click()触发元素的click事件)。

为何还要单独的模拟触发呢?

与直接触发相比,模拟事件包含以下特点:

  • 模拟特定场景:如触发click事件,可同时模拟是否按下Ctrl、Alt等按键。
  • 可触发自定义事件

模拟事件的创建方式:

  1. 老版:通过document.createEvent()方法创建各事件类型对象。1.
  2. 新版:通过各事件的构造函数创建事件类型对象。

老版本方式将会被新版本方式所替代,不再记录。。

新版创建步骤

  1. 通过各事件类型的构造函数创建一个event对象。
  2. 在元素上注册监听
  3. 调用元素对象的dispatchEvent(event对象)方法进行派发。

    //1、创建事件
    var clickElem = new Event("clickElem");
    
    //2、注册事件监听器
    elem.addEventListener("clickElem",function(e){
        //干点事
    })
    
    //3、触发事件
    elem.dispatchEvent(clickElem);
    

上面用到的Event属于基本事件类型

另外有一种自定义事件的方法叫 观察者模式

即JS设计模式种的 发布者订阅模式

vue的双向数据绑定就用到了此设计模式