# js 基础知识
# JavaScript:事件对象 Event 和冒泡
# 绑定事件的两种方式
<body>
<button>点我</button>
<script>
var btn = document.getElementsByTagName("button")[0];
//这种事件绑定的方法容易被层叠。
btn.onclick = function() {
console.log("事件1");
};
btn.onclick = function() {
console.log("事件2");
};
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
【这种绑定事件的方式,会层叠掉之前的事件。】点击按钮后,上方代码的打印结果:
事件2;
addEventListener()里的参数:- 参数 1:事件名(注意,没有 on)
- 参数 2:事件名(执行函数)
- 参数 3:事件名(捕获或者冒泡)
<body>
<button>按钮</button>
<script>
var btn = document.getElementsByTagName("button")[0];
//addEventListener: 事件监听器。 原事件被执行的时候,后面绑定的事件照样被执行
//第二种事件绑定的方法不会出现层叠。(更适合团队开发)
btn.addEventListener("click", fn1);
btn.addEventListener("click", fn2);
function fn1() {
console.log("事件1");
}
function fn2() {
console.log("事件2");
}
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
【这种绑定事件的方式,不会层叠掉之前的事件】点击按钮后,上方代码的打印结果:
事件1;
事件2;
2
# 事件对象
在触发 DOM 上的某个事件时,会产生一个事件对象 event,这个对象中包含着所有与事件有关的信息。比如鼠标操作时候,会添加鼠标位置的相关信息到事件对象中。
所有浏览器都支持 event 对象,但支持的方式不同
# ie 678 支持 window.event
于是,我们可以采取一种兼容性的写法。如下:
event = event || window.event; ////兼容性写法
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script>
//点击页面的任何部分
document.onclick = function (event) {
event = event || window.event; ////兼容性写法
console.log(event);
console.log(event.timeStamp);//事件生成的日期和事件
console.log(event.bubbles);//是否是起泡事件类型。返回布尔值
console.log(event.button);//返回事件出发时,哪个鼠标按钮被点击
console.log(event.pageX);//光标相对于网页的水平位置(ie无)
console.log(event.pageY);//光标相对于网页的垂直位置(ie无)
console.log(event.screenX);//光标相对于网页的水平位置(当前可视区域)
console.log(event.screenY);//光标相对于网页的垂直位置(当前可视区域)
console.log(event.target);//该事件被传送到的对象
console.log(event.type);// 事件类型
console.log(event.clientX);//光标相对于显示器的水平位置
console.log(event.clientY);//光标相对于显示器的垂直位置
}
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# event 属性
event 有很多属性
| 属性 | 作用 |
|---|---|
| timeStamp | 返回事件生产的时间 |
| bubbles | 返回布尔值,指示事件是否为可冒泡事件 |
| button | 返回事件触发时,哪个按钮被点击 |
| pageX | 光标相对于网页的水平位置(ie 无) |
| pageY | 光标相对于网页的垂直位置(ie 无) |
| clientX | 光标相对于网页的水平位置(当前可视区域) |
| clientY | 光标相对于网页的垂直位置(当前可视区域) |
| screenX | 光标相对于显示器的水平位置 |
| screenY | 光标相对于显示器的垂直位置 |
| target | 该事件被传送到的对象 |
| type | 事件类型 |
# event 举例
# 鼠标跟随
- 由于 pageX 和 pageY 的兼容性不好,我们可以这样做:
- 鼠标在页面的位置 = 被卷去的部分+可视区域部分。
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title></title>
<style>
body {
height: 5000px;
}
img {
position: absolute;
padding: 10px 0;
border: 1px solid #ccc;
cursor: pointer;
background-color: yellowgreen;
}
</style>
</head>
<body>
<img src="" width="100" height="100" />
<script>
//需求:点击页面的任何地方,图片跟随鼠标移动到点击位置。
//思路:获取鼠标在页面中的位置,然图片缓慢运动到鼠标点击的位置。
// 兼容ie67做pageY和pageX;
// 原理: 鼠标在页面的位置 = 被卷去的部分+可视区域部分。
//步骤:
//1.老三步。
//2.获取鼠标在页面中的位置。
//3.利用缓动原理,慢慢的运动到指定位置。(包括左右和上下)
//1.老三步。
var img = document.getElementsByTagName("img")[0];
var timer = null;
var targetx = 0;
var targety = 0;
var leaderx = 0;
var leadery = 0;
//给整个文档绑定点击事件获取鼠标的位置。
document.onclick = function(event) {
//新五步
//兼容获取事件对象
event = event || window.event;
//鼠标在页面的位置 = 被卷去的部分+可视区域部分。
var pagey = event.pageY || scroll().top + event.clientY;
var pagex = event.pageX || scroll().left + event.clientX;
targety = pagey - 30;
targetx = pagex - 50;
//要用定时器,先清定时器
clearInterval(timer);
timer = setInterval(function() {
//为盒子的位置获取值
leaderx = img.offsetLeft;
//获取步长
var stepx = (targetx - leaderx) / 10;
//二次处理步长
stepx = stepx > 0 ? Math.ceil(stepx) : Math.floor(stepx);
leaderx = leaderx + stepx;
//赋值
img.style.left = leaderx + "px";
//为盒子的位置获取值
leadery = img.offsetTop;
//获取步长
var stepy = (targety - leadery) / 10;
//二次处理步长
stepy = stepy > 0 ? Math.ceil(stepy) : Math.floor(stepy);
leadery = leadery + stepy;
//赋值
img.style.top = leadery + "px";
//清定时器
if (
Math.abs(targety - img.offsetTop) <= Math.abs(stepy) &&
Math.abs(targetx - img.offsetLeft) <= Math.abs(stepx)
) {
img.style.top = targety + "px";
img.style.left = targetx + "px";
clearInterval(timer);
}
}, 30);
};
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# 获取鼠标距离所在盒子的距离
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title></title>
<style>
.box {
width: 300px;
height: 200px;
padding-top: 100px;
background-color: pink;
margin: 100px;
text-align: center;
font: 18px/30px "simsun";
cursor: pointer;
}
</style>
</head>
<body>
<div class="box"></div>
<script src="animate.js"></script>
<script>
//需求:鼠标进入盒子之后只要移动,哪怕1像素,随时显示鼠标在盒子中的坐标。
//技术点:新事件,onmousemove:在事件源上,哪怕鼠标移动1像素也会触动这个事件。
//一定程度上,模拟了定时器
//步骤:
//1.老三步和新五步
//2.获取鼠标在整个页面的位置
//3.获取盒子在整个页面的位置
//4.用鼠标的位置减去盒子的位置赋值给盒子的内容。
//1.老三步和新五步
var div = document.getElementsByTagName("div")[0];
div.onmousemove = function(event) {
event = event || window.event;
//2.获取鼠标在整个页面的位置
var pagex = event.pageX || scroll().left + event.clientX;
var pagey = event.pageY || scroll().top + event.clientY;
//3.获取盒子在整个页面的位置
// var xx =
// var yy =
//4.用鼠标的位置减去盒子的位置赋值给盒子的内容。
var targetx = pagex - div.offsetLeft;
var targety = pagey - div.offsetTop;
this.innerHTML =
"鼠标在盒子中的X坐标为:" +
targetx +
"px;<br>鼠标在盒子中的Y坐标为:" +
targety +
"px;";
};
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 商品放大镜
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title></title>
<style>
* {
margin: 0;
padding: 0;
}
.box {
width: 350px;
height: 350px;
margin: 100px;
border: 1px solid #ccc;
position: relative;
}
.big {
width: 400px;
height: 400px;
position: absolute;
top: 0;
left: 360px;
border: 1px solid #ccc;
overflow: hidden;
display: none;
}
/*mask的中文是:遮罩*/
.mask {
width: 175px;
height: 175px;
background: rgba(255, 255, 0, 0.4);
position: absolute;
top: 0;
left: 0;
cursor: move;
display: none;
}
.small {
position: relative;
}
img {
vertical-align: top;
}
</style>
<script src="tools.js"></script>
<script>
window.onload = function() {
//需求:鼠标放到小盒子上,让大盒子里面的图片和我们同步等比例移动。
//技术点:onmouseenter==onmouseover 第一个不冒泡
//技术点:onmouseleave==onmouseout 第一个不冒泡
//步骤:
//1.鼠标放上去显示盒子,移开隐藏盒子。
//2.老三步和新五步(黄盒子跟随移动)
//3.右侧的大图片,等比例移动。
//0.获取相关元素
var box = document.getElementsByClassName("box")[0];
var small = box.firstElementChild || box.firstChild;
var big = box.children[1];
var mask = small.children[1];
var bigImg = big.children[0];
//1.鼠标放上去显示盒子,移开隐藏盒子。(为小盒子绑定事件)
small.onmouseenter = function() {
//封装好方法调用:显示元素
show(mask);
show(big);
};
small.onmouseleave = function() {
//封装好方法调用:隐藏元素
hide(mask);
hide(big);
};
//2.老三步和新五步(黄盒子跟随移动)
//绑定的事件是onmousemove,而事件源是small(只要在小盒子上移动1像素,黄盒子也要跟随)
small.onmousemove = function(event) {
//新五步
event = event || window.event;
//想要移动黄盒子,必须要知道鼠标在small小图中的位置。
var pagex = event.pageX || scroll().left + event.clientX;
var pagey = event.pageY || scroll().top + event.clientY;
//x:mask的left值,y:mask的top值。
var x = pagex - box.offsetLeft - mask.offsetWidth / 2; //除以2,可以保证鼠标mask的中间
var y = pagey - box.offsetTop - mask.offsetHeight / 2;
//限制换盒子的范围
//left取值为大于0,小盒子的宽-mask的宽。
if (x < 0) {
x = 0;
}
if (x > small.offsetWidth - mask.offsetWidth) {
x = small.offsetWidth - mask.offsetWidth;
}
//top同理。
if (y < 0) {
y = 0;
}
if (y > small.offsetHeight - mask.offsetHeight) {
y = small.offsetHeight - mask.offsetHeight;
}
//移动黄盒子
console.log(small.offsetHeight);
mask.style.left = x + "px";
mask.style.top = y + "px";
//3.右侧的大图片,等比例移动。
//如何移动大图片?等比例移动。
// 大图片/大盒子 = 小图片/mask盒子
// 大图片走的距离/mask走的距离 = (大图片-大盒子)/(小图片-黄盒子)
// var bili = (bigImg.offsetWidth-big.offsetWidth)/(small.offsetWidth-mask.offsetWidth);
//大图片走的距离/mask盒子都的距离 = 大图片/小图片
var bili = bigImg.offsetWidth / small.offsetWidth;
var xx = bili * x; //知道比例,就可以移动大图片了
var yy = bili * y;
bigImg.style.marginTop = -yy + "px";
bigImg.style.marginLeft = -xx + "px";
};
};
</script>
</head>
<body>
<div class="box">
<div class="small">
<img src="images/001.jpg" alt="" />
<div class="mask"></div>
</div>
<div class="big">
<img src="images/0001.jpg" alt="" />
</div>
</div>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/**
* Created by smyhvae on 2018/02/03.tool.js
*/
//显示和隐藏
function show(ele) {
ele.style.display = "block";
}
function hide(ele) {
ele.style.display = "none";
}
function scroll() {
// 开始封装自己的scrollTop
if (window.pageYOffset != null) {
// ie9+ 高版本浏览器
// 因为 window.pageYOffset 默认的是 0 所以这里需要判断
return {
left: window.pageXOffset,
top: window.pageYOffset,
};
} else if (document.compatMode === "CSS1Compat") {
// 标准浏览器 来判断有没有声明DTD
return {
left: document.documentElement.scrollLeft,
top: document.documentElement.scrollTop,
};
}
return {
// 未声明 DTD
left: document.body.scrollLeft,
top: document.body.scrollTop,
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# onclick 事件冒泡问题
事件传播的三个阶段是:事件捕获、事件冒泡和目标。
事件捕获阶段:事件从最上一级标签开始往下查找,直到捕获到事件目标 target。(从祖先元素往子元素查找,DOM树结构)。在这个过程中,事件相应的监听函数是不会被触发的。事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。事件冒泡阶段:事件从事件目标 target 开始,往上冒泡直到页面的最上一级标签。(从子元素到祖先元素冒泡)
不是所有的事件都能冒泡
以下事件不冒泡:blur、focus、load、unload、onmouseenter、onmouseleave。意思是,事件不会往父元素那里传递。
# 事件冒泡
当一个元素上的事件被触发的时候(比如说鼠标点击了一个按钮),同样的事件将会在那个元素的所有祖先元素中被触发。这一过程被称为事件冒泡;这个事件从原始元素开始一直冒泡到 DOM 树的最上层。
通俗来讲,冒泡指的是:子元素的事件被触发时,父盒子的同样的事件也会被触发。取消冒泡就是取消这种机制。
# 事件捕获
addEventListener 可以捕获事件:(捕获的顺寻跟冒泡顺序相反)【捕获:从祖先元素往子元素查找,DOM树结构】
box1.addEventListener(
"click",
function() {
alert("捕获 box3");
},
true
);
2
3
4
5
6
7
上面的方法中,参数为 true,代表捕获;参数为 false 或者不写参数,代表冒泡。
我们检查一个元素是否会冒泡,可以通过事件的以下参数:
event.bubbles;
如果返回值为 true,说明该事件会冒泡;反之则相反。
# 阻止冒泡的方法
w3c 的方法:(火狐、谷歌、IE11)
event.stopPropagation();
IE10 以下则是:
event.cancelBubble = true;
兼容代码如下:
box3.onclick = function(event) {
alert("child");
//阻止冒泡
event = event || window.event;
if (event && event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
};
2
3
4
5
6
7
8
9
10
11
使用 jQuery 阻止事件冒泡和默认行为与原生 JS 一样。不太清楚的可以查看我的《DOM 之事件(一)》。
$('div').on('click',function(){
//some code
e.stopPropagation();
});
//阻止事件冒泡
$('a').on('click',function(){
//some code
return false;//简单方式
//e.preventDefault();//W3C标准方式
});
//阻止默认行为
2
3
4
5
6
7
8
9
10
11
# React如果无法阻止可尝试
e.nativeEvent.stopImmediatePropagation();
# 浏览器中的事件
# 事件委托
事件委托,通俗地来讲,就是把一个元素响应事件(click、keydown......)的函数委托到另一个元素。
比如说有一个列表 ul,列表之中有大量的列表项 li,我们需要在点击列表项 li 的时候响应一个事件。如果给每个列表项一一都绑定一个函数,那对于内存消耗是非常大的,效率上需要消耗很多性能。
因此,比较好的方法就是把这个点击事件绑定到他的父层,也就是 ul 上,然后在执行事件的时候再去匹配判断目标元素。
所以事件委托可以减少大量的内存消耗,节约效率。
# 指针事件
# onmouseenter,onmouseleave等
onmousemove事件在鼠标移动到 div 元素上时触发。mouseleave事件只在鼠标指针移出 div 元素时触发。onmouseout事件在鼠标指针移出 div 元素及离开子元素(p 和 span)时触发。
<p> onmousemove 事件在鼠标移动到 div 元素上时触发。</p>
<p> onmouseleave 事件只在鼠标指针移出 div 元素时触发。 </p>
<p> onmouseout 事件在鼠标指针移出 div 元素及离开子元素(p 和 span)时触发。</p>
<div onmousemove="myMoveFunction()">
<p>onmousemove: <br> <span id="demo">鼠标移入和移出!</span></p>
</div>
<div onmouseleave="myLeaveFunction()">
<p>onmouseleave: <br> <span id="demo2">鼠标移入和移出!</span></p>
</div>
<div onmouseout="myOutFunction()">
<p>onmouseout: <br> <span id="demo3">鼠标移入和移出!</span></p>
</div>
2
3
4
5
6
7
8
9
10
11
12
onmousemove事件在鼠标移动到 div 元素上时触发。onmouseenter事件中有在鼠标指针进入 div 元素时触发。onmouseover事件在鼠标指针进入 div 元素时触发,在子元素上也会触发(p 和 span)。
<p> onmousemove 事件在鼠标移动到 div 元素上时触发。</p>
<p> onmouseenter 事件中有在鼠标指针进入 div 元素时触发。 </p>
<p> onmouseover 事件在鼠标指针进入 div 元素时触发,在子元素上也会触发(p 和 span)。</p>
<div onmousemove="myMoveFunction()">
<p>onmousemove: <br> <span id="demo">鼠标移动到我这!</span></p>
</div>
<div onmouseenter="myEnterFunction()">
<p>onmouseenter: <br> <span id="demo2">鼠标移动到我这!</span></p>
</div>
<div onmouseover="myOverFunction()">
<p>onmouseover: <br> <span id="demo3">鼠标移动到我这!</span></p>
</div>
2
3
4
5
6
7
8
9
10
11
12
配对
mouseenter与onmouseleave是一对onmouseover与onmouseout是一对
# video_audio事件
在视频/音频(audio/video)加载过程中,事件的触发顺序如下:
onloadstart(浏览器开始寻找指定资源)ondurationchange(视频/音频 的时长发生变化时触发)onloadedmetadata(指定视频/音频 的元数据加载后触发)onloadeddata(当前帧的数据加载完成且还没有足够的数据播放)onprogress(下载指定的视频/音频 时触发)oncanplay(用户可以开始播放视频/音频 时触发)oncanplaythrough(可以正常播放且无需停顿和缓冲时触发)