事件流
DOM同时支持两种事件模型:捕获型事件和冒泡型事件
并且每当某一事件发生时,都会经过捕获阶段->处理阶段->冒泡阶段(有些浏览器不支持捕获)
捕获阶段是由上层元素到下层元素的顺序依次。而冒泡阶段则正相反。
如下图
当事件触发时body会先得到有事件发生的信息,然后依次往下传递,直到到达最详细的元素。这就是事件捕获阶段。
还记得事件注册方法ele.addEventListener(type,handler,flag)吧,Flag是一个Boolean值,true表示事件捕捉阶段执行,false表示事件冒泡阶段执行。
接着就是事件冒泡阶段。从下往上 依次执行事件处理函数(当然前提是当前元素为该事件注册了事件句柄)。
在这个过程中,可以阻止事件的冒泡,即停止向上的传递。
阻止冒泡有时是很有必要的,例如
<div onclick=funcA()> <button onclick=funcB()>Click</button> </div>
本意是如果点击div中按钮以外的位置时执行funcA,点击button时执行funcB。但是实际点击button时就会先后执行funcB,funcA。
而如果在button的事件句柄中阻止冒泡的话,div就不会执行事件句柄了。
拖动事件
①相关拖动事件的触发时机 ☟☟
dragstart:选择当前元素并开始拖动时
drag:拖动当前元素并移动时
dragend:结束对当前元素的拖动(可以是将拖动内容放到其他容器中,也可以是中断拖动)
dragenter: 拖动元素进入当前元素时(区别于dragstart)
dragover: 拖动元素在当前元素上移动时(区别于drag)
dragleave: 拖动元素离开当前元素时(区别于dragover)
drop:有拖动元素被扔进当前元素(当前元素必须是能接受拖动内容的元素,如input:text,textarea...)
②可拖动属性
HTML5中 元素有一个属性draggable(IE10+)。true的时候是可以拖动。false则不可。
图片和链接元素默认是true,其他则都为false。
<img src="hello.png" draggable="false" /> <div draggble="true">Can be dragged</div>
如果拖动元素A(例如一张图片),当光标移动到一个不可放置的元素B(例如另一张图片)时
光标会变成无法拖入的样式(圈圈里面加个反斜线),且不会发生drop事件。
如果重写该元素的dragover和dragenter事件的话,可以改变这种情况。(改变了拖动时光标的样式,并且可以触发drop事件)
var main = document.getElementById("main"); main.ondragover = function() { event.returnValue = false;//取消默认行为 } main.ondragenter = function() { event.returnValue = false; }
③dataTransfer对象
在发生上述提及的这些拖动事件时,事件对象都会含有一个储存着拖动的数据的dataTransfer对象。(除此之外的事件是没有这个对象的,例如click,focus)
dataTransfer对象有两个主要方法getData()和setData();
显而易见,这分别是用来获取或设置拖动的数据的方法。但是使用过程中需要传入数据类型的参数。
考虑兼容的话,数据类型目前有两种:"text","URL"。二者的区别只有当拖动内容到另一个浏览器窗口(或新建浏览器的Tab位置)时才会体现出来。
"text"类型不会被特殊处理。"URL"类型则会被当成网页中的链接。
dataTransfer对象的用处:根据需要,开发人员可以在dragstart时用setData()改变拖动数据,或者是在drop时,对getData()获得的数据进行校验。例子:
<input type="text" id="studentID" size=5 maxLength=5/> <div id="tabA" schoolId="007"></div> <script> var stuId = document.getElementById("studentID"); stuId.ondrop = function(e){ e = e || window.event; var id = e.dataTransfer.getData("text"); event.returnValue=!/\D/.test(id); } /***************************************/ var tabA = document.querySelector("#tabA"); tabA.ondragstart=function(){ e = e || window.event; var sid=this.getAttribute("schoolId"); e.dataTransfer.setData("text",+sid); } </script>
dataTransfer还有两个决定拖动效果的属性:dropEffect,effectAllowed。
用原生JS写的事件工具类
var EventUtil = function() { return { //获得事件 getEvent: function(e) { return e || window.event; } //获得事件源 , getTarget: function(e) { return e.target || e.srcElement; } //添加事件监听 , addEvent: function(elem, type, fn) { if (elem.attachEvent) { elem.attachEvent('on' + type, fn); } else if (elem.addEventListener) { elem.addEventListener(type, fn, false); } } //移除事件监听 , removeEvent: function(elem, type, fn) { if (elem.detachEvent) { elem.detachEvent('on' + type, fn); } else if (elem.removeEventListener) { elem.removeEventListener(type, fn, false); } } //取消默认行为 , preventDefault: function(e) { if (e.preventDefault) { e.preventDefault(); } else { e.returnValue = false; } } //取消冒泡 , stopPropagation: function(e) { if (e.stopPropagation) { e.stopPropagation(); } else { e.cancelBubble = true; } } }; }
网页缩放触发resize的问题
平常我们提起resize事件,会想起拖动浏览器边缘,或者点击最大最小化按钮。
而实际上还有另一种情况,就是网页被缩放,无论是Ctrl + mousewheel 还是 通过浏览器的工具栏中的按钮。
缩放的时候,body中其他元素的长宽值不会发生变化,但是页面文档的滚动高度,可视高度会变化(如缩小的时候,看到的内容变小,但是看到的内容量会变大)
如
document.body.scrollHeight/document.activeElement.scrollHeight
document.body.clientHeight