事件
推文:
事件
事件是可以被 JavaScript 侦测到的用户行为(可以这么理解,事件就是用户行为)。事件通常与函数配合使用(叫做“注册事件监听函数”),当监听到用户有这个行为,执行事件对应的函数,响应用户的行为。
事件举例:
- 鼠标点击
- 页面或图像载入
- 鼠标悬浮于页面的某个热点之上
- 在表单中选取输入框
- 确认表单
- 键盘按键
应用怎么通过事件与用户行为交互的
过程为:针对要监听的行为注册事件——用户的某种行为触发事件——执行监听函数——响应用户行为(在设计模式中,这也叫做观察者模式)。
比如给页面上一个按钮注册点击事件,事件的处理程序是改变页面背景色,当用户点击的时候(用户行为),就会触发按钮的点击事件,执行处理程序,从而改变页面的背景色。
事件流概念
事件流分为冒泡流和捕获流。
当你点击页面的按钮时,可以认为你点击了这个按钮的同时,也点击了这个按钮的容器(父级元素),也可以认为你点击了页面。
如果在它们身上都注册了点击事件,那么到底是哪个先接收到了事件呢?这就有了事件流的概念,描述的是页面中元素(dom对象)接收事件的顺序。
由于历史原因,不同的浏览器开发团队提出了不同的事件流概念,也就是后来的事件冒泡和事件捕获,这两个描述的事件流顺序刚好是相反的。
IE认为事件是冒泡的,从点击的元素开始逐级向上级传递,现在所有的浏览器都默认事件冒泡。
而Netscape Communicator 团队则认为事件是从最外层传递进来的,叫做事件捕获,事件捕获的用意在于在事件到达预定目标之前捕获它。由于老版本的浏览器不支持,因此很少有人使用事件捕获。一般事件冒泡可以放心地使用,在有特殊需要时再使用事件捕获。
“ DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段)。但是不同浏览器和版本之间这些划分时机并不一致
假如我们点击一个div, 实际上是先点击document,然后点击事件传递到div,而且并不会在这个div就停下,div有子元素就还会向下传递,最后又会冒泡传递回document。
为了兼容更多的浏览器,非特殊情况一般我们都是把事件添加到在事件冒泡阶段。
哪些事件冒泡,哪些事件不冒泡
根据事件类型的场景来分类:
焦点事件
blur,focus:不冒泡,所有浏览器都支持它。
focusin,focusoutt:与 HTML 事件 focus , blur等价,但冒泡。
focus 和 blur最大问题是它们不冒泡。所以在代理场景中下会用另外的focusin和focusoutt。比如在下拉列表等许多常用的效果中,事件代理往往非常的重要,因为许多在各个链接上触发的事件,往往可以很容易的在根节点中进行监听。这个转载了一篇关于focus和blur代理的译文——谈 focus 和 blur 的事件代理。
鼠标事件
除了 mouseenter 和 mouseleave,所有鼠标事件都会冒泡,也可以被取消,而取消鼠标事件将会影响浏览器的默认行为。
click,dblclick(双击主鼠标按钮),mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup
移动到后代元素上不会触发的:mouseenter,mouseleave
得到另外一个元素时才触发,不仅仅是针对当前元素而言:mouseout,mouseover(在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。)
页面上的所有元素都支持鼠标事件。
注意:取消鼠标事件的默认行为还会影响其他事件,因为鼠标事件与其他事件是密不可分的关系。比如:
只有在同一个元素上相继触发 mousedown 和 mouseup 事件,才会触发 click 事件;如果mousedown 或 mouseup 中的一个被取消,就不会触发 click 事件。
类似地,只有触发两次 click 事件,才会触发一次 dblclick 事件。如果有代码阻止了连续两次触发 click 事件(可能是直接取消 click事件,也可能通过取消 mousedown 或 mouseup 间接实现),那么就不会触发 dblclick 事件了。
阻止冒泡
w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true
事件的注册
分为在html中注册或者在js中注册,在js中注册也分为两种写法:dom0与dom2级的事件注册
html中注册
缺点是 HTML 与 JavaScript 代码紧密耦合,改动html中代码的同时也得改动js中的代码
<input type="button" value="Echo Username" onclick="alert(username.value)">
js中注册
dom0级事件注册:
var btn = document.getElementById("myBtn"); btn.onclick = function(){ alert("Clicked"); };
删除事件:
btn.onclick = null; //删除事件处理程序
dom2级事件注册(addEventListener):三个参数,处理程序里的this都指向元素本身。
var btn = document.getElementById("myBtn"); btn.addEventListener("click", function(){ alert(this.id); }, false);
最后这个布尔值参数如果是 true,表示在捕获阶段调用事件处理程序;如果是 false,表示在冒泡阶段调用事件处理程序。上面这个程序会在事件冒泡阶段被触发
使用 DOM2 级方法添加事件处理程序的主要好处是可以添加多个事件处理程序,事件按照注册顺序触发:
var btn = document.getElementById("myBtn"); btn.addEventListener("click", function(){ alert(this.id); }, false); btn.addEventListener("click", function(){ alert("Hello world!"); }, false);
通过 addEventListener()添加的事件处理程序只能使用 removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。通过 addEventListener()添加的匿名函数将无法移除,上面那种就是匿名函数,下面这种写法就能被移除:
var btn = document.getElementById("myBtn"); var handler = function(){ alert(this.id); }; btn.addEventListener("click", handler, false); btn.removeEventListener("click", handler, false);
IE的事件注册
attachEvent()和 detachEvent(),两个参数,可注册多个事件,但是事件触发顺序与注册顺序是相反的,都是在冒泡阶段被处理,且处理可程序里的this指向window。事件的移除中同样不能移除匿名函数
IE事件注册与移除的全过程:
var btn = document.getElementById("myBtn"); var handler = function(){ alert("Clicked"); }; btn.attachEvent("onclick", handler); btn.detachEvent("onclick", handler);
阻止事件的默认行为
w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false;
要阻止特定事件的默认行为,可以使用 preventDefault()方法,前提是事件对象event的属性 cancelable 设置为 true 。
function preventDefa(e){ if(window.event){ //IE中阻止函数器默认动作的方式 window.event.returnValue = false; } else{ //阻止默认浏览器动作(W3C) e.preventDefault(); } }
例如,链接的默认行为就是在被单击时会导航到其 href 特性指定的 URL。如果你想阻止链接导航这一默认行为,那么通过链接的
onclick 事件处理程序可以取消它,如下面的例子所示。
var link = document.getElementById("myLink"); link.onclick = function(event){ event.preventDefault(); };
事件代理
在JavaScript中,经常会碰到要监听列表中多项li的情形,假设我们有一个列表如下,要实现以下功能:当鼠标点击某一li时,alert输出该li的内容
<ul id="list"> <li id="item1">item1</li> <li id="item2">item2</li> <li id="item3">item3</li> <li id="item4">item4</li> </ul>
一个办法是给每个li注册监听事件。
另一个办法就是:把事件处理器添加到一个父级元素上,这样就避免了把事件处理器添加到多个子级元素上。
事件代理机制
事件代理用到了两个在JavaSciprt事件中两个特性:事件冒泡以及目标元素。使用事件代理,我们可以把事件处理器添加到一个元素上,等待一个事件从它的子级元素里冒泡上来,并且可以得知这个事件是从哪个元素开始的。
事件代理实现
第一步,找到目标元素
function getEventTarget(e) { e = e || window.event;//事件对象 return e.target || e.srcElement; }
第二步,判断目标元素,进行相关操作
function editCell(e){ var target = getEventTarget(e); if(是否是目标元素) { // DO SOMETHING WITH THE CELL } }
事件代理推文:【翻译】谈 focus 和 blur 的事件代理