DOM事件,捕获与冒泡处理,委托模式
事件流程
3阶段:
1 捕获阶段
事件应当从最顶层元素开始执行 一层层往下 直到最精确的元素
2 处于目标阶段
事件在最精确的元素身上执行
3 冒泡阶段
事件应当从最精确元素开始执行 一层层往上 直到最顶层元素
最顶层元素: IE8及以下中 最顶层元素是document
高级浏览器中 最顶层元素是window
之前介绍的 元素.on事件类型 = 函数
这种方式 是DOM0级事件绑定方式 它只能够绑定到冒泡阶段
window.onclick = function() {
console.log("window");
}
document.onclick = function() {
console.log("document");
}
document.documentElement.onclick = function() {
console.log("html");
}
document.body.onclick = function() {
console.log("body")
}
//运行结果:body
html
document
window
DOM2级事件绑定方式
元素.addEventListener(type, handler, boolean);
type 事件类型字符串 不带on
handler 事件处理函数
boolean 布尔值 如果值为true 该绑定是绑定到捕获阶段 否则绑定到冒泡阶段
元素.removeEventListener(type, handler, boolean);
type 事件类型字符串 不带on
handler 事件处理函数
boolean 布尔值 如果值为true 该移除是移除捕获阶段 否则移除冒泡阶段
IE8 浏览器及以下的浏览器 不支持addEventListener
元素.attachEvent(type, handler)
type 事件类型字符串 带on
handler 事件处理函数
没有第三个参数 表示不能绑定到捕获阶段
元素.detachEvent(type, handler)
type 事件类型字符串 带on
handler 事件处理函数
注:想要移除的时候,一定要确保移除的函数就是绑定的函数
注:当处于目标阶段时,事件的执行不分捕获与冒泡,而是依照绑定顺序依次执行
<script>
// 定义变量 简化书写
var w = window;
var d = document;
var h = document.documentElement;
var b = document.body;
// 给window添加点击事件
w.onclick = function() {
console.log("我是window冒泡")
}
// 给document添加点击事件
d.onclick = function() {
console.log("我是document冒泡")
}
// 给html添加点击事件
h.onclick = function() {
console.log("我是html冒泡")
}
// 给body添加点击事件
b.onclick = function() {
console.log("我是body冒泡")
}
// 通过dom2级方式绑定事件到捕获阶段
w.addEventListener("click", function() {
console.log("我是window捕获")
}, true)
d.addEventListener("click", function() {
console.log("我是document捕获")
}, true)
h.addEventListener("click", function() {
console.log("我是html捕获")
}, true)
b.addEventListener("click", function() {
console.log("我是body捕获")
}, true)
</script>
运行结果如图:
阻止冒泡
阻止冒泡指的是当子元素的事件触发之后 不要让父元素的事件触发
事件流程分捕获与冒泡,通常我们只会往冒泡阶段添加事件。
有些时候,我们希望子元素执行事件,父元素不执行事件。
- 高级浏览器中
- e.stopPropagation()
- IE浏览器中
-
e.cancelBubble = true;
-
阻止冒泡案例(让放大和移动可以不受对方影响)
功能分析:
1 当按住裁剪区域(亮的部分)并拖动时 可以左右 上下移动
按住: mousedown
拖动: mousemove
在mousemove事件中改变定位值
要求计算定位值的变化 = 鼠标的变化量(鼠标移动之后的位置减去鼠标按下的位置)
2 当按住右下角的点并拖动时 可以改变裁剪区域的大小
按住: mousedown
拖动: mousemove
在mousemove事件中改变宽高
要求计算宽高的变化 = 鼠标的变化量(鼠标移动之后的位置减去鼠标按下的位置)
<style>
*{margin: 0;padding: 0;}
#box{width: 430px; height: 430px; margin: 50px auto; border: 1px solid #000; background: url(./images/small.jpg) no-repeat 0 0;position: relative;}
.mask{width: 100%;height: 100%; position: absolute;top: 0;left: 0; background: rgba(65,65,65,.4);}
#area{width:200px;height: 200px; position: absolute;top: 0; left: 0; background: url(./images/small.jpg) no-repeat 0 0;}
#dot{width: 10px;height: 10px; position: absolute; right: -5px; bottom: -5px; border-radius: 50%; background: orange;}
</style>
<body>
<div id="box" class="a">
<div class="mask"></div>
<div id="area" class="a">
<div id="dot" class="a"></div>
</div>
</div>
<script>
// 获取元素
var area = document.getElementById("area");
var dot = document.getElementById("dot");
// 添加按住事件 也就是mousedown
area.onmousedown = function(e) {
// 获取鼠标按下时的位置
var mouseX = e.clientX;
var mouseY = e.clientY;
// 获取鼠标按下时的定位值
var left = area.offsetLeft;
var top = area.offsetTop;
// 添加mousemove事件
document.onmousemove = function(e) {
// 获取鼠标现在的位置
var nowX = e.clientX;
var nowY = e.clientY;
// 计算变化量
var resultX = nowX - mouseX + left;
var resultY = nowY - mouseY + top;
area.style.left = resultX + "px";
area.style.top = resultY + "px";
area.style.backgroundPositionX = - resultX + "px";
area.style.backgroundPositionY = - resultY + "px";
}
}
document.onmouseup = function() {
document.onmousemove = null;
}
// 添加小圆点的按住事件 也就是onmousedown事件
dot.onmousedown = function(e) {
e.stopPropagation();
// 记住按下时的宽度高度
var width = area.clientWidth;
var height = area.clientHeight;
// 记住按下时的鼠标位置
var mouseX = e.clientX;
var mouseY = e.clientY;
// 添加移动事件
document.onmousemove = function(e) {
// 记住移动之后的位置
var nowX = e.clientX;
var nowY = e.clientY;
// 计算变化
var resultX = width + nowX - mouseX;
var resultY = height + nowY - mouseY;
area.style.width = resultX + "px";
area.style.height = resultY + "px";
}
}
运行结果:
停止默认行为
在浏览器中,有许许多多的元素,有一些元素是具备默认行为的
比如:
a标签点击的时候会默认跳转页面
dom0级事件阻止a标签跳转,两种方法,1.return false (只适用用0级)
2 .e.preventDefault();
var a = document.getElementsByTagName("a")[0];
a.onclick = function(e) {
alert(123)
// 如果不希望跳转 可以阻止默认行为
// 1 只适用于dom0级
// return false;
// 2 通过事件对象
// e.preventDefault();
}
a.addEventListener("click", function(e) {
// 只能用这种
e.preventDefault();
// dom2级中 return false无法阻止默认行为
// return false;
// })
submit按钮点击时会默认触发表单的submit事件
<form action='/a' method="get">
用户名: <input type="text" name="username" >
密码: <input type="password" name="password" >
邮箱: <input type="text" name="email" >
提交: <button>如果我出现在form中并且没有指定type 则默认为submit</button>
</form>
<script>
var inputs = document.getElementsByTagName("input");
var button = document.getElementsByTagName("button")[0];
button.onclick = function(e) {
var flag = true;
for (var i = 0; i < inputs.length; i++) {
if (inputs[i].value === "") {
flag = false;
break;
}
}
// flag代表是否有空值 为真表示没有空值 为假表示有空值
if (!flag) {
// 为假时会执行这里的代码 不要提交
e.preventDefault();
}
}
委托模式
使用案例:
<body>
<table>
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr><td>张三</td><td>22</td><td>男</td><td>×</td></tr>
<tr><td>张三</td><td>22</td><td>男</td><td>×</td></tr>
<tr><td>张三</td><td>22</td><td>男</td><td>×</td></tr>
<tr><td>张三</td><td>22</td><td>男</td><td>×</td></tr>
<tr><td>张三</td><td>22</td><td>男</td><td>×</td></tr>
<tr><td>张三</td><td>22</td><td>男</td><td>×</td></tr>
</tbody>
</table>
<button id="btn">点我增加数据</button>
<script>
// 获取tbody
var tbody = document.querySelector("tbody");
// 获取btn
var btn = document.querySelector("#btn");
btn.onclick = function() {
tbody.innerHTML += "<tr><td>张三</td><td>22</td><td>男</td><td>×</td></tr>"
}
// 使用委托模式
// 给tbody添加点击事件
tbody.onclick = function(e) {
// 判定 如果是最后一个元素 我们就将当前的行移除
if (e.target === e.target.parentNode.lastChild) {
e.target.parentNode.remove();
}
}
// 使用方式: 将事件绑定给不会被移除的父元素 通过判定方式让代码只再符合某种条件时执行
</script>
</body>