Open
Description
一、事件
-
事件就是一件事情或行为是元素天生自带的,比如鼠标事件,键盘事件等
1. 事件绑定
- DOM0:
element.onXXX = function(){}
,例如box.onclick = function(){}, box.onclick = null
。DOM0 事件绑定利用的是给已经存在的私有属性赋值,事件绑定后浏览器会建立监听机制。 - 事件监听 DOM2:
element.addEventListener('xxx', function(){}, false)
,element.removeEventListener('xxx', fn)
, - 事件捕获:
element.attachEvent('xxx', function(){})
,DOM2 中的监听事件可以给同一个元素对象绑定多个事件形成事件池,事件的执行顺序是按队列的结构依次执行。addEventListener('xxx', function(){}, false)
中xxx
是DOM中的事件也可以自定义,function(){}
中的this
指向element
。 addEventListener()
, 第三个参数默认值是 false。true:表示事件在捕获阶段执行
。false 表示事件在冒泡阶段执行
- 上面的方法执行时浏览器会传递一个事件对象,根据绑定的事件不同,浏览器传递的事件对象也就不同
一个小例子
oBox.onclick = function(ev){
}
oBox1.onclick = function(ev){
}
- 一些比较重要的事件对象属性
target
:操作的元素,preventDefault
:阻止默认行为。stopPropagation
:阻止冒泡事件的传播。
2. 事件默认行为
默认行为:事件本身就带有的,即使没有绑定方法,也会存在一些默认效果,这些默认效果就是默认行为。比如
<a></a>
标签的点击行为:页面跳转,HASH 定位(锚点定位)。(扫个盲:锚点定位是在根目录下给a
标签的链接加上一个标识<a href='#xx'></a>
和页面的某个节点的 IDxx
匹配。)
<a href="#box1">我跳到box1</a>
<a href="#" name="#box2">我跳到box2</a>
<div id="box1"></div>
<div id="box2"></div>
HASH 定位能够无刷新页面将数据返回。可用于 SPA 单页应用。
3. 阻止默认行为
- 使用
preventDefault
,以<a></a>
示例
[a标签].onclick = function(event) {
event = event || window.event // 兼容浏览器
event.preventDefault ? event.preventDefault() || return false
}
<!-- 或者 -->
<a href="javascript:;"></a>
<a href="javascript:void 0;"></a>
<a href="javascript:undefined;"></a>
<a href="javascript:null;"></a>
4. 事件传播机制
- 事件的
冒泡传播机制
指:触发子元素的事件,父元素的事件也被相继触发,一般认为body
是最外层的元素,这个传播过程就是事件冒泡。 - 事件
捕获机制
:当内部事件触发时,浏览器首先会在外层元素向内查找,找到事件触发的源头,这个过程就是捕获事件。而捕获的目的是为了规划冒泡的传播路径。
所以触发事件后,先有捕获事件->到执行目标事件->才有冒泡事件。
小思考
- 移动浏览器单击为什么有 300ms 延迟的问题?怎么解决?
- 原因:在 PC 端 click 事件属于点击事件可以连续双击。在移动端的 click 代表的是单击行为,移动端是以 300ms 为零界限判断是否再有点击行为,如果在 300ms 内再次点击是属于双击的。300ms 外点击才是下一次单击。
- 解决:禁用缩放
<meta name="viewport" content="user-scalable=no" />
html { touch-action: manipulation; } /* 或者*/ html { touch-action: manipulation; // IE11+ -ms-touch-action: manipulation; // IE10 }
- 不禁用缩放
<meta name="viewport" content="width=device-width" />
二、事件委托(事件代理)
事件委托是指:一个容器内的很多子元素都需要做某些相同事的话,那么可以利用
冒泡传播机制
直接在父容器上处理子元素要做的事,这样就不需要给每个子元素都绑定方法,根据事件对象中的事件源ev.target
来做处理。
- 场景:菜单折叠等。
事件委托的场景题1
- 获取标签 ul 下的所有子标签li
<ul>
<li>1<li/>
<li>2<li/>
<li>3<li/>
<li>4<li/>
<li>5<li/>
</ul>
let ul = document.querySelector('ul')
ul.addEventListener('click', function(e){
console.log(e.target.tagName.toLowerCase())
if(e.target.tagName.toLowerCase() === 'li') {
let liList = document.querySelectorAll('li')
// 类数组需要使用 call 指定调用对象
let index = Array.prototype.indexOf.call(liList, e.target)
console.log(e.target.innerHTML, index)
}
})
(面试题)event.stopPropagation() 和 event.preventDefault() 区别
stopPropagation()
是用于阻止事件的进一步传播,但是不能阻止事件的默认行为,像 a 标签点击的默认跳转行为- preventDefault() 可以阻止默认行为。
使用场景二
- 假设一个用户被禁封,点击页面任何按钮时,alter 出禁封的信息实现。可以使用捕获阶段排阻止用户的事件传播
//param: banner: 表示该用户被禁封
window.addEventListener('click', function(e){
if(banner === true){
e.stopPropagation()
}
}, true)
(面试题)event.target 和 event.currentTarget 的区别
- event.target 是 触发事件的元素。
- event.currentTarget 是绑定事件监听的元素。