事件(Event)是JavaScript应用跳动的心脏,当我们与浏览器中Web页面进行某些类型的交互时,事件就发生了。
事件可能是用户在某些内容上的点击、鼠标经过某个特定元素或按下键盘上的某些按键,事件还可能是Web浏览器中发生的事情,比如说某个Web页面加载完成,或者是用户滚动窗口或改变窗口大小。说白了,事件是文档或浏览器中发生的特定交互瞬间!
通过使用JavaScript,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应。
事件让我们可以与浏览器关联起来。
1.如何绑定事件处理函数。
方法1:ele.onxxx=function()={}
<div style="width:100px;height:100px;background-color:red;display:inline-block" > </div> <script type="text/javascript"> var div=document.getElementsByTagName('div')[0]; div.onclick=function() { console.log('cyl'); } </script>
我们给div绑定了一个程序,当div被点击的时候打印cyl
没有任何问题。
这种方法的兼容性的非常好,浏览器基本都支持。缺点也很明显:这种方式就像属性赋值一样,很明显不能同时触发俩个程序的,后面一个函数会覆盖前面一个。
这种方式等同于写在html行间样式。
<div style="width:100px;height:100px;background-color:red;display:inline-block" onclick="console.log('cyl')" > </div>
这个和上面那一个等同的。
程序this指向dom元素对象
2.obj.addEventListener(tyle,fn,false);//false后面会讲的。
这种处理方法就比较正式一些。
dom元素.addEventListener(事件类型,处理函数,false);
<div style="width:100px;height:100px;background-color:red;display:inline-block" > </div> <script type="text/javascript"> var div=document.getElementsByTagName('div')[0]; div.addEventListener('click',function() { console.log('cyl'); },false) </script>
同样可以实现相同的功能。
<div style="width:100px;height:100px;background-color:red;display:inline-block" > </div> <script type="text/javascript"> var div=document.getElementsByTagName('div')[0]; div.addEventListener('click',function() { console.log('wust'); },false); div.addEventListener('click',function() { console.log('cyl'); },false); </script>
我们给div设置俩个程序,按照循序都执行了。
第二个参数也可以放上函数的引用。
缺点:IE9下不兼容,同一个函数被绑定多次,只会执行一次。优点:可以同时绑定多个程序,很正式。
程序this指向dom元素对象
方法三:obj.attachEvent('on'+type,fn) IE浏览器独有。
obj.attachEvent('on'+事件类型,处理函数)
<div style="width:100px;height:100px;background-color:red;display:inline-block" > </div> <script type="text/javascript"> var div=document.getElementsByTagName('div')[0]; div.attachEvent('onclick',function() { console.log('wust'); }); div.attachEvent('onclick',function() { console.log('cyl'); }); </script>
缺点:谷歌没有。优点:可以实现一个事件绑定多个处理程序,也可以实现,同一个函数被绑定多次,执行多次。
程序this指向window
这里就有一个问题,我们想知道调用者怎么办,而this指向的window很麻烦,call的使用就凸显出来了。
var div=document.getElementsByTagName('div')[0]; div.attachEvent('onclick',function() { deel.call(div); }); function deel(){ }
我们的attachEvent里面的处理函数只负责调用deel而我们调用deel时就把它的this改了。
封装一个给一个dom对象添加该事件类型的处理函数。
function addEvent(elem,type,handle) { if (elem.addEventListener) { elem.addEventListener(type,handle,false); } else if (elem.attachEvent) { elem.attachEvent('on'+type,function(){ handle.call(elem); }); } else { elem['on'+type]=handle; } }
事件的解除
对应的三种方式的接触操作:
elem.'on'+xxx=false||null;
obj.removeEventListener(type,fn,false);
obj.detachEvent('on'+type,fn);
注意如果你写的是匿名函数,那么将无法解除。
俩种事件处理模型:冒泡和捕获
事件冒泡:结构上(非视觉上)嵌套关系的元素,会存在事件冒泡功能,即同一事件,存在子元素向父元素的传递。(自底向上)
<div class='demo' > <div class='wrapp'> <div class='cyl'></div> </div> </div> <script type="text/javascript"> var demo=document.getElementsByClassName('demo')[0]; var wrapp=document.getElementsByClassName('wrapp')[0]; var cyl=document.getElementsByClassName('cyl')[0]; demo.onclick=function() { console.log('demo'); } wrapp.onclick=function() { console.log('wrapp'); } cyl.onclick=function() { console.log('cyl'); } </script>
.demo{ width:100px; height:100px; background-color:red; } .wrapp{ width:50px; height:50px; background-color:green; } .cyl{ width:20px; height:20px; background-color:black; }
效果是这样的:
cyl是黑色的,结构上最底一层.我们点击cyl
我们发现三个程序都执行了。我们点击绿色的俩个
红色的仅仅输出demo.现在应该知道冒泡是什么意思了吧!
顺序是:事件源的执行,然后在冒泡到父元素。
事件捕获:结构上(非视觉上)嵌套关系的元素,会存在事件冒泡功能,即同一事件,即父元素会向子元素传递。(自顶向下)
<div class='demo' > <div class='wrapp'> <div class='cyl'></div> </div> </div> <script type="text/javascript"> var demo=document.getElementsByClassName('demo')[0]; var wrapp=document.getElementsByClassName('wrapp')[0]; var cyl=document.getElementsByClassName('cyl')[0]; demo.addEventListener('click',function() { console.log('demo'); },true); wrapp.addEventListener('click',function() { console.log('warpp'); },true); cyl.addEventListener('click',function() { console.log('cyl'); },true); </script>
我们就需要利用addEventListener的第三个参数,把它改成true那么就会触发捕获了。
我们点击黑色的,冒泡是cyl wrapp demo 而捕获相反是
从父元素一步步带事件源节点。父元素捕获,一直到事件源的执行
同一个对象的事件类型的一个处理函数只能满足一个事件处理模型不是冒泡就是捕获。
注意:
我们刚刚说了同一个对象的事件类型的处理函数只能满足一个事件处理模型,但是如果有多个处理函数了?那么冒泡和捕获的顺序了?
看如下代码:
<div class='demo' > <div class='wrapp'> <div class='cyl'></div> </div> </div> <script type="text/javascript"> var demo=document.getElementsByClassName('demo')[0]; var wrapp=document.getElementsByClassName('wrapp')[0]; var cyl=document.getElementsByClassName('cyl')[0]; demo.addEventListener('click',function() { console.log('demobb'); },false); wrapp.addEventListener('click',function() { console.log('warppbb'); },false); cyl.addEventListener('click',function() { console.log('cylbb'); },false); demo.addEventListener('click',function() { console.log('demo'); },true); wrapp.addEventListener('click',function() { console.log('warpp'); },true); cyl.addEventListener('click',function() { console.log('cyl'); },true); </script>
看如下代码,我们给每一个事件都加上了俩个处理函数,那么点击cyl会发什么了?
答案是这样的。意料之中。我们这样可以得到一些结论:捕获优先级>事件源的执行>冒泡优先级
而事件源的执行满足先绑定,先执行。
focus blur change submit selet 等不冒泡。
处理函数上面的形参
demo.addEventListener('click',function(e) { console.log(e); },false);
我们打印e,发现这里有很多有用的信息,
在ie下面却不在这里,而在window.event上面,so:
var event=e||window.event;
取消冒泡事件
1:even.stopPropagation() 不支持iE9以下的浏览器
2:ie独有 even.cancelBubble=true;
cyl.addEventListener('click',function(e) { e.stopPropagation(); console.log('cylbb'); },false);
这里添加了取消冒泡后,那么demo,wrapp就不打印了;
事件源对象
在event上面有俩个属性可以记录是谁触发的这个事件
event.target 火狐只有这个
event.srcElement ie只有这个
谷歌都有
一个题目,一个ul下面有三千个li,每个li里面是一个数(从1到3000,顺序递增),我点击哪一个打印对应数字
怎么搞?
这个时候我们就要利用冒泡和源对象。
ul.onclick=function(e) { var event=e||window.event; var target=event.target||event.srcElement; console.log(targe.innerText); }
搞定了。
点击li的事件会冒泡到ul,那么根据事件源对象,我们可以轻松的找到点击的li
这个称为事件委托,性能上不需要挨个绑定所有的li,灵活度,当增加新的li时,不需要重新绑定。