事件基础
事件是由访问 Web 页面的用户引起的一系列操作。当用户执行某些操作的时候,再去执行一系列代码,或者用来获取事件的详细信息。
事件处理函数
JavaScript可以处理的事件类型为:鼠标事件、键盘事件、HTML事件
所有的事件处理函数都会都有两个部分组成,on + 事件名称
JavaScript中常用的事件处理函数
事件名称 | 兼容性 | 作用 |
---|---|---|
onclick | IE3、N2 | 鼠标单击时触发此事件 |
ondblclick | IE4、N4 | 鼠标双击时触发此事件 |
onmousedown | IE4、N4 | 按下鼠标时触发此事件 |
onmouseover | IE3、N2 | 当鼠标移动到某对象范围的上方时触发此事件 |
onmousemove | IE4、N4 | 鼠标移动时触发此事件 |
onload | IE3、N2 | 页面加载完成时触发此事件 |
onresize | IE4、N4 | 当浏览器的窗口大小被改变时触发此事件 |
onscroll | IE4、N | 浏览器的滚动条位置发生变化时触发此事件 |
onblur | IE3、N2 | 当表单失去焦点时触发此事件 |
onchange | IE3、N2 | 当表单内容发生改变并失去焦点时触发此事件 |
onfocus | IE3、N2 | 当表单获取焦点时触发此事件 |
事件对象
- 什么时候会产生事件对象呢?
当触发某个事件时,会产生一个事件对象,这个对象包含着所有与事件有关的信息 。包括导致事件的元素、事件的类型、以及其它与特定事件相关的信息。
例如: 当用户单击某个元素的时候,我们给这个元素注册的事件就会触发,该事件的本质就是一个函数,而该函数的形参接收一个event对象。
var oBox = document.getElementById('box');
oBox.onclick = function(e){
var evt = e || event;
console.log(evt);
}
可以看出,当我们单击的时候控制台会打印出这个对象包含着所有与事件有关的信息(只截取部分,具体可以复制代码进行测试)
- 事件通常与函数结合使用,函数不会在事件发生前被执行!
事件冒泡
- 事件冒泡的概念
事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。
事件在目标事件上触发后,并不在这个元素上终止。它会随着DOM树一层层向上冒泡,直到到达最外层的根节点,一直向上传播,直到document对象。也就是说,同一事件会一次在目标节点的父节点,父节点的父节点…直到最外层的节点上触发。这一过程称为事件冒泡。绝大多数事件是会冒泡的,但并非所有的。
var oInner = document.getElementById('inner');
var oOuter = document.getElementById('outer');
oInner.onclick = function(){
console.log('inner')
}
oOuter.onclick = function(){
console.log('outer')
}
document.onclick = function(){
console.log('document')
}
当给inner,outer,document添加同样的事件时,点击inner同时会触发外层的事件(即使document没有添加onclick事件也会触发此事件,只是没有返回值)
- 阻止事件冒泡
非IE低版本浏览器中 ,在当前事件内添加:e.stopPropagation();
IE低版本中需要在当前时间内添加:e.cancelBubble = true;
兼容性写法:
if(e.cancelBubble = true){
e.cancelBubble = true;
}else{
e.stopPropagation();
}
这里以非IE浏览器举例
var oInner = document.getElementById('inner');
var oOuter = document.getElementById('outer');
oInner.onclick = function(){
console.log('inner')
}
oOuter.onclick = function(e){
console.log('outer')
e.stopPropagation();
}
document.onclick = function(){
console.log('document')
}
当我们为 outer 的事件添加了e.stopPropagation();
之后点击 inner 的时候控制台只输出了inner和outer;可见,事件在传播到outer的时候被阻止了
浏览器默认行为
- 什么是浏览器默认行为
JavaScript事件本身所具有的属性,例如a标签的跳转,Submit按钮的提交,右键菜单,文本框的输入等。- 阻止浏览器默认行为
阻止默认行为的方式:
w3c的方法是e.preventDefault(),
IE则是使用e.returnValue = false;
return false;
为什么要阻止?
例如:在某些情况下我们不希望用户右击则可以通过阻止浏览器默认行为来控制。
document.oncontextmenu = function stopDefault(e){
//防止浏览器默认行为(W3C)
if(e && e.preventDefault){
e.preventDefault();
}
//IE中组织浏览器行为
else{
window.event.returnValue=fale;
return false;
}
}
此时在浏览器内右击则不会出现选项卡
事件委托
- 为什么要用事件委托
一般来说,Dom需要有事件处理程序,我们都会直接给它设事件处理程序就好了,那如果是很多的Dom需要添加事件处理呢?比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件,那这么做会存在什么影响呢?
在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与Dom节点进行交互,访问Dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到Js程序里面,与Dom的操作就只需要交互一次,这样就能大大的减少与Dom的交互次数,提高性能;而且当我们为某元素添加事件之后如果动态添加新的同样的元素是不会具有该事件的(例如当我们为li添加点击事件之后再页面上点击按钮再次添加li,此时添加的li是不具有事件的)。
如果用事件委托,那么我们就可以只对它的父级(如果只有一个父级)这一个对象进行操作,这样我们就需要一个内存空间就够了,自然性能就会更好,并且能解决动态添加的元素不具有事件的问题。- 事件委托的原理
事件委托是利用事件的冒泡原理来实现的,何为事件冒泡呢?就是事件从最深的节点开始,然后逐步向上传播事件,举个例子:页面上有这么一个节点树,div>ul>li>a;比如给最里面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行顺序a>li>ul>div,有这样一个机制,那么我们给最外面的div加点击事件,那么里面的ul,li,a做点击事件的时候,都会冒泡到最外层的div上,所以都会触发,这就是事件委托,委托它们父级代为执行事件。- 事件委托的实现
这里用父级ul做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发,当然,这里当点击ul的时候,也是会触发的,如果我想让事件代理的效果跟直接给节点的事件效果一样怎么办,比如说只有点击li才会触发?
Event对象提供了一个属性叫target,可以返回事件的目标节点,我们称为事件源,也就是说,target就可以表示为当前的事件操作的Dom,但是不是真正操作Dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,这里我们用nodeName来获取具体是什么标签名,这个返回的是一个大写的,我们需要转成小写再做比较或者直接写入一个大写的值。
var oUl = document.getElementById("ul1");
oUl.onclick = function(ev){
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if(target.nodeName.toLowerCase() == 'li'){//toUpperCase()转为大写
alert(123);
alert(target.innerHTML);
}
}
这样改下就只有点击li会触发事件了,且每次只执行一次dom操作,如果li数量很多的话,将大大减少Dom的操作,优化性能!