DOM事件模型
DOM即文档对象模型。是HTML和XML文档的编程接口。
DOM事件:就是用户或浏览器自身执行的某种动作。click,load,onmouseover,onmouseout。
DOM事件流:描述的是从页面中接收事件的顺序
DOM事件流包括三个阶段:
- 事件捕获阶段(capture phase
- 处于目标阶段(target phase)
- 事件冒泡阶段(bubbling phase)
DOM事件模型分为两类
捕获事件:
从外向内找监听函数,DOM树上的表现就是由根节点传播到叶子节点。
冒泡事件:
从内向外找监听函数,DOM树上就是事件从叶子节点传播到根节点。
W3C事件模型:首是进入捕获阶段,直到达到目标元素,再进入冒泡阶段
标准的事件处理模型分为三个阶段:
- 父元素中所有的捕捉型事件(如果有)自上而下地执行
- 目标元素的冒泡型事件(如果有)
- 父元素中所有的冒泡型事件(如果有)自下而上地执行
示例代码:点击这里
注册事件监听
son.addEventListener("event", fn, bool);
// 如果bool不填 就默认是冒泡事件 如果bool为ture就是监听事件
target 与 currentTarget的区别
- e.target -用户操作的元素
- e.currentTarget -程序员监听的元素
- this是e.currentTarget,不推荐使用它
特例:如果监听的是用户点击的div,也就是没有考虑父子同时被监听
div.addEventListener("click", fn);
div.addEventListener("click", fn,ture);
谁先监听谁先执行
<body>
<div class="son">文字</div>
<script>
//冒泡
let son = document.querySelector(".son");
son.addEventListener("click", () => {console.log(1)});
// 监听
son.addEventListener( "click",() => { console.log(2)},true);
</script>
</body>
// 返回 1 , 2
我们可以取消冒泡但是不能取消监听 e.stopPropagation()这个可以中断冒泡,浏览器就不会再向上走。
不可冒泡事件
引用知乎的一篇文章:JavaScript 中那些不会冒泡的事件
UI事件
- load
- unload
- scroll
- resize
焦点事件
- blur
- focu
鼠标事件
- mouseleave
- mouseenter
例如 scroll事件
scroll 事件在元素滚动条在滚动时触发。
我们创建一个网页测试以下
// 样式
#container {
width: 100%;
height: 400px;
border: 1px solid red;
}
#outer {
width: 80%;
height: 600px;
border: 1px solid blue;
}
#inner {
width: 40%;
height: 900px;
border: 1px solid pink;
}
// 网页本体
<div id="grandFather">
<div id="father">
<div id="son">123</div>
</div>
</div>
如下图所示
加上script设置成了冒泡事件
<script>
father.addEventListener(
"scroll",
function () {
console.log("滚动了");
});
</script>
如果它是一个冒泡事件当鼠标在#son
和#father
区域滑动时就会在控制台输出滚动了。但是没有输出任何内容。他就是一个不可冒泡事件。同样也对他没法进行e.stopPropagation()
阻止冒泡。
我们阻止滚动就用的另一种方法wheel
和touchtstart
<script>
// 去除鼠标滚动
father.addEventListener("wheel", function (e) {
console.log("滚动了");
e.preventDefault();
});
// 去除手机触屏滚动
father.addEventListener("touchstart", function (e) {
console.log("滚动了");
e.preventDefault();
});
</script>
自定义事件
创建了一个名称为"frank"
可以冒泡但是不能不能阻止冒泡的自定义事件
<body>
<div id="div1">
<button id="button1">点击触发 frank 事件</button>
</div>
<script>
let button = document.querySelector("#button1");
button.addEventListener("click", () => {
const event = new CustomEvent("frank", {
detail: { name: "frank", age: 18 },
bubbles: true,
});
button.dispatchEvent(event);
});
div1.addEventListener("frank", (e) => {
console.log("frank事件触发了");
console.log(e.detail);
});
</script>
</body>
事件委托
事件委托就是利用事件的冒泡原理,委托他们的父级代为执行事件。
举例:在一个公司里面有同事的快递到了,他有2个方式,一是自己去公司外面拿快递,二是委托前台的同事代为签收。当然第二种方式更加的便捷,同时即使公司来了新员工,前台同事也会在收到寄给新员工的快递后核实并代为签收。
下面就是来了一个新同事span的例子。
<body>
<div id="div1"></div>
<script>
setTimeout(() => {
const button = document.createElement("button");
button.textContent = "click1";
div1.appendChild(button);
}, 1000);
div1.addEventListener("click", (e) => {
// target就可以表示为当前的事件操作的dom
const t = e.target;
// 获取标签的小写
if (t.tagName.toLowerCase() === "button") {
console.log("button 被点击了");
}
});
</script>
</body>
封装好的事件委托
<body>
<div id="div1"></div>
<script>
setTimeout(() => {
const button = document.createElement("button");
button.textContent = "click1";
div1.appendChild(button);
}, 1000);
on("click", "#div1", "button", () => {
console.log("button被点击了2");
});
function on(eventType, element, selector, fn) {
if (!(element instanceof Element)) {
element = document.querySelector(element);
}
element.addEventListener(eventType, (e) => {
const t = e.target;
// 判断传进来的element是否符合button
while (!t.matches(selector)) {
// 找到最顶层 也就是 #div1 找不到了就停止
if (element === t) {
t = null;
break;
}
// 传进的元素等于他的爸爸
t = t.parentNode;
}
t && fn.call(t, e, t); // 第一个是 this
});
return element;
}
</script>
</body>
注意:JS不支持事件 ,JS只是调用了DOM提供的addEventListener