浏览器事件解析

196 阅读4分钟

JavaScript、浏览器、事件之间的关系

JavaScript程序采用了异步事件驱动编程(Event-driven programming)模型,事件驱动程序模型基本的实现原理基本上都是使用 事件循环(Event Loop),这部分内容涉及浏览器事件模型、回调原理。在javascript中使用了异步事件,也就是说:js中的事件都是异步执行的.

1、事件绑定的方法

  • 行内绑定   直接在DOM元素上通过设置on + eventType绑定事件处理程序。例如:

<a href="#none" onclick="alert('clicked')">点击我</a>

  • 在DOM元素上重写事件回调函数

使用DOM Element上面的on + eventType属性 API

var el = getElementById('button');  //button是一个<button>元素
el.onclick = function(){ alert('button clicked.') };
el.onclick = function(){ alert('Button Clicked.') };
//实际之弹出'Button Clicked.',函数发生了覆盖

  • 标准绑定方法

标准的绑定方法有两种,addEventListener和attachEvent前者是标准浏览器支持的API,后者是IE8以下浏览器支持的API:

<button class="left" id="left">left</button>
var ele = document.getElementById('left');
        if(ele.addEventListener){
            ele.addEventListener('click',function(){
                alert(this.id);
            },false);
        }
        if(ele.attachEvent){
            ele.attachEvent('onclick',function(){
                alert('1');
            })
        }

需要注意的是:
1. addEventLister的第一个参数事件类型是不加on前缀的,而attachEvent中需要加on前缀。
2. addEventLister中的事件回调函数中的this指向事件元素target本身,而attachEvent中的事件回调函数的this指向的是window。
3. addEventLister有第三个参数,true表示事件工作在捕获阶段,false为冒泡阶段(默认值:false)。而attachEvent只能工作在冒泡阶段。

在chrome中运行如下代码:

 <a href="javascript:alert('1')" onclick="alert('2')" id="link">link</a>
<script
var link = document.getElementById('link');
        link.onclick = function(){
            alert('3');
        };
        link.addEventListener('click',function(){
            alert('4');
        });
        link.addEventListener('click',function(){
            alert('5');
        });
</script>

点击后弹出顺序是: 3 -> 4 -> 5 -> 1

这里第4行代码覆盖了行内的onclick定义,如果注释了这一行,输入顺序为: 2 -> 4 -> 5 -> 1,而addEventListener之间不会发生覆盖。

2、解除事件绑定

对于上述的前二个方法,解除事件绑定只需要将对应的事件函数设为null,就可以了:

var el = document.getElementById('button');
el.onclick = null;

对于上述第三种方法使用removeListen()方法即可,在IE8中,对应使用detachEvent()。注意,他们和上面的注册方法一一对应,不能混用。

var ele = doucment.getElementById('button');
var handler = function(){alert('button')};

if(ele.addEventListener){
	ele.addEventListener('click',handler,false);
}
if(ele.attachEvent){
	ele.attachEvent('onclick',handler);
}
//移除事件
if(ele.removeEventListener){
	ele.removeEventListener('click',handler,false);
}
if(ele.detachEvent){
	ele.detachEvent('onclick',handler);
}

3、事件的冒泡与捕获

冒泡:在一个元素上触发的某一事件,会在这个元素的父辈元素上会依次由内向外触发该事件,直到window元素。

捕获:在一个元素上触发的某一事件,这个元素的每一层的所有子元素上触发该事件,并逐层向内,直到所有元素不再有子元素。

事件间回到函数参数是一个事件对象,它里面包括许多事件属性和方法,比如,我们可以用以下方式阻止冒泡和默认事件:

function handler(event){
	event = event || window.event;
	//阻止冒泡
	if(event.stopPropagation){
		event.stopPropagation(); //标准
	}else{
		event.cancelBubble = true;  //IE
	}

	//阻止默认事件
	if(event.preventDefault) {
		event.preventDefault();
	}else{
		event.returnValue = false;
	}
}

普通注册事件只能阻止默认事件,不能阻止冒泡

element = document.getElementById('submit');
element.onclick = function(e){
	return false; //通过返回false,阻止冒泡
}

4、事件代理

  • 事件委托注册方法,不论内容有多少都只注册1次,支持动态添加元素:

<ul id="list">
    <li>item-1</li>
    <li>item-2</li>
    <li>item-3</li>
    <li>item-4</li>
    <li>item-5</li>
</ul>
<script>
var list = document.getElementById('list');

var handler = function(event){
	event = event || window.event;
	target = event.target || event.srcElement;
	if(target.nodeName && target.nodeName === 'Li'){
		console.log(target.innerHTML);
	}
}

if(list.addEventListener){
	list.addEventListener('click',handler,false);
}else{
	list.attachEvent('onclick',handler);
}
//添加元素
var nodeList = document.createElement('li');
nodeList.innerHTML = 'item-6';
list.appendChild(nodeList);
</script>

5、事件封装

//冒泡事件模型
var myEventUtil = {
	//添加事件函数
	addEvent: function(ele, type, func){
		if(ele.addEventlistener){
			ele.addEventlistener(type,func,false);
		}else if(ele.attachevent){
			ele.attachevent('on' + type,func);
		}else{
			ele["on" + type] = func;
		}
	},
	//删除事件
	delEvent: function(ele, type, func){
		if(ele.removeEventListener){
			ele.removeEventListener(type, func, false);
		}else if(ele.detachEvent){
			ele.detachEvent('on' + type, func);
		}else{
			ele["on" + type] = null;
		}
	},
	//获取触发事件的源DOM元素
	getSrcElement: function(event){
		return event.target || event.srcElement;
	},
	//获取事件类型
	getType: function(event) {
		return event.type;
	},
	//获取事件
	getEvent: function(event) {
		return event || window.event;
	}
	//阻止事件冒泡
	stopPropagation: function(event){
		if(event.stopPropagation){
			event.stopPropagation();
		}else{
			event.cancelBubble = true;
		}
	}
	//禁用默认行为
	preventDefault: function(event) {
		if(event.preventDefault){
			event.preventDefault();
		}else{
			event.returnValue = false;
		}
	}
}

参考文章