5.DOM事件详解
1.注册事件的方法
element.addEventListener()
方法
• 参数:
第一个参数:事件类型的字符串(直接书写”click”,不需要加 on)
第二个参数:事件函数
• 同一个元素可以多次绑定事件监听,同一个事件类型可以注册多个事件函数
• 兼容性问题:不支持 IE9 以下的浏览器
<body>
<input type="button" value="点击" id="btn">
<script>
var btn = document.getElementById("btn");
// DOM0 级事件
// 绑定事件的方式
// btn.onclick = function () {
// alert(1);
// };
// 绑定多次相同的事件
// btn.onclick = function () {
// alert(2);
// };
// DOM 2 级事件绑定方式
btn.addEventListener("click",function () {
alert(1);
});
// 多次绑定相同的事件类型,事件会根据书写的顺序进行一个事件排队
btn.addEventListener("click",clickEvent);
function clickEvent() {
alert(2);
}
// 方法不支持 IE 9 以下的浏览器
</script>
</body>
element.attachEvent()
方法。
• 参数:
第一个参数:事件类型的字符串(需要加 on)
第二个参数:事件函数
• 同一个元素可以多次绑定事件监听,同一个事件类型可以注册多个事件函数
• 兼容性问题:只支持 IE10 及以下的浏览器
<body>
<input type="button" value="点击" id="btn">
<script>
var btn = document.getElementById("btn");
// DOM 2 级事件绑定方式
// 兼容:IE 10 及以下浏览器
// IE8 及以下的浏览器处理事件队列时,会出现顺序错乱
btn.attachEvent("onclick",function () {
alert(3);
});
btn.attachEvent("onclick",clickEvent);
function clickEvent() {
alert(4);
}
</script>
</body>
2.注册事件的兼容写法
• 自定义一个注册事件函数
• 参数:事件源,事件类型(不加 on),事件函数
• IE9 及以上的浏览器,使用 addEventListener 方法
• IE9 以下的浏览器,使用 attachEvent 方法
• 判断浏览器时,不需要判断它的版本,可以检测浏览器能力
• 浏览器能力检测:将某个方法的调用作为 if 语句的判断条件,如果浏览器认识该方法返回
true,否则返回 false。
<body>
<input type="button" value="点击" id="btn">
<script>
var btn = document.getElementById("btn");
// 调用函数
addEvent(btn,"click",function () {
alert(1);
});
// DOM 2 级事件绑定方式
// 自己制作一个兼容所有浏览器的绑定事件的函数
// 参数:事件源,事件类型,事件函数
function addEvent(ele,type,fn) {
// IE 9 及以上的浏览器和其他浏览器,使用 addEventListener 方法
// IE 9 以下的浏览器,使用 attachEvent 方法
// 浏览器能力检测
if (ele.addEventListener) {
ele.addEventListener(type,fn);
} else if (ele.attachEvent) {
ele.attachEvent("on" + type,fn);
}
}
</script>
</body>
3.移除事件的其他方法
element.removeEventListener()
方法。
• 参数:
第一个参数:事件类型的字符串(直接书写”click”,不需要加 on)
第二个参数:事件函数引用名
• 注意:没有办法移除一个匿名函数,所以在注册事件时需要单独声明一个有函数名的事件函数。
• 兼容性问题:不支持 IE9 以下的浏览器
element.detachEvent()
方法。
• 参数:
第一个参数:事件类型的字符串(需要加 on)
第二个参数:事件函数
• 注意:没有办法移除一个匿名函数,所以在注册事件时需要单独声明一个有函数名的事件函数。
兼容性问题:只支持 IE10 及以下的浏览器
<body>
<input type="button" value="点击" id="btn">
<script>
var btn = document.getElementById("btn");
// 绑定事件
// btn.onclick = function () {
// alert(1);
// };
// 解除绑定方法
// btn.onclick = null;
// 绑定事件
// btn.addEventListener("click",fun);
// btn.addEventListener("click",fun2);
// 解除绑定
// btn.removeEventListener("click",fun);
// 绑定事件
btn.attachEvent("onclick",fun);
// 解除绑定
btn.detachEvent("onclick",fun);
function fun() {
alert(2);
}
function fun2() {
alert(3);
}
</script>
</body>
4.移除事件的兼容写法
• 自定义一个移除事件函数
• 参数:事件源,事件类型(不加 on),事件函数
• IE9 及以上的浏览器,使用 removeEventListener 方法
• IE9 以下的浏览器,使用 detachEvent 方法
• 建议:将自己封装的一些常用函数和方法,放到一个单独的 .js 文件中。
<body>
<input type="button" value="点击" id="btn">
<script>
var btn = document.getElementById("btn");
// 调用函数
addEvent(btn,"click",fun);
// 移除事件
removeEvent(btn,"click",fun);
function fun() {
alert(1);
}
// DOM 2 级事件绑定方式
// 自己制作一个兼容所有浏览器的绑定事件的函数
// 参数:事件源,事件类型,事件函数
function addEvent(ele,type,fn) {
// IE 9 及以上的浏览器和其他浏览器,使用 addEventListener 方法
// IE 9 以下的浏览器,使用 attachEvent 方法
// 浏览器能力检测
if (ele.addEventListener) {
ele.addEventListener(type,fn);
} else if (ele.attachEvent) {
ele.attachEvent("on" + type,fn);
}
}
// 兼容所有浏览器的 解除绑定事件的函数
// 参数:事件源,事件类型,事件函数
function removeEvent(ele,type,fn) {
// 浏览器能力检测
if (ele.removeEventListener) {
ele.removeEventListener(type,fn);
} else if (ele.detachEvent) {
ele.detachEvent("on" + type,fn);
}
}
</script>
</body>
5.DOM 事件流
事件流的三个阶段
• 第一个阶段:事件捕获
• 第二个阶段:事件执行过程
• 第三个阶段:事件冒泡
• addEventListener() 第三个参数为 false 时,事件冒泡
• addEventListener() 第三个参数为 true 时,事件捕获
• onclick 类型:只能进行事件冒泡过程,没有捕获阶段
• attachEvent() 方法:只能进行事件冒泡过程,没有捕获阶段
<style>
#box1{
width: 300px;
height: 300px;
background-color: yellowgreen;
}
#box2{
width: 200px;
height: 200px;
background-color: pink;
}
#box3{
width: 100px;
height: 100px;
background-color: skyblue;
}
</style>
</head>
<body>
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
<script>
// 获取元素
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
// 添加点击事件
// addEventListener 有第三个参数,用来决定事件流的方向
// 参数值是 布尔类型的值,false 表示事件冒泡过程,true 表示事件捕获过程
// 参数默认值是 false
// box1.addEventListener("click",function () {
// console.log(1);
// },false);
// box2.addEventListener("click",function () {
// console.log(2);
// },false);
// box3.addEventListener("click",function () {
// console.log(3);
// },false);
// box1.addEventListener("click",function () {
// console.log(this.id);
// },true);
// box2.addEventListener("click",function () {
// console.log(this.id);
// },true);
// box3.addEventListener("click",function () {
// console.log(this.id);
// },true);
// onclick 属性添加事件方法只有冒泡过程,没有捕获过程
// attachEvent() 方法添加事件方法只有冒泡过程,没有捕获过程
box1.onclick = function () {
console.log(1);
};
box2.onclick = function () {
console.log(2);
};
box3.onclick = function () {
console.log(3);
};
</script>
</body>HTML
事件委托
• 利用事件冒泡,将子级的事件委托给父级加载
• 同时,需要利用事件函数的一个 e 参数,内部存储的是事件对象
批量给兄弟添加相同事件,用事件委托,减少外部循环优化代码
<style>
* {
margin: 0;
padding: 0;
}
ul {
width: 300px;
border: 1px dashed #f0f;
margin: 50px auto;
font-size: 24px;
line-height: 48px;
list-style: none;
}
li {
padding-left: 20px;
cursor: pointer;
}
</style>
</head>
<body>
<ul id="list">
<li>刘亦菲</li>
<li>杨幂</li>
<li>唐嫣</li>
<li>赵丽颖</li>
<li>刘诗诗</li>
</ul>
<script>
// 让每个 li 被点击后,自己添加特殊的背景色,而其他兄弟不添加
// 以前的思路:获取所有的 li 标签元素,批量添加事件
// 事件委托:可以将一些子级的公共类型的事件委托给他们的父级添加,在父级内部想办法找到真正触发事件的最底层的事件源
// 获取元素
var list = document.getElementById("list");
var lis = list.children;
// 给 ul 添加点击事件
list.onclick = function (e) {
// 在内部要想办法找到真正触发事件的 li
// 借用事件函数内部的一个参数 e,e 是事件对象
// 只要触发事件,函数内部都可以得到一个事件对象,对象中存储了关于事件的一系列数据
// e.target 属性记录的就是真正触发事件的事件源
// 排除其他
for (var i = 0 ; i < lis.length ; i++) {
lis[i].style.backgroundColor = "";
}
e.target.style.backgroundColor = "pink";
};
</script>
</body>
事件对象
• 只要触发事件,就会有一个对象,内部存储了与事件相关的数据。
• e 在低版本浏览器中有兼容问题,低版本浏览器使用的是 window.event
• 事件对象常用的属性:
e.eventPhase | 查看事件触发时所处的阶段 |
---|---|
e.target | 用于获取触发事件的元素 |
e.srcElement | 用于获取触发事件的元素,低版本浏览器使用 |
e.currentTarget | 用于获取绑定事件的事件源元素 |
e.type | 获取事件类型 |
e.clientX/e.clientY | 所有浏览器都支持,鼠标距离浏览器窗口左上角的距离 |
e.pageX/e.pageY | IE8 以前不支持,鼠标距离整个HTML页面左上顶点的距离 |
<style>
#box1{
width: 300px;
height: 300px;
background-color: yellowgreen;
}
#box2{
width: 200px;
height: 200px;
background-color: pink;
}
#box3{
width: 100px;
height: 100px;
background-color: skyblue;
}
</style>
</head>
<body>
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
<script>
// 获取元素
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
// 添加事件
box1.onclick = function (e) {
// e指的就是存储事件对象的参数,只要事件被触发,e就会自动接收数据
// 兼容问题
e = e || window.event;
// e.eventPhase 判断出事件执行时处于哪个阶段
// 1:捕获阶段
// 2:目标阶段
// 3:冒泡阶段
// console.log(e.eventPhase);
// 获取真正触发事件的元素
var target = e.target || e.srcElement;
// console.log(target);
// 获取绑定事件的事件源元素
console.log(e.currentTarget);
// this 指向
console.log(this);
};
</script>
</body>
<style>
#box1{
width: 200px;
height: 200px;
background-color: yellowgreen;
}
</style>
</head>
<body>
<div id="box1"></div>
<script>
// 获取元素
var box1 = document.getElementById("box1");
// e.type 属性获取事件类型
// box1.onclick = function (e) {
// // 事件对象兼容
// e = e || window.event;
// // 触发的事件类型
// console.log(e.type);
// };
// 更多时候可能给同一个元素对象添加不同的事件类型,对应执行的事件函数内部的代码 不同
// box1.onmouseover = function () {
// box1.style.backgroundColor = "skyblue";
// };
// box1.onmouseout = function () {
// box1.style.backgroundColor = "yellowgreen";
// };
// 可以将 所有给一个元素绑定的事件的事件函数写在一个 函数内,通过函数内部的 e.type 判断走不同的分支
box1.onmouseover = fn;
box1.onmouseout = fn;
// 避免添加多个函数,占用更多的内存
function fn(e) {
e = e || window.event;
// 根据事件类型,执行不同的代码
switch (e.type) {
case "mouseover":
box1.style.backgroundColor = "pink";
break;
case "mouseout":
box1.style.backgroundColor = "yellowgreen";
break;
}
}
</script>
</body>
<style>
* {
margin: 0;
padding: 0;
}
body {
height: 1000px;
}
#box1{
width: 200px;
height: 200px;
margin: 100px;
margin-top: 500px;
background-color: pink;
}
</style>
</head>
<body>
<div id="box1"></div>
<script>
// 获取元素
var box1 = document.getElementById("box1");
// 事件对象中有一些获取尺寸的属性
box1.onclick = function (e) {
// client系列: 客户端尺寸,点击的点参考浏览器窗口左上角的距离
console.log(e.clientX);
console.log(e.clientY);
// page 系列:html 页面尺寸,点击的点参考html文档左上角的距离
console.log(e.pageX);
console.log(e.pageY);
};
</script>
</body>
案例
图片跟随鼠标移动效果
<style>
* {
margin: 0;
padding: 0;
}
#pic {
position: fixed;
}
</style>
</head>
<body>
<img src="images/tianshi.gif" alt="" id="pic">
<script>
// 通过 鼠标移动事件给 图片添加 left 和 top 的值
// 获取元素
var pic = document.getElementById("pic");
// 给整个文档添加鼠标移动事件
document.onmousemove = function (e) {
e = e || window.event;
// 给元素的css 属性赋值
pic.style.left = e.clientX + "px";
pic.style.top = e.clientY + "px";
};
</script>
</body>
取消默认行为和阻止事件传播的方式
e.preventDefault() | 取消默认行为 |
---|---|
e.returnValue | 取消默认行为,低版本浏览器使用 |
e.stopPropagation(); | 阻止冒泡,标准方式 |
e.cancelBubble = true; | 阻止冒泡,IE 低版本,标准中已废弃 |
<body>
<a id="link" href="52_图片跟随鼠标移动效果.html">点击</a>
<script>
var link = document.getElementById('link');
link.onclick = function (e) {
e = e || window.event;
alert('hello');
// 普通的方式阻止默认行为 既阻止点击a链接进行跳转网页
// return false;
// DOM 的方法
// e.preventDefault();
// 低版本浏览器需要使用一个对象的属性
e.returnValue = false;
}
</script>
</body>
<style>
#box1 {
width: 300px;
height: 300px;
background-color: red;
}
#box2 {
width: 200px;
height: 200px;
background-color: green;
}
#box3 {
width: 100px;
height: 100px;
background-color: blue;
}
</style>
</head>
<body>
<div id="box1">
<div id="box2">
<div id="box3">
</div>
</div>
</div>
<script>
// 事件冒泡
var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
var box3 = document.getElementById('box3');
var array = [box1, box2, box3];
for (var i = 0; i < array.length; i++) {
var box = array[i];
box.onclick = function (e) {
e = e || window.event;
console.log(this.id);
// 阻止事件冒泡
// e.stopPropagation();
// 低版本浏览器使用 属性
e.cancelBubble = true;
}
}
</script>
</body>
6.DOM特效
1.偏移量属性
offsetParent | 偏移参考父级,距离自己最近的有定位的父级,如果都没有定位参考body(html) |
---|---|
offsetLeft/offsetTop | 偏移位置 |
offsetWidth/offsetHeight | 偏移大小 |
<style>
body {
margin: 0;
}
#box {
/* position: relative; */
width: 300px;
height: 300px;
border: 10px solid yellowgreen;
background-color: skyblue;
overflow: hidden;
margin: 50px;
}
#child {
width: 100px;
height: 100px;
background-color: pink;
margin: 50px;
border: 10px solid yellow;
padding: 10px;
}
</style>
</head>
<body>
<div id="box">
<div id="child">
</div>
</div>
<script>
// 获取元素
var child = document.getElementById("child");
// 元素天生就认识自己的偏移参考父级
// console.log(child.offsetParent);
// 偏移位置
// console.log(child.offsetLeft);
// console.log(child.offsetTop);
// 偏移大小
console.log(child.offsetWidth);
console.log(child.offsetHeight);
</script>
</body>
2.客户端大小
client | 系列没有参考父级元素。 |
---|---|
clientLeft/clientTop | 边框区域尺寸,不常用 |
• clientWidth/clientHeight | 边框内部大小 |
<style>
body {
margin: 0;
}
#box {
width: 100px;
height: 100px;
margin: 50px;
border: 40px solid red;
padding: 10px;
background-color: green;
}
</style>
</head>
<body>
<div id="box">
</div>
<script>
var box = document.getElementById("box");
// client 客户端大小
console.log(box.clientLeft); // 40 边框大小
console.log(box.clientTop); // 40
console.log(box.clientWidth); // 120
console.log(box.clientHeight); //120
</script>
</body>
3.滚动偏移属性
scrollLeft/scrollTop | 盒子内部滚动出去的尺寸 |
---|---|
scrollWidth/scrollHeight | 盒子内容的宽度和高度 |
<style>
*{
padding: 0;
margin: 0;
}
body {
margin: 0;
}
#box {
width: 200px;
height: 200px;
margin: 50px;
border: 30px solid red;
padding: 50px;
background-color: green;
overflow: auto;
}
#box p{
width: 300px;
height: 300px;
background-color: pink;
}
</style>
</head>
<body>
<div id="box">
<p></p>
</div>
<script>
var box = document.getElementById("box");
// 滚动偏移位置和大小
// 滚动条滚动事件
box.onscroll = function () {
console.log(box.scrollLeft);
console.log(box.scrollTop);
};
// console.log(box.scrollLeft);
// console.log(box.scrollTop);
// console.log(box.scrollWidth);
// console.log(box.scrollHeight);
// console.log(box.clientWidth);
// console.log(box.offsetWidth);
</script>
</body>
案例
• 拖拽案例
• 弹出登录窗口
<style>
* {
margin: 0;
padding: 0;
}
.nav {
height: 30px;
background: #036663;
border-bottom: 1px solid #369;
line-height: 30px;
padding-left: 30px;
}
.nav a {
color: #fff;
text-align: center;
font-size: 14px;
text-decoration: none;
}
.d-box {
width: 400px;
height: 300px;
border: 5px solid #eee;
box-shadow: 2px 2px 2px 2px #666;
position: absolute;
top: 40%;
left: 40%;
background-color: white;
/* 不让文字被选中 */
-webkit-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
user-select:none;
}
.hd {
width: 100%;
height: 25px;
background-color: #7c9299;
border-bottom: 1px solid #369;
line-height: 25px;
color: white;
cursor: move;
}
#box_close {
float: right;
cursor: pointer;
}
</style>
</head>
<body>
<div class="nav">
<a href="javascript:;" id="register">注册信息</a>
</div>
<div class="d-box" id="d_box">
<div class="hd" id="drop">注册信息 (可以拖拽)
<span id="box_close">【关闭】</span>
</div>
<div class="bd"></div>
</div>
<script src="common.js"></script>
<script>
// 获取元素
var box = document.getElementById("d_box");
var drop = document.getElementById("drop");
var close = document.getElementById("box_close");
// 给 drop 添加鼠标按下事件,在内部继续绑定一个鼠标移动事件
drop.onmousedown = function (e) {
e = e || window.event;
// 记忆鼠标按下时,鼠标在父盒子内部的间距
var l = e.pageX - box.offsetLeft;
var t = e.pageY - box.offsetTop;
// 鼠标移动事件
drop.onmousemove = function (e) {
e = e || window.event;
// 鼠标移动过程中,可以计算 box 的 left 和 top
var nowleft = e.pageX - l;
var nowtop = e.pageY - t;
// 赋值给 box 的样式属性
box.style.left = nowleft + "px";
box.style.top = nowtop + "px";
};
};
// 鼠标弹起事件
drop.onmouseup = function () {
drop.onmousemove = null;
};
// 点击 关闭 box
close.onclick = function () {
box.style.display = "none";
};
</script>
</body>
<style>
.login-header {
width: 100%;
text-align: center;
height: 30px;
font-size: 24px;
line-height: 30px;
}
ul, li, ol, dl, dt, dd, div, p, span, h1, h2, h3, h4, h5, h6, a {
padding: 0px;
margin: 0px;
}
.login {
width: 512px;
position: absolute;
border: #ebebeb solid 1px;
height: 280px;
left: 50%;
right: 50%;
background: #ffffff;
box-shadow: 0px 0px 20px #ddd;
z-index: 9999;
margin-left: -256px;
margin-top: 140px;
display: none;
}
.login-title {
width: 100%;
margin: 10px 0px 0px 0px;
text-align: center;
line-height: 40px;
height: 40px;
font-size: 18px;
position: relative;
cursor: move;
-moz-user-select:none;/*火狐*/
-webkit-user-select:none;/*webkit浏览器*/
-ms-user-select:none;/*IE10*/
-khtml-user-select:none;/*早期浏览器*/
user-select:none;
}
.login-input-content {
margin-top: 20px;
}
.login-button {
width: 50%;
margin: 30px auto 0px auto;
line-height: 40px;
font-size: 14px;
border: #ebebeb 1px solid;
text-align: center;
}
.login-bg {
width: 100%;
height: 100%;
position: fixed;
top: 0px;
left: 0px;
background: #000000;
filter: alpha(opacity=30);
-moz-opacity: 0.3;
-khtml-opacity: 0.3;
opacity: 0.3;
display: none;
}
a {
text-decoration: none;
color: #000000;
}
.login-button a {
display: block;
}
.login-input input.list-input {
float: left;
line-height: 35px;
height: 35px;
width: 350px;
border: #ebebeb 1px solid;
text-indent: 5px;
}
.login-input {
overflow: hidden;
margin: 0px 0px 20px 0px;
}
.login-input label {
float: left;
width: 90px;
padding-right: 10px;
text-align: right;
line-height: 35px;
height: 35px;
font-size: 14px;
}
.login-title span {
position: absolute;
font-size: 12px;
right: -20px;
top: -30px;
background: #ffffff;
border: #ebebeb solid 1px;
width: 40px;
height: 40px;
border-radius: 20px;
}
</style>
</head>
<body>
<div class="login-header"><a id="link" href="javascript:void(0);">点击,弹出登录框</a></div>
<div id="login" class="login" >
<div id="title" class="login-title">登录会员
<span><a id="closeBtn" href="javascript:void(0);" class="close-login">关闭</a></span>
</div>
<div class="login-input-content">
<div class="login-input">
<label>用户名:</label>
<input type="text" placeholder="请输入用户名" name="info[username]" id="username" class="list-input">
</div>
<div class="login-input">
<label>登录密码:</label>
<input type="password" placeholder="请输入登录密码" name="info[password]" id="password" class="list-input">
</div>
</div>
<div id="loginBtn" class="login-button"><a href="javascript:void(0);" id="login-button-submit">登录会员</a></div>
</div>
<!-- 遮盖层 -->
<div id="bg" class="login-bg" ></div>
<script>
// 获取元素
var link = document.getElementById("link");
var login = document.getElementById("login");
var bg = document.getElementById("bg");
var closeBtn = document.getElementById("closeBtn");
// 添加 link 的点击事件,让登录窗口和遮盖层显示
link.onclick = function () {
login.style.display = "block";
bg.style.display = "block";
};
// 添加 btn 的点击事件,让登录窗口和遮盖层隐藏
closeBtn.onclick = function () {
login.style.display = "none";
bg.style.display = "none";
};
</script>
</body>