五、事件处理模型 — 事件冒泡、捕获
事件处理的两个模型:事件冒泡、捕获(不能同时存在)
只点了黄色区域,但是出现了 box,content,wrapper,往下漏了,就是事件冒泡
1、事件冒泡:
结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,即同一事件,自子元素冒泡向父元素。(自底向上)
结构上存在父子关系的元素,如果点击到子元素,会一级级向父元素传递这个事件(从代码的角度是自底向上一层层冒泡的)
例:加了 margin,只点黄色的,还是出现了 box,content,wrapper。所以与视觉无关
2、事件捕获:(只有谷歌有,最新火狐有)
1)结构上(非视觉上)嵌套关系的元素,会存在事件捕获的功能,即同一事件,自父元素捕获至子元素(事件源元素)(自底向上)
2)IE 没有捕获事件
一个对象的一个事件类型,只能存在一个事件处理模型(冒泡或捕获)
obj.addEventListener(type, fn, true);第三个参数为 true 就是事件捕获
点击黄的:先红的捕获事件并且执行,再绿的捕获事件并且执行,最后只执行事件黄的。捕获是把结构的最外面先抓住。最外面先捕获,再一层层向里面捕获,最里面的是按常规执行。
把 false 改成 true,就变成了事件捕获
html 部分沿用下侧冒泡情况的代码,只把 false 改成 true 就变成事件捕获了
思考:同一个对象的同一个事件类型,上面绑定了两个事件处理函数,一个符合冒泡,一个符合捕获,点击一个元素后,是先捕获,还是先冒泡?
3、触发顺序,先捕获,后冒泡
同一个对象的一个事件处理类型,上面绑定了两个事件处理,分别执行事件冒泡和事件执行
html 部分沿用上面冒泡情况的代码
改变了冒泡和捕获的顺序
这个的顺序是先捕获红色,再捕获绿色,boxBubble 黄色区域事件执行,box 黄色区域事件执行,冒泡到绿色,冒泡到红色(谁先绑定,谁先执行,boxBubble 先绑定,所以先执行)
4、focus,blur,change,submit,reset,select 等事件不冒泡
六、取消冒泡和阻止默认事件
不给 div 绑定事件处理函数,依然会冒泡(document 冒泡到 div 上)
给 div 也绑定个事件(点红色,也冒泡到 document)
在每一个事件处理函数中【div.οnclick=function(){}】,我们可以写一个形参(如 e),系统可以传递事件对象(记载了数据发生时的状态和信息)到这个参数里面去
1、取消冒泡:
1)W3C 标准 event.stopPropagation();但不支持 ie9 以下版本
事件对象上有一个 event.stopPropagation();取消冒泡事件
2)IE 独有 event.cancelBubble = true;【实际上谷歌也实现了】
ie 里面事件对象上有一个 event.cancelBubble = true;能取消冒泡事件
3)封装取消冒泡的函数 stopBubble(event)
2、阻止默认事件:
1)默认事件 — 表单提交,a 标签跳转,右键菜单等
例浏览器点右键出菜单,是一个事件(默认事件)
2)return false; 兼容性非常好,以对象属性的方式注册的事件才生效(这是句柄的方式阻止默认事件,只有句柄的方式绑定事件才好使)
ele.onxxx = function (event) {}是句柄的绑定方式,才能用 return false;
3)event.preventDefault(); W3C 标注,IE9 以下不兼容
4)event.returnValue = false; 兼容 IE
5)封装阻止默认事件的函数 cancelHandler(event);
a 标签有一个跳转的默认时间,如何取消看下面
下面也能取消 a 标签的默认值
<a href = “avascript:void( )”>
七、事件对象
非 ie 浏览器会把事件对象(记载了数据发生时的状态和信息)打包传到参数里面去。
ie 浏览器在 window.event 里面储存事件对象。
1、event || window.event 用于 IE
window.event 用于 IE,event 只能用于非 ie 浏览器
这是储存事件对象的兼容性写法
点红色会执行,点绿色会冒泡执行。点红色是点击到他自己来执行;点绿色身上,触发事件的点在绿色身上,是绿色传递的,我们把触发事件的地方叫事件源。
事件对象上有个专门的信息是存储事件源的。
点击之后查看控制台 srcElement:这就是储存事件源的地方
2、事件源对象:(找事件源对象的方法)
event.target 火狐独有的
event.srcElement Ie 独有的
这俩 chrome 都有
事件源对象的兼容性写法
八、事件委托
例 我们给每个 li 绑定事件,要求点哪个 li 就输出哪个内容,这不涉及闭包问题
上面的写法不好(如果是三千亿个 li 就没效率),不能动态,要用事件源和事件冒泡
事件委托:利用事件冒泡,和事件源对象进行处理
优点:
- 性能 不需要循环所有的元素一个个绑定事件
- 灵活 当有新的子元素时不需要重新绑定事件
例给每个 li 绑定事件,再增加 li 也能使用
习题:预习下面的三个词,写拖拽功能(鼠标按住方块跟着动,松开就不跟着走)
上面这个写法有问题,鼠标在方块的左上角,不在你点击他时候的位置
思路:上面是把点击鼠标的点设置成方块的 left 和 top(就是把鼠标的点设置成了左顶点,这样就少了个距离),我们把这个距离算上就可以了,加上鼠标第一次点击时候的离方块上边和左边的距离再赋给左上角了
之前是把 div.onmousemove 写在 div 上面,有一个 bug,我们按住鼠标迅速离开方块,方块就不会跟着动,一直按住鼠标,再一进去,又可以带着方块动了,是一个事件监听的问题(鼠标挪动频次大于事件监听频次),我们写成document.onmousemove就可以了
上面这个写的很粗糙,没有封装也没有绑定。document.οnmοusemοve=function(){}这种写法是不可以的,要用 addEventListener 绑定。
习题:就是把上面绑定成函数,函数名叫 function drag (elem) {}
面试问题,什么是事件捕获,一个是冒泡,一个是捕获 obj.addEventListener(type, fn, true);他所说的第二种捕获不是事件处理模型,而是一种真实的事件获取的过程,用于解决拖拽鼠标出方块的问题
仅在 ie 好使,利用 div.setCapture();会捕获页面上发生的所有事情,都获取到自己身上。对应的用 div.releaseCapture();释放。但是方法比较老旧,一般不用。
九、事件分类
1、鼠标事件(不需要小驼峰和大驼峰)
click、mousedown、mousemove、mouseup、contextmenu、mouseover、mouseout、
mouseenter、 mouseleave
例: click=mousedown+mousemove
这三个事件的触发顺序是 mousedown,mouseup,click
例 contextmenu 右键取消菜单,mousemove 是鼠标移动的事件
例 相对应 mouseover、mouseout 鼠标覆盖区域与mouseenter 、mouseleave 鼠标离开
但是 mouseenter 、mouseleave 是 html5 的,都是鼠标进去,出来发生的变化
2、用 button 来区分鼠标的按键,0/1/2
只有 mouseup、mousedown 两个能区分鼠标垫左右键
button 返回值,右键是 0,左键是 2,中间是 1
3、DOM3 标准规定:click 事件只能监听左键,只能通过 mousedown 和 mouseup 来判断鼠标键
例: click 不能监听右中
4、如何解决 mousedown 和 click 的冲突
十、事件练习作业
1、拖拽应用:
实现拖拽正常拖,但是点击就正常跳转
思路:拖拽由 down+move+up,但是不管隔多久都算一个 click,可以理解成拖拽不等于点击 click。
看时间差解决:按下+抬起的时间差>多少,就能知道是拖拽了。
答案:按下之后才绑定 move 事件,抬起了 move 事件就解除
2、应用 mousedown mousemove mouseup
3、随机移动的方块(完全随机)
思路:当把鼠标挪到方块里面去 onmouseover 的时候,方块随机向八个方向挪动
4、mouseover
移动端 onmousedown 不能用,只能用touchstart,touchmove,touchend