概述
事件绑定:普通绑定和代理绑定
事件冒泡
事件绑定
// 通用的事件绑定函数
function bindEvent(elem, type, fn) {
elem.addEventListener(type, fn)
}
// 普通绑定
const btn1 = document.getElementById('btn1')
bindEvent(btn1, 'click', function (event) {
// console.log(event.target) // 获取触发的元素
event.preventDefault() // 阻止默认行为
alert(this.innerHTML)
})
事件冒泡
事件冒泡:由触发元素一层一层向上传递
阻止事件冒泡:event.stopPropagation()
<div id="div1">
<p id="p1">激活</p>
<p id="p2">取消</p>
<p id="p3">取消</p>
<p id="p4">取消</p>
</div>
<div id="div2">
<p id="p5">取消</p>
<p id="p6">取消</p>
</div>
const p1 = document.getElementById('p1')
bindEvent(p1, 'click', event => {
// event.stopPropagation() // 阻止冒泡
console.log('激活')
})
const body = document.body
bindEvent(body, 'click', event => {
console.log('body clicked')
console.log(event.target)
})
const div2 = document.getElementById('div2')
bindEvent(div2, 'click', event => {
console.log('div2 clicked')
console.log(event.target)
})
事件代理
由于冒泡机制,所以事件会传递到触发元素的父级元素,不需要使用循环为每个同级元素添加事件。
当许多同级元素都需要相同的事件行为时,将事件加在他们的父元素上,比如瀑布流。
由于父级元素可能包含其他的元素,不需要添加事件,所以需要区分。
function bindEvent(elem, type, selector, fn) {
if (fn == null) {
fn = selector
selector = null
}
elem.addEventListener(type, event => {
const target = event.target
if (selector) {
// 代理绑定
if (target.matches(selector)) {
fn.call(target, event)
}
} else {
// 普通绑定
fn.call(target, event)
}
})
}
<div id="div3">
<a href="#">a1</a><br>
<a href="#">a2</a><br>
<a href="#">a3</a><br>
<a href="#">a4</a><br>
<button>加载更多...</button>
</div>
// 代理绑定
const div3 = document.getElementById('div3')
bindEvent(div3, 'click', 'a', function (event) {
event.preventDefault()
alert(this.innerHTML)
})
target.matches(selector)
MDN
Element.matches()
如果元素被指定的选择器字符串选择,
Element.matches()
方法返回true; 否则返回false。
let result = element.matches(selectorString);
result
的值为true
或false
.selectorString
是个css选择器字符串.
示例
比如我们有这样的一个 HTML 片段:
<ul id="list">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
......
<li>item n</li>
</ul>
// ...... 代表中间还有未知数个 li
我们来实现把 #list 下的 li 元素的事件代理委托到它的父层元素也就是 #list 上:
// 给父层元素绑定事件
document.getElementById('list').addEventListener('click', function (e) {
// 兼容性处理
var event = e || window.event;
var target = event.target || event.srcElement;
// 判断是否匹配目标元素
if (target.nodeName.toLocaleLowerCase === 'li') {
console.log('the content is: ', target.innerHTML);
}
});
在上述代码中, target 元素则是在 #list 元素之下具体被点击的元素,然后通过判断 target 的一些属性(比如:nodeName,id 等等)可以更精确地匹配到某一类 #list li 元素之上;
使用 Element.matches 精确匹配
如果改变下 HTML 成:
<ul id="list">
<li className="class-1">item 1</li>
<li>item 2</li>
<li className="class-1">item 3</li>
......
<li>item n</li>
</ul>
// ...... 代表中间还有未知数个 li
这里,我们想把 #list 元素下的 li 元素(并且它的 class 为 class-1)的点击事件委托代理到 #list 之上;
如果通过上述的方法我们还需要在 if (target.nodeName.toLocaleLowerCase === 'li')
判断之中在加入一个判断 target.nodeName.className === 'class-1'
;
但是如果想像 CSS 选择器般做更加灵活的匹配的话,上面的判断未免就太多了,并且很难做到灵活性,这里可以使用 Element.matches API
来匹配;
Element.matches API
的基本使用方法: Element.matches(selectorString)
,selectorString
既是 CSS 那样的选择器规则,比如本例中可以使用 target.matches('li.class-1')
,他会返回一个布尔值,如果 target 元素是标签 li 并且它的类是 class-1 ,那么就会返回 true,否则返回 false;