Adamwu1992 / adamwu1992.github.io

My Blog
2 stars 0 forks source link

DOM事件 #3

Open Adamwu1992 opened 6 years ago

Adamwu1992 commented 6 years ago

0级DOM事件

通过Javascript制定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理属性。第四代web浏览器出现,至今为所有浏览器所支持。简单且具有跨浏览器的优势。

每一个DOM对象都有对应事件的一个属性,比如click事件对应的DOM属性就是onclick,通过为这个属性赋值可以绑定这个元素的点击处理事件,但是只能绑定一个事件。

<input value="Sign in" id="signin" onclick="alert("Welcome to here")"/>

或者

var dom = document.querySelector('#signin');
dom.addEventListener('click', function(e) {
    alert('Welcome to here');
})

DOM0事件绑定迅速,并且会在DOM2事件之前触发。

2级DOM事件

DOM2为事件制定了3个阶段

  1. 事件捕获: 在 capturing 阶段,事件从 Document 对象沿着文档树向下传播给节点。如果目标的任何一个祖先专门注册了事件监听函数,那么在事件传播的过程中就会运行这些函数。
  2. 处于目标阶段: 发生在目标节点自身,直接注册在目标上的适合的事件监听函数将运行。
  3. 事件冒泡: 这个阶段事件将从目标元素向上传播回 Document 对象(与 capturing 相反的阶段)。虽然所有事件都受 capturing 阶段的支配,但并不是所有类型的事件都 bubbling。(0级DOM事件模型处理没有capturing 阶段)。

执行顺序

通常来讲,首先执行捕获阶段的事件,再执行处于目标阶段的事件,最后执行冒泡阶段的事件。这个与绑定的时机无关,取决于事件流的流动。 但是,有一种情况就是如果处于目标阶段(event.target === event.currentTarget),无论事件绑定在捕获阶段还是冒泡阶段都会被无视掉,而是按照绑定的先后顺序执行。

当前对象

event.target表示触发该事件的节点,event.currentTarget表示正在处理该事件的节点,可能是target,也可能是target的父节点在捕获或者冒泡阶段绑定触发了事件。 事件回调函数里的this不一定指向当前节点对象,用currentTarget可以准确获取到当前节点对象。

Adamwu1992 commented 6 years ago

事件代理

利用事件的冒泡机制,将事件处理方法绑定在父节点上,当事件冒泡到父节点时,根据target属性判断点击是否命中目标,如果命中就执行处理方法,否则判断target的父节点是否命中,直到遍历到父节点或者跟节点。

        const isSelectedCurry = selector => {
            const _selector = selector.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
            if (_selector.match(/^\./)) {
                return target => target.className.indexOf(_selector.substring(1)) > -1;
            } else if (_selector.match(/^#/)) {
                return target => target.id === _selector.substring(1);
            } else {
                return target => target.tagName === _selector.toUpperCase();
            }

        }

        Element.prototype.myDelegate = function(type, targetSelector, callabck) {
            const bindingEl = this;
            const isSelected = isSelectedCurry(targetSelector);
            bindingEl.addEventListener(type, event => { 
                let t = event.target;
                while(!isSelected(t)) {
                    t = t.parentNode;
                    if (t === bindingEl || !t) {
                        t = null;
                        break;
                    }
                }
                if (t) {
                    callabck.call(t, event);
                }
            })
        }