事件
参考:
原生JavaScript和Vue、小程序都是如何阻止事件冒泡的?
DOM事件级别
1 事件流
事件流描述的是从页面中接受事件的顺序。
1.1 事件冒泡
IE 的事件流叫事件冒泡,即事件开始时由最具体的元素接受,然后逐级向上传播到较为不具体的节点(文档),Chrome,Firefox和Safari会将事件一直冒泡到 window 对象。
1.2 事件捕获
Netscape Communicator 团队提出的另一种事件流叫做事件捕获。事件捕获的思想是不太具体的节点应该更早接受到事件,而最具体的节点应该最后接收到事件。事件捕获的用以在于事件到达预定目标之前捕获它。一般从document对象开始传播,用的较少。
1.3 DOM事件流
上图是W3C标准的DOM事件流模型图。
“DOM2级事件” 规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。
2 事件处理程序
前面说的是事件,事件就是用户或浏览器自身执行的某种动作。如:click是事件的名字,事件处理程序就是onclick。为事件指定处理程序的方式有好几种
2.1 HTML 事件处理程序
其实就是,HTML 的on属性
<button onclick="doxxx()">点击</button>
使用这个方法指定的监听代码只会在冒泡阶段触发。
2.2 DOM0 级事件处理程序,缺点:无法设置多个事件处理函数
元素节点的事件属性。
let btn = document.getElementById("myBtn:); btn.onclick = function () { alert("Clickes") }
如果想删除只需:btn.onclick = null; // 删除事件处理程序
2.3 DOM2 级事件处理程序,可以为事件设置多个事件处理函数,可以通过第三个参数 ( useCapture ) 设置在什么阶段执行事件处理函数,默认是 false, 即在事件冒泡阶段执行事件处理函数。
DOM2级事件定义了两个方法,用于处理指定 和 删除事件处理程序的操作:addEventListener() 和 removeEventListener()。
所有DOM节点中都包含这两种方法。并且都接受三个参数:要处理的事件名,作为事件处理程序的函数,一个布尔值(默认false表冒泡)。
(1)EventTarget.addEventListener()
function print() { console.log('Print Hello world'); } var btn = document.getElementById('btn'); btn.addEventListener('click', print, false);
上面代码的意思就是,节点id为btn的元素,使用addEventListener方法绑定click事件,点击的时候会监听函数print会发生。另外,因为useCapture设置为false,所以该函数只在冒泡阶段触发。
当我们需要添加多个不同的监听函数,使用addEventListener非常棒,遵从先添加先触发原则,而且不小心在同一个事件重复添加了同一个函数,它还会自动移除,该函数只会执行一次。
(2)EventTarget.removeEventListener()
顾名思义,用来移除addEventListener方法添加的事件监听函数。
它的参数和addEventListener方法一致,而且写的时候要一一对应。要做到三同,一是在同一个元素节点,二是同一个监听函数,三是第三个参数要一致。
2.4 IE 事件处理程序
(1)事件绑定监听函数:attachEvent(eventType, listener)
(2)事件移除监听函数:detachEvent(eventType, listener)
3 事件对象
3.1 DOM 中的事件对象
bubbles:返回一个布尔值,表示当前事件是否会冒泡。该属性为只读属性
cancelable:返回一个布尔值,表示事件是否可以取消。该属性为只读属性
currentTarget:事件处理程序当前正在处理事件的那个元素
preventDefault():取消事件的默认行为。如果 cancelable 是true,则可以使用这个方法
stopPropagation():取消事件的进一步捕获或冒泡。如果 bubbles为 true,则可以使用这个方法。
事件处理程序使用DOM0 级或是 DOM2 级,都会传入 event 对象,这个我们不用care。
var btn = document.getElementById("myBtn"); btn.onclick = function(event){ console.log(event.type); //"click" }; btn.addEventListener("click", function(event){ console.log(event.type); //"click" }, false); <button onclick="console.log(event.type)()">点击</button>
3.2 IE中的事件对象
与访问DOM中的event对象不同,要访问IE中的event对象有几种不同的方式,取决于指定事件处理程序的方法。
(1) HTML 事件处理程序
<button onclick="console.log(event.type)()">点击</button>
(2) DOM0 级事件处理程序
let btn = document.getElementById("btn"); btn.onclick = function(){ var event = window.event; console.log(event.type); //"click" };
(3) DOM2 级事件处理程序
let btn = document.getElementById("btn"); btn.attachEvent("onclick", function(event){ console.log(event.type); //"click" });
4 事件类型(DMO3 级事件规定了以下几类事件)
DOM3 级事件是在 DOM2 级事件的基础上添加了更多的事件类型,允许自定义事件。
- UI事件,当用户与页面上的元素交互时触发,如:load、scroll
- 焦点事件,当元素获得或失去焦点时触发,如:blur、focus
- 鼠标事件,当用户通过鼠标在页面执行操作时触发如:dbclick、mouseup
- 滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel
- 文本事件,当在文档中输入文本时触发,如:textInput
- 键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress
- 合成事件,当为IME(输入法编辑器)输入字符时触发,如:compositionstart
- 变动事件,当底层DOM结构发生变化时触发,如:DOMsubtreeModified
如何阻止冒泡
例子:
<div onclick="alert('最外层')">
<div onclick="alert('中间层')">
<a id="ahref" href="http://www.javanx.cn" onclick="alert('最里层')">点击我</a>
</div>
</div>
点击:点击我 --》最里层 --》中间层 --》最外层 --》然后转跳链接。这就是冒泡。
1.JavaScript 中
$(function() { $("#ahref").click(function(event) { event.stopPropagation(); }); });
点击:点击我 --》 最里层 --》转跳链接 事件处理过程中,阻止了事件冒泡,但不会阻击默认行为(执行了超链接的跳转)
return false
$(function() { $("#ahref").click(function(event) { return false; }); });
点击:点击我 --》 最里层 --》但不会转跳链接 事件处理过程中,阻止了事件冒泡,也阻止了默认行为(没有执行超链接的跳转)
event.preventDefault()
$(function() { $("#ahref").click(function(event) { event.preventDefault() }); });
点击:最里层 --》中间层 --》最外层,但最后没有转跳 不能阻止事件冒泡,但阻止了默认行为。
2.Vue 解决事件冒泡
Vue.js 提供了事件修饰符,我们只需要添加 click.stop 即可预防事件冒泡
<div @click="cancelSelect"> <div class="picker_alert" @click.stop> <div class="ofh"> <span @click="toRoomManagement">编辑</span> <span @click="confirmRoom">确定</span> </div> </div> </div>
Vue修饰符:.stop, .prevent, .capture, .self, .once
<!-- 阻止单击事件冒泡 --> <a v-on:click.stop="doThis"></a> <!-- 提交事件不再重载页面 --> <form v-on:submit.prevent="onSubmit"></form> <!-- 修饰符可以串联 --> <a v-on:click.stop.prevent="doThat"></a> <!-- 只有修饰符 --> <form v-on:submit.prevent></form> <!-- 添加事件侦听器时使用事件捕获模式 --> <div v-on:click.capture="doThis">...</div> <!-- 只当事件在该元素本身(比如不是子元素)触发时触发回调 --> <div v-on:click.self="doThat">...</div>
提示:使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。
因此,用 @click.prevent.self 会阻止所有的点击,而 @click.self.prevent 只会阻止元素上的点击。