事件概念
事件冒泡
事件冒泡:事件的向上传递,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发,在开发中大部分情况,冒泡都是有用的,如果不希望发生事件冒泡,可以通过事件对象来取消冒泡。[运行后,点击span,box1和body的点击事件都会被触发,称为事件冒泡]。
event.cancelBubble=true;
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>显示鼠标坐标</title>
<style type="text/css">
#box1{
width: 100px;
height: 100px;
background-color:greenyellow;
}
#s1{
background-color: crimson;
}
</style>
</head>
<body>
<div id="box1">
<span id="s1">我是span</span>
</div>
<script type="text/javascript">
/*事件冒泡:事件的向上传递,当后代元素上的事件被触发时
其祖先元素的相同事件也会被触发
在开发中大部分情况,冒泡都是有用的,如果不希望发生事件冒泡
可以通过事件对象来取消冒泡*/
//为s1绑定一个单击响应函数
var s1=document.getElementById("s1");
s1.onclick=function(){
alert("span");
//取消冒泡
event.cancelBubble=true;
};
//为box1绑定一个单击响应函数
var box1=document.getElementById("box1");
box1.onclick=function(){
alert("box1");
};
//为body绑定单击响应函数
document.body.onclick=function(){
alert("body");
};
</script>
</body>
</html>
事件的委派
给超链接委派事件:给每一个超链接添加一个单击响应函数,但是如果我们后来再新增超链接,这些新增的超链接是没有绑定单击响应函数的。
</head>
<body>
<ul id="u1">
<li><a href="javascript:;">超链接一</a></li>
<li><a href="javascript:;">超链接二</a></li>
<li><a href="javascript:;">超链接三</a></li>
</ul>
<button id="btn1">添加超链接</button>
<script type="text/javascript">
var ul=document.getElementById("u1");
/*点击按钮添加超链接*/
var btn1=document.getElementById("btn1");
btn1.onclick=function(){
var li=document.createElement("li");
li.innerHTML="<a href='javascript:;'>新建超链接</a>";
ul.appendChild(li);
};
/*为每个超链接绑定一个单击响应函数,
为每一个超链接都绑定了一个单击响应函数,操作麻烦
且只能为已有的超链接设置事件,而新添加的超链接必须重新设置*/
var allA=document.getElementsByTagName("a");
for(var i=0;i<allA.length;++i)
{
allA[i].onclick=function(){
alert("aaa");
};
}
</script>
</body>
</html>
结果:新增的超链接没有绑定单击响应函数,且我们需要为每一个超链接都绑定一次单击响应事件,太麻烦了。
改进:我们希望,只绑定一次事件,就可以应用到多个元素上,即使该元素是后来添加的
尝试:将其绑定给元素共同的祖先元素
//为a的祖先ul绑定一个单击响应函数
ul.onclick=function(){
alert("1111");
};
结果:只绑定一次,结果无论是原来的还是后来新增的都有了单击响应函数。
a标签的祖先li标签,li标签的祖先ul被绑定了单击响应函数【这就是事件冒泡的作用体现】。
事件委派:将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件
事件委派是利用了冒泡,通过委派可以减少事件的绑定次数,提供程序的性能。
看起来似乎解决了问题,但其实,问题有点大。
加了个背景颜色后,无论是点击绿色背景的哪个地方,都会出现委派的单击事件函数。
解决:判断点击的地方是不是我们想要的,不是的话就不触发。
关键代码:
//为a的祖先ul绑定一个单击响应函数
ul.οnclick=function(event){
event=event||window.event;
//event.target 表示事件触发的对象
if(event.target.className==‘link’){
alert(“111”);
}
//如果触发事件是我们期望的标签,我们就触发
};
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>显示鼠标坐标</title>
</head>
<body>
<ul id="u1" style="background-color: #bfa;">
<li><a href="javascript:;" class="link">超链接一</a></li>
<li><a href="javascript:;" class="link">超链接二</a></li>
<li><a href="javascript:;" class="link">超链接三</a></li>
</ul>
<button id="btn1">添加超链接</button>
<script type="text/javascript">
var ul=document.getElementById("u1");
/*点击按钮添加超链接*/
var btn1=document.getElementById("btn1");
btn1.onclick=function(){
var li=document.createElement("li");
li.innerHTML="<a href='javascript:;' class='link'>新建超链接</a>";
ul.appendChild(li);
};
//为a的祖先ul绑定一个单击响应函数
ul.onclick=function(event){
event=event||window.event;
//event.target 表示事件触发的对象
if(event.target.className=='link'){
alert("111");
}
//如果触发事件是我们期望的标签,我们就触发
};
/*为每个超链接绑定一个单击响应函数,
为每一个超链接都绑定了一个单击响应函数,操作麻烦
且只能为已有的超链接设置事件,而新添加的超链接必须重新设置*/
/* var allA=document.getElementsByTagName("a");
for(var i=0;i<allA.length;++i)
{
allA[i].οnclick=function(){
alert("aaa");
};
}*/
</script>
</body>
</html>
事件的绑定
<script type="text/javascript">
var btn01=document.getElementById("btn01");
btn01,onclick=function(){
alert("11");
};
btn01.onclick=function(){
alert("2");
};
</script>
这样,在IE8以下,事件会被覆盖掉,后来的浏览器,都会先执行2,再执行1.
解决:addEventListener(),不支持ie8及以下的浏览器
/*addEventListener()
参数:
1.事件的字符串,不要on
2.回调函数,当事件触发时会被调用
3.是否在捕获阶段触发事件,需要一个布尔值,一般穿false
使用addEventListener()可以为同一个元素的相同事件同时绑定多个响应函数
这样当事件被触发时,响应函数将会按照函数的绑定顺序执行】
* 在IE8中不兼容,使用attachEvent()来绑定事件
* 参数:
* 1.事件的字符串,要on
* 2.回调函数
* 这个方法也可以同时为一个事件绑定多个事件,不同的是它是后绑定,先执行
* 执行顺序和addEventListener()相反*/
btn01.addEventListener("click",function(){
alert("111");
},false)
btn01.addEventListener("click",function(){
alert("222");
},false)
btn01.addEventListener("click",function(){
alert("333");
},false)
使用addEventListener()【IE8以上或者其他浏览器才可使用】和attachEvent()【IE8及以下才可以使用】来解决,兼容性问题怎么解决?
function bind(obj,eventStr,callback){
if(obj.addEventListener){
//大部分浏览器兼容的方式
obj.addEventListener(eventStr,callback,false);
}
else{
//IE8及以下
obj.attachEvent("on"+eventStr,function(){
callback(obj);//修改this对象,本来直接传个回调函数,调用者是浏览器
//这里我们使用匿名函数,我们在匿名函数中调用对应函数,那会调用权,
//这样,也可以把我们的this修改为自己的对象了
});
}
bind(btn01,"click",function(){
alert("111");
});
事件传播
微软公司认为事件应该由内而外传播的,也就是当事件触发时,应该先触发当前元素上的事件,然后再向当前祖先元素上传播,也就是说事件应该由里向外传【事件冒泡】;
网景公司任务事件应该是由外而内传播的,也就是先触发当前事件的祖先元素的事件,然后再向内传播给后代元素
最后W3C综合了两个公司的方案,将事件的传播分为三个阶段:
1.捕获阶段:在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,默认此时不会触发事件
2.目标阶段:事件捕获到目标元素,捕获结束后开始在目标元素上触发事件
3.冒泡阶段:事件从目标元素向祖先元素传递,依次触发祖先元素上的事件
如果希望我们在捕获阶段就触发事件,可以将addEventListener() 的第三个参数设置为true,一般情况下我们是设置为false的。
IE8一下的浏览器不会有捕获阶段。
事件应用
鼠标事件之拖拽
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>显示鼠标坐标</title>
<style type="text/css">
#box01{
width: 200px;
height: 200px;
background-color: yellowgreen;
position: absolute;
}
#box02{
width: 200px;
height: 200px;
background-color: skyblue;
position: absolute;
left: 500px;
top: 500px;
}
</style>
</head>
<body>
<div id="box01"></div>
<div id="box02"></div>
<script type="text/javascript">
/*
* 拖拽div:
1.当鼠标在被拖拽元素上被按下时开启拖拽 onmousedown
2.当鼠标移动时,被拖拽元素跟随鼠标移动 onmousemove
3.当鼠标松开时,被拖拽元素固定在当前位置 onmouseup
*/
var box01=document.getElementById("box01");
box01.onmousedown=function()
{
/*
当鼠标移动时被拖拽元素随鼠标移动 onmousemove
为document绑定一个onmousemove 事件
* */
event=event||window.event;
var ol = event.clientX -box01.offsetLeft;
var ot = event.clientY -box01.offsetTop;
document.onmousemove=function(event){
event=event||window.event;
var left=event.clientX;
var top=event.clientY;
box01.style.left=left+"px";
box01.style.top=top+"px";
};
};
//为元素绑定一个鼠标松开事件
document.onmouseup=function(){
//当鼠标松开时,被拖拽元素固定在当前位置
document.onmousemove=null;//鼠标一松开,这个事件就没有意义了
box01.onmouseup=null;//鼠标一松开,这个事件就没有意义了
};
</script>
</body>
</html>
有一个bug:一取消掉document.onmouseup,第二次就会跟着鼠标一直移动。。【修了一个下午也没有找出来原因。。】
这里有个用户体验不好,我们每次点击div,最终鼠标都是跟div的左上角重合,得改进一下,鼠标和div的相对位置保持不变。
改进代码:
var box01=document.getElementById("box01");
box01.onmousedown=function(event)
{
/*
当鼠标移动时被拖拽元素随鼠标移动 onmousemove
为document绑定一个onmousemove 事件
* */
event=event||window.event;
var ol = event.clientX -box01.offsetLeft;
var ot = event.clientY -box01.offsetTop;
document.onmousemove=function(event){
event=event||window.event;
var left=event.clientX-ol;
var top=event.clientY-ot;
box01.style.left=left+"px";
box01.style.top=top+"px";
};
};
鼠标事件之滚轮滚动
一个div,随着滚轮向上滚动,逐渐变大,随着滚轮向下滚动,逐渐减小。
火狐浏览器的一些方法和属性不与谷歌,ie浏览器兼容,需要另做处理。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>显示鼠标坐标</title>
<style type="text/css">
#box01{
width: 200px;
height: 200px;
background-color: yellowgreen;
position: absolute;
}
</style>
</head>
<body>
<div id="box01"></div>
<script type="text/javascript">
var box01=document.getElementById("box01");
/*鼠标滚轮滚动事件,会在滚轮滚动时触发
火狐不兼容,需要使用 DOMMouseScroll()函数来绑定
* */
box01.onmousewheel=function(event){
event=event||window.event;
if(event.wheelDelta>0 || event.detail<0)
{
box01.style.height=box01.clientHeight-10+"px";
}
else
{
box01.style.height=box01.clientHeight+10+"px";
}
event.preventDefault && event.preventDefault();//判断一下,如果存在了,就执行,不然在IE8里面会报错
//专门为火狐浏览器设置的,但是IE8不支持这个属性,如果直接调用会报错
//判断一下,有就用,没有就不用
/*
event.wheelDelta这个值不看大小,看正负【跟鼠标灵敏度有关】
但是火狐浏览器不支持event.wheelDelta
在火狐中,利用event.detail获取滚动方向
*/
return false;
/*当滚轮滚动时,如果浏览器有滚动条,滚动条会随着滚动
这是浏览器的默认行为,如果不希望发生,取消该行为
但是火狐浏览器通过函数是不能够使用return false的
所以加上一句 event.preventDefault();*/
};
//为火狐浏览器绑定滚轮事件
bind(box01,"DOMMouseScroll",box01.onmousewheel);
function bind(obj,eventStr,callback){
if(obj.addEventListener){
//大部分浏览器兼容的方式
obj.addEventListener(eventStr,callback,false);
}
else{
//IE8及以下
obj.attachEvent("on"+eventStr,function(){
callback(obj);//修改this对象,本来直接传个回调函数,调用者是浏览器
//这里我们使用匿名函数,我们在匿名函数中调用对应函数,那会调用权,
//这样,也可以把我们的this修改为自己的对象了
});
}
}
</script>
</body>
</html>
键盘事件
每个按键一按下去,都会有一个keycode编码会被接收到,js就是根据这个东西来判断是按下了哪个按键的。制作小游戏的关键。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>显示鼠标坐标</title>
<style type="text/css">
#box01{
width: 100px;
height: 100px;
background-color: skyblue;
}
</style>
</head>
<body>
<input type="text" />
<script type="text/javascript">
/*键盘事件:
onkeydown:按键被按下
如果一直按着某个按键不松手,则事件会一直触发
当onkeydown连续触发时,第一次和第二次时间会比较慢,其他的会非常快
这种设计是为了防止误操作
onkeyup:按键被松开
键盘事件我们一般绑定给一些可以获取到焦点的对象或者是document
*/
document.onkeydown=function(event){
event=event||window.event;
/*可以通过keycode获得按键event.keyCode
除了keycode,还提供了alt ctrl shift是否被按下
如果被按下,返回true。
*/
};
document.onkeyup=function(){
}
var input=document.getElementsByTagName("input")[0];
input.onkeydown=function(event){
event= event||window.event;
console.log("111");
if(event.keyCode>=48 && event .keyCode<=57)
//在文本框中输入文本属于onkeydown的默认行为,
//这里可以取消默认行为
return false;//即文本框不能输入数字
//这里只针对笔记本电脑,如果还有小键盘,那还要继续增加条件
}
</script>
</body>
</html>
键盘的上下左右移动:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>显示鼠标坐标</title>
<style type="text/css">
#box01{
width: 100px;
height:100px;
background-color: skyblue;
position: absolute;
/*要绝对定位才能移动*/
}
</style>
</head>
<body>
<div id="box01"></div>
<script type="text/javascript">
//为document 绑定一个事件
var box01=document.getElementById("box01");
document.onkeydown=function(event){
event=event||window.event;
/*37左
38 上
39右
40下*/
var speed=10;
if(event.ctrlKey)//用户按了ctrl,加快速度
speed=50;
switch(event.keyCode){
case 37:
// alert("左");
box01.style.left=box01.offsetLeft-speed+"px";break;
case 38:
// alert("上");
box01.style.top=box01.offsetTop-speed+"px";break;
case 39:
// alert("右");
box01.style.left=box01.offsetLeft+speed+"px";break;
case 40:
// alert("下");
box01.style.top=box01.offsetTop+speed+"px";break;
}
};
</script>
</body>
</html>