dom元素滚动条⾼度js_DOM事件与事件委托点击事件
<div class = 爷爷>
<div class = 爸爸>
<div class = ⼉⼦>
⽂字
</div>
</div>
</div>
// 即 .爷爷 > .爸爸 > .⼉⼦
// 给三个 div 分别添加事件监听 fnYe/fnBa/fnEr
提问1:点击了谁
点击⽂字,算不算点击⼉⼦?
点击⽂字,算不算点击爸爸?
点击⽂字,算不算点击爷爷?
答案:都算
提问2:调⽤顺序
点击⽂字,最先调⽤ fnYe / fnBa / fnEr 中的哪⼀个函数?
答案:都⾏
IE5 认为先调 fnEr,⽹景认为先调 fnYe,然后掐上了
最后闹到了 W3C
2002年,W3C 发布标准
规定浏览器应该同时⽀持两种调⽤顺序
⾸先按 爷爷 => 爸爸 => ⼉⼦ 顺序看有没有函数监听
然后按 ⼉⼦ => 爸爸 => 爷爷 顺序看有没有函数监听
由监听函数就调⽤,并提供事件信息,没有就跳过
术语
从外向内监听函数,叫事件捕获
从内向外监听函数,叫事件冒泡
疑问:这样是不是把 fnYe / fnBa / fnEr 都调⽤两次呐?=> 开发者可以⾃⼰选择把 fnYe 放在捕获阶段还是放在冒泡阶段⽰意图
addEventListener
事件绑定API
baba.attachEvent('onclick',fn) // 冒泡 => IE5*
baba.addEventListener('click',fn) // 捕获 => ⽹景
baba.addEventListener('click',fn,bool) // W3C
如果 bool 不传或为 falsy
就让 fn ⾛冒泡,即当浏览器在冒泡阶段发现 baba 有 fn 监听函数,就会调⽤ fn ,并提供事件信息。如果 bool 为 true
就让 fn ⾛捕获,即当浏览器在捕获阶段发现 baba 有 fn 监听函数,就会调⽤ fn,并提供事件信息。⼀个特例
背景
只有⼀个 div 被监听(不考虑⽗⼦同时被监听)
fn 分别在捕获阶段和冒泡阶段监听 click 事件
⽤户点击的元素就是开发者监听的
代码
div.addEventLisenter('clicl', f1)
div.addEventLisenter('clicl', f2, true)
请问 f1 先执⾏还是 f2 先执⾏?
如果把两⾏代码调换位置后,请问哪个先执⾏?
答案:谁先监听谁先执⾏。 => 这是⼀个特例
target V.S. currentTarget
区别
e.target - ⽤户操作的元素
e.currentTarget - 开发者监听的元素
this 是 e.currentTarget,不推荐使⽤
举例
div > span {⽂字},⽤户点击⽂字
e.target 就是 span
e.currentTarget 就是 div
取消冒泡
捕获不可取消,但冒泡可以
e.stopPropagation() 可中断冒泡,浏览器不再向上⾛
⼀般⽤于封装某些独⽴的组件
不可取消冒泡
有些事件不可取消冒泡
MDN 搜索 scroll event ,看到 Bubbles 和 Cancelable Bubbles 的意思是该事件是否冒泡
Cancelable 的意思是开发者是否可以取消冒泡
上⾯提到 scroll ,那么如果阻⽌滚动
⾸先scroll 事件不可取消冒泡
阻⽌ scroll 默认动作没⽤,因先有滚动才有滚动事件
要阻⽌滚动,可阻⽌ wheel 和 touchstart 的默认动作
需要准滚动条所在的元素
JS Bin j s.jirengu
但是滚动条还能⽤,可⽤ CSS 让滚动条 display: none CSS 也可以
使⽤ overf: hidden 可以直接取消滚动条
但此时 JS 依然可以修改 scrollTop
事件委托(event.target 属性可以实现事件委托)
事件委托(
场景⼀
现在需要给100个按钮添加点击事件,怎么办?
答:监听这100个按钮的祖先,等冒泡的时候判断 target 是不是这100个按钮中的⼀个。
场景⼆
现在要监听⽬前不存在的元素的点击事件,怎么办
答:监听祖先,等点击的时候看看是不是我想要监听的元素即可
优点
省监听数(内存)
可以监听动态元素
封装事件委托
要求
函数 on('click', '#div1', 'button',fn)
当⽤户点击 #div1 ⾥的 button 元素时,调⽤ fn 函数
js控制滚动条需要⽤到事件委托
答案⼀
判断 target 是否匹配 'button'
const div1 = document.querySelector("#div1");
setTimeout(() => {
const button = ateElement("button");
div1.appendChild(button);
}, 1000);  // ⼀秒钟后在 div1 中添加button元素
on("click", "#div1", "button", () => {
console.log("button被点击了");
});
function on(eventType, element, selector, fn) {
if (!(element instanceof Element)) {
element = document.querySelector(element);
}
element.addEventListener(eventType, (e) => {
const t = e.target;
if (t.matches(selector)) {
fn(e);
}
});
} // 封装函数 on
⾸先通过这个 on 函数理解事件委托,给传⼊ element 参数的元素添加⼀个监听,然后判断当前的 target 是否满⾜ selector,如果满⾜就调⽤函数,如果不满⾜就放过。
但是这个⽅法是有误区的,如果 传⼊ selector 参数的元素还被 span 包围了,也就是说 button 元素中还含有⼀个 span ,然后 span 元素⾥有⽂字内容,这时我点击 button,委托事件是不会触发的,因为实际上我点击的是 span 元素,例⼦如下所⽰:
const div1 = document.querySelector("#div1");
setTimeout(() => {
const button = ateElement("button");
const span = ateElement("span");
button.appendChild(span);
div1.appendChild(button);
}, 1000);
on("click", "#div1", "button", () => {
console.log("button被点击了");
});
function on(eventType, element, selector, fn) {
if (!(element instanceof Element)) {
element = document.querySelector(element);
}
element.addEventListener(eventType, (e) => {
const t = e.target;
if (t.matches(selector)) {
fn(e);
}
});
}
答案⼆
递归判断 target / target 的爸爸 / target的爷爷
function on(eventType, element, selector, fn) {
if (!(element instanceof Element)) {
element = document.querySelector(element);
}
element.addEventListener(eventType, (e) => {
let el = e.target;
while (!el.matches(selector)) {
if (element === el) {
el = null;
break;
}
el = el.parentNode;
}
el && fn.call(el, e, el);
});
return element;
}
JS ⽀持事件吗?
⽀持,也不⽀持。上⾯所说的 DOM 事件不属于 JS 的功能,属于浏览器提供的 DOM 的功能JS 只是调⽤了 DOM 提供的 addEventListener

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。