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快很多,具体体验地址。

浙公网安备 33010602011771号