javaScript事件知识点

 提纲

  • event事件
  • event事件中常用属性
  • 元素注册事件
  • 事件冒泡
  • 事件委托
  • 事件监听和解绑的兼容封装
     
 
 
事件event对象
 
也就是说有事件的情况下,才存在事件对象;事件对象中存着该事件的相关信息;
 
兼容处理
document.onclick = function(event){
         event = event ||  window.event;
}
 
主流浏览器支持事件函数的参数event,而ie不支持参数的形式通过window.event获取。
 
 
event事件中常用属性
 
clientY     支持IE           获取的是鼠标到浏览器可视窗口顶部的距离
 
clientX    支持IE           获取鼠标到浏览器可视窗口左边的距离
 
pageY    不支持IE8及以下     获取鼠标到文档顶部的距离
 
pageX    不支持IE8及以下     获取鼠标到文档左边的距离
 
对于主流浏览器和IE9及以上都支持
 
 
如果需要使用pageX和pageY就需要解决兼容问题,以便符合需求。
 
 
 
clientX/clientY和 pageX/pageY 兼容处理代码
 
function getXY(ev){
var ev = ev || window.event;
var x = 0,y = 0;
if(ev.pageX){
x = ev.pageX;
y = ev.pageY;
}else{
// 滚动条的距离
var cleft = document.body.scrollLeft || document.documentElement.scrollLeft;
var ctop = document.body.scrollTop || document.documentElement.scrollTop;
x = ev.clientX + cleft;
y = ev.clientY + ctop;
}
return {x : x,y : y};
}
 
 
 
 

 
元素注册事件
 
注册事件方式有两种,直接注册和监听注册事件两种方式。
 
 
以点击事件为例:
第一种直接注册
document.onclick = function(){
    alert( 1 );
}
 
兼容性:兼容所有浏览器
 
 
直接注册有两种写法,一种是直接使用匿名函数,一种是定义一个有名函数,进行赋值。
 
var oEle = docuemnt.getElementById(' btn ');
 
oEle.onclick = function(){ }
 
或是
 
oEle.onclick = btnFn;
 
function  btnFn(){  } 
 
 使用名定义的函数。
 
 
 
注意注册事件的函数,不能定义成函数表达式,如下:
 
oEle.onclick = btnFn;
var btnFn  =  function(){  };   
 
 
弊端:直接注册事件,同一个元素添加多个事件容易被覆盖。通常的开发中是使用第二种形式。
 
 
解除元素注册绑定的事件
 
ele.eventName = null;
 
例如解除元素oEle的click事件
 
oEle.onclick = null; 
 
 
 
第二种监听事件也是事件监听
 
el.addEventListener('eventName', eventFn , boolean)只支持主流浏览器, 不支持IE
el.attachEvent('eventName', eventFn , boolean) 不支持主流浏览器,支持IE
 
eventName第一个参数事件名;
 
eventFn第二个参数处理方法,也可以是匿名函数
 
boolean值实质决定了该事件执行的顺序,所以与每个事件的事件对象没有必然的联系。
 
boolean值是true,表示以事件捕获的顺序执行事件,也就是父元素向子元素执行,最后执行当前点击时元素所绑定的事件;
 
boolean值是false,表示以事件冒泡的顺序执行事件,也就是先执行当前点击时元素所绑定的事件 
 
// 主流浏览器
document.addEventListener( 'click',function(){
       alert( 1 );
},false);
 
// IE浏览器
document.attachEvent( 'onclick',function(){
    alert( 1 );
},false);
 
//兼容主流浏览器和IE浏览器普通写法
document.addEventListener( 'click',function(){
       alert( 1 );
},false);
 
document.attachEvent&&document.attachEvent( 'onclick',function(){
    alert( 1 );
},false);
 
其实就是写两套代码,当然这种做法是非常愚蠢的。 前辈们已为我们想出了办,这也是设计模式中的适配器设计模式。
 
 
 
元素绑定事件统一进行兼容
 
//事件绑定
function addEvent(el, type, callback) { //这样就没有办法传递参数了
var fn = function (e) {
var e = e || window.event;
callback.call(el, e); //让回调中的this和此时的this都指向el, args是一个参数数组
};
 
if( window.addEventListener ){
el.addEventListener(type , fn);
}else {
el.attachEvent('on'+ type , fn);
}
 
    return fn; //返回fn是为了解绑的时候统一
}
 
 
addEventListener()和attchEvent()事件解绑分别是以下两个事件
 
// 事件解绑
function removeEvent( obj , eName , fn) {
if ( document.addEventListener ){
    obj.removeEventListener(eName, fn);
}else{
    obj.detachEvent('on'+eName , fn);
}
 
}
 
注意,在事件绑定和解绑的时候,fn指的的是同一个方法,所以在绑定的时候需要返回
 
 
这些知识点很简单,大家应该都知道,我也是回过头把初学时不完整的进行补充,更重要的是对其中关键点有了一步深刻的认识和理解。
 

 
事件冒泡
 
冒泡的概念,当子元素绑定事件的时候,事件会向上传递,直到根元素。
 
为页面中id=box的div添加一个onclick事件,event对象里path属性,可以看到事件每一级的冒泡,如下
 
 
阻止事件冒泡
 
在实际应用中,我们是不希望事件向上传递,因此也有了阻止事件冒泡
 
return false;     //即阻止了事件的默认行为,有阻止了事件冒泡; 只是在使用事件监听注册事件时是不管用的
 
 
event.cancelBubble = true;  //阻止事件冒泡,只是不符合W3C规范
 
 
event.stopPropagation();  //阻止事件冒泡,符合W3C规范
 
 
event.preventDefault();  //阻止链接行为,没有阻止冒泡
 
 
在为元素注册事件时,一般都要添加阻止事件冒泡,这样做似乎更合理一些。
 
 

 
事件委托
 
事实子元素要注册事件,确添加到其父元素上,称之为事件委托。
 
例如:
 
我现在有一组li列表,我想为每一个 li 添加一个点击事件
 
实例实现点击li列表,输出li的索引值
 
常见的实现方法
var list = document.querySelectorAll('.ulBox li');
for(var i=0 ,len=list.length; i < len; i++ ){
list[i].index = i; //自定义属性,存储每个li的索引值
list[i].onclick = function () {
console.log(this.index); // 输出当前点击的索引值
}
}
 
 
利用事件委托来实现
 
oUl.onclick = function (ev) {
var ev = window.event || ev;
var tag = ev.target || ev.srcElement; //取到当前点击的元素
var ali = tag.parentNode.children; //这是一个文档对象,需要转化为数组
var arrALi =[].slice.call(ali); //把类数组对象转化为数组对象
if(tag.nodeName.toLocaleLowerCase() === 'li'){
console.log( arrALi.indexOf(tag) )
}
}
var tag = ev.target || ev.srcElement;  
这行代码ev.target是不兼容IE8的,所以有了ev.srcElement这个来处理IE8兼容问题滴。
 
 
 
写每一行代码,都是有目的的;所写的每一行代码都是有原因的;这似乎就像存在的就是合理的,合理的就会存在一样哒~~
 

 
 
事件监听和解绑的兼容封装
 
 注册事件的代码
 
function addEvent(obj, eName, callback) {
var fn = function (e) {
e = window.event || e;
e.stopPropagation();
callback.call(obj,e);
};
/*
obj.myEvent存放放注册事件的形式
* obj.myEvent = {
* ['click':fn1, 'click':fn2, 'mouse':fn1],
* ['mouse',fn1]}
*
* */
//obj是否存在自定义属性,当不存在时为每一个Dom对象添加一个自定义属性,存放该对象注册事件的执行函数
if (!obj.myEvent)obj.myEvent = {};
 
//obj的自定义属性中是否存在eName这个事件,存在继续向下执行,不存在则添加
if(!obj.myEvent[eName])obj.myEvent[eName] = [];
 
obj.myEvent[eName].push(fn);//把每次注册事件的执行函数,pushobj的自定义属性中
window.addEventListener?obj.addEventListener(eName, fn):obj.attachEvent('on'+eName, fn);
return fn; //返回这个函数,目的是为了解绑
}
 
 
解绑事件的代码
 
function removeEvent(obj, eName, fnName) {
if(obj.myEvent && obj.myEvent[eName]){ //obj元素是否有事件并注册了事件
var eNameArr = obj.myEvent[eName]; //obj当前要解除的所有事件函数存放在一个数组里
var len=eNameArr.length; //一个元素注册相同事件的个数
if( fnName ){ //解除元素指定的事件函数
var idx = -1;
for(var i=0; i<len; i++){
if(eNameArr[i] === fnName){
idx = i;
break; //跳出循环
}
}
 
if(idx != -1){
obj.myEvent[eName].splice(idx,1); //删除数组中指定位置的数组
window.addEventListener?obj.removeEventListener(eName, fnName):obj.detachEvent('on'+eName, fnName);
}
 
}else { //解除元素所有的指定事件函数
for(i=0; i<len; i++){ //循环移除元素的所有eName事件
window.addEventListener?obj.removeEventListener(eName, eNameArr[i]):obj.detachEvent('on'+eName, eNameArr[i]);
}
obj.myEvent[eName] = []; //清空元素自定义属性中的eName说有事件
}
}
}
原文地址:https://www.cnblogs.com/wjh0916/p/9413807.html