DOM学习之路--Mr.Ember

DOM学习之路--Mr.Ember

摘要:打好基础从DOM基础开始

 

一. 事件冒泡和捕获

当一个元素事件被触发时,不仅只在对象本身触发一次,还经历了三个阶段:

(1)捕获阶段:文档先由根对象(document)向内捕获事件对象

(2)目标阶段:到达目标事件位置,触发事件

(3)冒泡阶段:再从目标事件位置往文档的根节点方向回溯,从内向外冒泡事件对象。

 

下面来看一个栗子🌰

 

<div class="box1">a
     <div class="box2">b
          <div class="box3">c

          </div>
     </div>
</div>

 

给这三个元素绑定上事件

a.addEventListener('click', function() {
    console.log(1)
}, true)
b.addEventListener('click', function() {
    console.log(2)
}, false)

c.addEventListener('click', function() {
    console.log(3)
}, false)

addEventListener第三个参数表达的含义是:是否在捕获阶段执行。默认false

所以点击c时,上述打印结果为: 1,3,2

但是如果是都在a上添加事件,事件就会按照js的顺序执行。

a.addEventListener('click', function() {
    console.log(1)
}, false)
a.addEventListener('click', function() {
    console.log(2)
}, true)

上述执行的结果是:1,2

所以可以总结出:事件是按照其他元素的捕获事件->本身元素的事件顺序->其他元素的冒泡事件。

 

 

二. jQuery选择器模拟实现

选择器有很多种,下面我简单的实现class选择器,id选择器,标签选择器。

<div id="div1">div1</div>

<div class="div2">222</div>
<div class="div2">333</div>
    
<p>p1</p>
<p>p2</p>

有上述html代码片段,打印下面的值

window.onload = function() {
    var ele1 = $('#div1');
    var ele2 = $('.div2');
    var ele3 = $('p');

    console.log(ele1)
    console.log(ele2)
    console.log(ele3)
}

首先创建一个空数组,用来放置选择器选中的值

var TQObject = function() {
    this.data = [];
}

在上述对象的原型上挂载$

TQObject.prototype = {}
var $ = function(selecter) {
    this.tqObject = new TQObject();
    if(selecter.substring(0,1) == '#') {   //id选择器
        var elem = document.getElementById(selecter.substring(1));
        this.tqObject.data.push(elem);
    }else if(selecter.substring(0,1) == '.') {  //class选择器
        var elems = document.getElementsByTagName('*');
        var reg = new RegExp("(^|\\s)" + selecter.substring(1) + "($|\\s)");
        for(var i =0; i<elems.length;i++) {
            if(reg.test(elems[i].className)) {
                this.tqObject.data.push(elems[i])
            }
        }
    } else {   //标签选择器
        var elems = document.getElementsByTagName(selecter);
        // console.log(elems)
        for(var i = 0; i<elems.length; i++) {
            this.tqObject.data.push(elems[i])
        }
    }
    return tqObject
}

ID选择器通过document.getElementById()进行选择。但是class可以通过document.getElementsByClassName()选择,也可以通过正则表达式选择,上述代码是通过正则表达式匹配实现的。

选择结果:

 

三. 实现一个事件绑定函数

首先实现一个简单的自定义事件绑定函数

function bindEvent(obj, events, fn) {
    obj.listeners = obj.listeners || {};
    bindCore(obj, events, fn)
}

bindCore函数

function bindCore(obj, events, fn) {
    obj.listeners[events] = obj.listeners[events] || [];
    obj.listeners[events].push(fn);
    
    if(obj.nodeType) {
        if(obj.addEventListener) {
            obj.addEventListener(events, fn, false);
        }else {
            obj.attachEvent('on'+ events, fn);
        }
    }
}

上述的绑定函数,下面还有解绑函数

function unbindEvent(obj, events, fn) {
    obj.listeners = obj.listeners || {};
    obj.listeners[events] = obj.listeners[events] || [];
    

    if(obj.nodeType) {
        if(obj.removeEventListener) {
            obj.removeEventListener(events, fn, false)
            console.log(obj.listeners)
        }else {
            obj.detachEvent('on' + events, fn)
        }
    }
}

给绑定函数解绑,最重要的一点是:绑定事件的函数和解绑事件的函数必须是一个函数。并且不可以是匿名函数。

下面实现一个可以为多个元素添加多个事件、多个元素添加一个事件、一个元素添加一个事件、一个元素添加多个事件

function bindEvent(objArr, eventsArr, fn) {
    if(objArr.length > 1) {  
        objArr.forEach(element => { //多个元素添加事件
            element.listeners = element.listeners || {};
            if(eventsArr.length > 1) {
                eventsArr.forEach( eventItem => {  //多个元素添加多个事件
                    bindCore(element, eventItem, fn)
                })
            }else {  //多个元素添加一个事件
                bindCore(element, eventsArr, fn)
            }
        });
    }else {  //一个元素添加一个事件
        objArr.listeners = objArr.listeners || {};
        if(eventsArr.length > 1) {
            eventsArr.forEach( eventItem => {  //一个元素添加多个事件
                bindCore(objArr, eventItem, fn)
            })
        }else {  //多个元素添加一个事件
            bindCore(objArr, eventsArr, fn)
        }
    }
}

 

四. queryselect和getELementByxxxx之间的区别

1. queryselect可以获取页面的静态集合,但是不能通过这个值改变这个值,但是getELementByxxxx可以通过这个获取的值来改变当前的页面上的值。

2. queryselector仅仅返回匹配指定选择器的第一个元素,queryselectall是获取所有的元素。

3. 性能方面来说,getELementByxxxx要比queryselect快很多,具体体验地址

 

posted @ 2019-01-21 21:16  Mr.Ember  阅读(197)  评论(0)    收藏  举报