关于事件冒泡和事件捕捉的用法,我们需要先了解什么叫做事件流:当该事件被触发时,事件的流动方向:
事件冒泡的定义:关于事件冒泡的原理很简单,就是从事件源触发,一直到document,从下到上,遇到相同事件触发执行,如果没有则跳过寻找下一层
例如:
<div id="div1" style="background: red">
<div id="div2" style="background: blue">
<div id="div3" style="background: green">
</div>
</div>
</div>
<script> var aDiv=document.querySelectorAll('div'); for(var i=0;i<aDiv.length;i++){ aDiv[i].onclick=function () { alert(this.style.background) } }</script>
正常情况下都是事件冒泡,只有在dom2级处理方式中才可以实现下沉(捕获)
<body> <div id="div1" style="background: red"> <div id="div2" style="background: blue"> <div id="div3" style="background: green"></div> </div> </div></body>
<script> var aDiv=document.querySelectorAll('div'); for(var i=0;i<aDiv.length;i++){ aDiv[i].addEventListener('click',function () { alert(this.style.background) },true) }</script>
如图所示在我们的事件下沉,只有在dom2级事件中才能实现,本来的代码应该是
oBtn.addEventListener('不加on的事件名',事件执行的函数,是否事件捕获);
在添加事件监听的后面有一个事件捕获,有true和false两个选项,默认为false,需要实现事件下沉的话,需要改为true
我罗列了一些常用的事件,大家可以参考一下:
【事件】
onclick 鼠标点击时
oncontextmenu 鼠标右键时
onmouseover 鼠标移上去时
onmouseout 鼠标出上去时
onmousemove 鼠标移动时
高频率触发事件,建议里面不要放复杂计算的代码
onblur 失去焦点时
onfocus 聚焦时
onload 加载时
onresize 改变窗口大小时
onscroll 鼠标滚动时
onsubmit 提交时
onkeydown
内容还没有进入输入框
onkeypress
内容还没有进入输入框
他的键码是ASCII
只能识别(按下去以后能在输入框出现东西的建)
onkeyup
内容已经进入输入框
oninput 高级事件 只兼容高级浏览器
onpropertychange IE事件 当属性发生改变的时候 ie9版本里面不能删除输入框中的内容,可以添加
一个定时器,来计算输入了多少个字符
兼容性操作:oInp.oninput=oInp.onpropertychange=function(){}
事件流中的事件对象:储存事件触发执行时候的相信的数据的对象
oDiv.onclick=function(ev){ //兼容到高级浏览器
event //兼容所有浏览器
var oEvent=ev||event;//如果后期event出现bug了需要用兼容性就可以这样写,目前event兼容所有浏览器所有我们现在都用event
}
例如:
<script> /*document.onclick=function (event) { console.log(event); }*/ document.onmousemove=function (event) { console.log(event.clientX,event.clientY); }</script>
event的常用属性:
event.clientX /event.clientY 可视区x,y轴坐标
event.pageX/ event.pageY 页面的x,y轴坐标
案例:移动鼠标,div跟随:
</head><body> <div></div></body></html><script> var oDiv = document.querySelector('div'); document.onmousemove = function(ev) { oDiv.style.top = ev.clientY - oDiv.offsetHeight / 2 + 'px'; //div的位置=鼠标位置的y轴加上物体高度的一半 oDiv.style.left = ev.clientX - oDiv.offsetWidth / 2 + 'px'; //div的位置=鼠标位置的x轴加上物体宽度的一半 }</script>
event.cancelBubble=true; 取消冒泡
<body>//三个div的中间阻止冒泡 <div id="div1" style="background: red"> <div id="div2" style="background: blue"> <div id="div3" style="background: green"></div> </div> </div></body></html><script> var aDiv=document.querySelectorAll('div'); aDiv[0].onclick=function () { alert(this.style.background) } aDiv[1].onclick=function(){ alert(this.style.background) //event.cancelBubble=true; event.stopPropagation() } aDiv[2].onclick=function () { alert(this.style.background) } document.onclick=function () { alert(1) }</script>
下来菜单案例,阻止冒泡
<body> <h4>---未选择---</h4> <ul> <li>html</li> <li>css</li> <li>js</li> </ul></body></html><script> var oH=document.querySelector('h4'); var oUl=document.querySelector('ul'); var aLi=document.querySelectorAll('li'); oH.onclick=function () { oUl.style.display='block'; event.cancelBubble=true;//阻止冒泡
} for(var i=0;i<aLi.length;i++){ aLi[i].onclick=function () { oH.innerHTML=this.innerHTML; oUl.style.display='none'; } } document.onclick=function () { oUl.style.display='none';//添加一个效果,收起下来下来菜单,但添加了这个效果整个下拉菜单
//就打不开了,所以需要在标题h4的点击事件下面阻止冒泡 }</script>
event.stopPropagation ie不兼容.cancelBubble,所以需要用这样取消冒泡,但11以下都不支持
延申一个小知识点:关于阻止浏览器默认事件的方法:(用法,例如阻止firim表单,ctrl+c等例子中
1:return false;
2,preventdefault
3,event.returnvalue=flase:兼容低版本ie浏览器
案例:模拟鼠标右键(因为鼠标右键选项是浏览器设置的默认样式,需要先阻止才能展现我们自己设置的样式:
<style> * { margin: 0; padding: 0; } ul{ border: 1px solid red; width: 130px; position: absolute; left: 0; top: 0; display: none; } li{ border-bottom: 1px dashed #999; line-height: 30px; background: #eee; text-align: center; list-style: none; } </style></head><body> <ul> <li>html</li> <li>css</li> <li>js</li> </ul></body></html><script> var oUl=document.querySelector('ul'); document.oncontextmenu=function () { oUl.style.display='block'; oUl.style.left=event.clientX+'px'; oUl.style.top=event.clientY+'px'; event.returnValue=false;//有以下三种阻止浏览器的的方式,随便哪一种都可以 //return false //event.preventDefault() }</script>
event.keyCode属性:键码:
onkeydown:事件在用户按下任何键盘键(包括系统按钮)时发生;
onkeyup:事件在用户松开任何键盘键(包括系统按钮)时发生;
onkeypress 事件在用户按下并放开任何字母数字键时发生;
区别:onkeydown 捕获的 keyCode 不区分字母大小,而 onkeypress 区分。
onkeyUp / onkeyDown : a=65/A=65 0 48 / 删除 8 / 回车 13;上下左右:37 38 39 40;
案例1:用键盘上下左右控制div移动:
<style> * { margin: 0; padding: 0; } div{ width: 100px; height: 100px; background: red; position: absolute; left: 0; top: 0; } </style></head><body> <div></div></body></html><script> var oDiv=document.querySelector('div'); document.onkeydown=function () //要是想按下按下按键一直移动必须用onkeydown switch (event.keyCode){ case 37: oDiv.style.left=oDiv.offsetLeft-2+'px'; break; case 38: oDiv.style.top=oDiv.offsetTop-2+'px'; break; case 39: oDiv.style.left=oDiv.offsetLeft+2+'px'; break; case 40: oDiv.style.top=oDiv.offsetTop+2+'px'; break; } }</script>
案例2:简易留言板:
<style> * { margin: 0; padding: 0; } </style></head><body> <input type="text"> <button>增加</button> <ul> <li> <!--<span>xxxxxxxxxxx </span> <a href="javascript:;">删除</a>--> </li> </ul></body></html><script> var oInp=document.querySelector('input'); var oBtn=document.querySelector('button'); var oUl=document.querySelector('ul');//获取元素 function add() {//申明一盒函数add var oLi=document.createElement('li');//在ul里面添加一个li oLi.innerHTML=` <span>${oInp.value}</span> <a href="javascript:;">删除</a> `;//设置li里面的内容等于input输入框中的value+带a标签的删除 oUl.appendChild(oLi);//appendchild,按顺序添加 oInp.value='';//将输入框中内容赋值给li,再清空输入框 var oA=oLi.getElementsByTagName('a')[0];//获取a标签 oA.onclick=function () {//添加一个点击事件 oUl.removeChild(this.parentNode)//删除ul中的a标签的父级li,实现删除功能 } } oBtn.onclick=add;//点击按钮的时候效果=add oInp.onkeyup=function () {//添加一个按键事件 if(event.keyCode==13){//13是回城键的键值,判断用户按下的按键是否是回车键, add();//如果用户点击回车,调用add函数 } }</script>
关于键值中的组合键:
案例:网页防被扒资源,阻止用户ctrl+c/或者直接让用户不能选中:
<body> <p>今天天气真好!!!!</p></body></html><script> //阻止选中 document.onmousedown=function(){//当鼠标落下的时候 event.preventDefault();//阻止浏览器默认事件 }; //阻止ctrl+c document.onkeydown=function () { console.log(event.keyCode); if(event.ctrlKey && event.keyCode==67){//判断用户是否点击的是ctrl和c一起 event.preventDefault();//阻止浏览器默认事件 } }</script>
如上述案例:要组织用户ctrl+c,不能直接写两个键值,onkeydown无法识别系统按钮
如果是组合键 需要用 ctrlKey shiftKey altKey && event.keyCode==键值数
下面该轮到我们本章最重要的内容了:
事件委托:
事件委托的定义:别名叫事件代理:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件
给需要选择的标签父级加触发事件,利用冒泡原理,寻找有同样触发事件的事件源子级,选中特定的子级,才能触发条件,
为什么要用事件委托:
比如我们有100个li,每个li都有相同的click点击事件,卡哇伊用for循环,来遍历所有的li,然后给它们添加事件,事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;
用法案例:(加入我们body里有很多个div,要给每个div家伙加上一个事件
<body> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div></body><script> var aDiv = document.querySelectorAll('div'); /*for(var i=0;i<aDiv.length;i++){ aDiv[i].onclick=function () { } }*/ document.body.onclick = function() { if (event.target.tagName == 'DIV') { // //事件,目标,标识名 alert(1); } } //或者: document.body.onclick = function() { if (event.srcElement.nodeName) {
//事件 , 元素源 ,节点名 alert(1) } }</script>
target.tagName(目标的标识名)主要作用于高级浏览器
srcelement.nodeName(元素源的节点名)兼容性更好
事件委托的好处:
1,给不存在的元素,未来的元素加事件
2,提升浏览器性能
结尾再奉上一个小例子:
再浏览器拖拽div:(这个案例主要分为三步,第一步是按下onmousedown,第二步是鼠标移动onmouse move,第三步是鼠标松开:onmouseup,结束事件
<style> * { margin: 0; padding: 0; } div{ width: 100px; height: 100px; background: red; position: absolute; left: 0; top: 0; } </style>
//js代码:
<script> var oDiv=document.querySelector('div'); //鼠标点在物体上面 oDiv.onmousedown=function () { var disX=event.clientX-oDiv.offsetLeft; var disY=event.clientY-oDiv.offsetTop; document.onmousemove=function () { var l=event.clientX-disX; var t=event.clientY-disY; oDiv.style.left=l+'px'; oDiv.style.top=t+'px'; }; document.onmouseup=function () { oDiv.onmousemove=null; } };</script><script> var oDiv=document.querySelector('div'); //鼠标点在物体上面 oDiv.onmousedown=function () {//获取一个鼠标点击的位置 var disX=event.clientX-oDiv.offsetLeft;//可视区x轴-div左边的距离 var disY=event.clientY-oDiv.offsetTop;//可视区y轴-div上边的距离 document.onmousemove=function () { var l=event.clientX-disX;//可视区x轴-disx的距离 var t=event.clientY-disY;//可视区y轴-disy的距离 oDiv.style.left=l+'px'; oDiv.style.top=t+'px'; }; document.onmouseup=function () { oDiv.onmousemove=null;//让onmouseup的事件=空就结束事件 } };</script>