JavaScript基础篇(四):事件模型

154 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

事件流

在JavaScript中的事件可以看出是和HTML和浏览器中的一种交互操作,而HTML是一种DOM树结构,在绑定父子节点事件后,触发事件时会有一个先后顺序问题,就是事件流概念。

事件流三个阶段:

  • 捕获阶段(从上到下)
  • 事件处于目标触发阶段
  • 冒泡阶段(从下到上)

事件模型

原始事件模型(DOM0)

在HTML中直接绑定

<input type="button" onclick="fun()">
// 或
var btn = document.getElementById('.btn');
btn.onclick = fun;

特性:

  • 只能绑定一次,后绑定的事件会覆盖之前的事件
  • 只支持冒泡,不支持捕获
  • 删除时只需要将对应事件置为null就行 btn.onclick = null;
  • 兼容性好

标准事件模型

在标准事件模型中,包含三个阶段。

方式:

  • 绑定:addEventListener(eventType, handler, useCapture)
  • 移除:removeEventListener(eventType, handler, useCapture)

参数如下:

  • eventType指定事件类型(不要加on)
  • handler是事件处理函数
  • useCapture是一个boolean用于指定是否在捕获阶段进行处理,一般设置为false与IE浏览器保持一致

特性:

  • 支持多个事件

IE事件模型

IE事件模型只支持事件处理阶段和事件冒泡阶段。

方式:

  • 绑定:attachEvent(eventType, handler) // onclick
  • 移除:detachEvent(eventType, handler)

事件对象Event

事件发生以后,会产生一个事件对象,作为参数传给监听函数。浏览器原生提供一个Event对象,所有的事件都是这个对象的实例,或者说继承了Event.prototype对象。

  • Event.preventDefault方法取消浏览器对当前事件的默认行为。该方法只是取消事件对当前元素的默认影响,不会阻止事件的传播。如果要阻止传播,可以使用stopPropagation()stopImmediatePropagation()方法。
  • Event.stopPropagation()方法阻止事件在 DOM 中继续传播,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上其他的事件监听函数。
  • Event.stopImmediatePropagation方法阻止同一个事件的其他监听函数被调用,不管监听函数定义在当前节点还是其他节点。
  • Event.cancelBubble属性是一个布尔值,如果设为true,相当于执行Event.stopPropagation(),可以阻止事件的传播。
  • Event.returnValue阻止事件默认行为
  • Event.target属性返回原始触发事件的那个节点,即事件最初发生的节点。
  • Event.currentTarget属性返回事件当前所在的节点,即事件当前正在通过的节点,也就是当前正在执行的监听函数所在的那个节点。随着事件的传播,这个属性的值会变。

如何手写一个事件监听工具函数

const EventUtil = {
    addEvent: function (element, type, handler) {
        if (element.addEventListener) {
            // 支持 DOM2 级事件,优先使用
            element.addEventListener(type, handler)
        } else if (element.attachEvent) {
            // 支持 IE 事件模型
            element.attachEvent('on' + type, handler)
        } else {
            // 兼容性处理
            element['on' + type] = handler;
        }
    },
    removeEvent: function (element, type, handler) {
        if (element.removeEventListener) {
            // 支持 DOM2 级事件,优先使用
            element.removeEventListener(type, handler)
        } else if (element.detachEvent) {
            // 支持 IE 事件模型
            element.detachEvent('on' + type, handler)
        } else {
            // 兼容性处理
            element['on' + type] = null;
        }
    },
    getTarget: function(event) {
        return event.target || event.srcElement;
    },
    currentTarget: function(event) {
        return event.currentTarget;
    },
    stopPropogation: function(event) {
        if (event.stopPropogation) {
            event.stopPropogation();
        }
        event.cancelBubble = true
    },
    preventDefault: function(event) {
        if (event.preventDefault) {
            event.preventDefault();
        }
        event.returnValue = false
    },
}

事件委托

什么是事件委托?

简单的说,就是把一个元素响应事件(clickkeydown......)的函数委托到另一个元素。

真正绑定事件的是外层元素,而不是目标元素。当事件响应到目标元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。

事件委托的应用场景?

我们有大量列表项,需要对每一项进行事件绑定处理,当我们新增或删除一个列表项时,就要对新增的元素绑定事件,给即将删去的元素解绑事件,这就很麻烦,而且内存消耗是非常大的。所以,这个时候我们就可以用事件委托,把事件绑定在他们公共的父级上,然后执行事件的时候再去匹配目标元素。

事件委托有哪些优点和缺点?

优点:

  • 减少整个页面所需的内存,提升整体性能
  • 动态绑定,减少重复工作

缺点:

  • 只能对有冒泡机制的事件进行委托,而focusblur这些事件没有事件冒泡机制,所以无法进行委托绑定事件。
  • mousemovemouseout这样的事件,虽然有事件冒泡,但是只能不断通过位置去计算定位,对性能消耗高,因此也是不适合于事件委托的