JavaScript 事件分类
一、鼠标事件
click、mousedown、mousemove、mouseup、contextmenu、mouseover、mouseout、mouseenter、mouseleave
用e.button来区分鼠标的按键,0/1/2
DOM3标准规定: click事件只能监听左键, 只能通过mousedown 和 mouseup来判断鼠标键
如何解决mousedown和click的冲突
1、click是敲击鼠标事件
click = mousedown + mouseup 是一个过程,
看一下click、mousedown、mouseup这三个事件的触发顺序
document.onclick = function(){ console.log('click'); } document.onmousedown = function(){ console.log('mousedown'); } document.onmouseup = function(){ console.log('mouseup'); } // mousedown、mouseup这两个是一组
顺序是先mousedown,后mouseup,再click,跟绑定顺序没关系
2、其他鼠标事件
contextmenu 右键弹出菜单事件,它唯一有用处的地方就是右键取消菜单,它监听右键不好吗,监听右键另外有方法不用这种麻烦的方法
mousemove 鼠标移动的事件
mouseover 鼠标挪入
mouseout 鼠标挪出
mouseenter 鼠标挪入(HTML5新规范)
mouseleave 鼠标挪出(HTML5新规范)
3、鼠标挪入\鼠标挪出
mouseover事件 鼠标移入的时候发生
mouseout事件 鼠标离开的时候发生
<div style="width:100px;height:100px;background-color:yellow;"></div> <script> var div = document.getElementsByTagName('div')[0]; div.onmouseover = function(){ div.style.backgroundColor = "tomato"; } div.onmouseout = function(){ div.style.backgroundColor = "yellow"; } </script>
HTML5新的规范是mouseenter、mouseleave,css的内部原理hover就是用js写的
<div style="width:100px;height:100px;background-color:yellow;"></div> <script> var div = document.getElementsByTagName('div')[0]; div.onmouseenter = function(){ div.style.backgroundColor = "tomato"; } div.onmouseleave = function(){ div.style.backgroundColor = "yellow"; } </script>
4、总结
click 鼠标点击
mousedown 鼠标点击按下
mouseup 鼠标抬起
mousemove 鼠标移动
contextmenu 鼠标左键
mouseover 鼠标移入(老版本)
mouseout 鼠标移出(老版本)
mouseenter 鼠标移入(HTML5新版本)
mouseleave 鼠标一出(HTML5新版本)
5、用事件对象里的button属性来区分鼠标的按键
有些需求就想左键出来什么东西、右键出来什么东西,
比如扫雷游戏左键打开、右键插旗,必须知道什么时候左键、什么时候右键
如何区分鼠标的左右按键?
能区分左右键的只有两个事件,一个是mouseup一个是mousedow,其它的事件都不可能
先试一下 onmousedown 左键点击按下
document.onmousedown = function(e){ console.log(e); }
左键点击一下,
1). 控制台出来事件对象,事件对象上有一个button属性,
2). 这个button属性记载了鼠标是右键的还是左键,
3). 如果左建返回 0
如果右键返回 2
如果是中间滚动轮返回 1
右键点击botton是2
所以判断e.button的值就知道左键还是右键了
document.onmousedown = function(e){ if(e.button == 2){ console.log('右键点击'); }else if(e.button == 0){ console.log('左键点击'); } }
mousedown和mouseup是一组的
1). mousedown 鼠标按下能判断左键右键
2). mouseup 鼠标抬起也能这样判断
6、click事件不能的监听左键的
click对右键的监听基本上是无能为力的状态,右键没触发click事件
document.onclick = function(e){ console.log(e); }
click点击右键,在控制台没有显示任何内容,只在页面弹出右键菜单
w3c规定click事件只能监听左键不能监听右键,能触发右键的只有mousedow和mouseup
7、如何解决mousedown和click的冲突
问一个问题,
拖拽一个小方块,这个小方块还是一个A标签,
A标签有个特性,在他上面写网址一点击就跳转页面了,
现在把A标签做成可以拖拽的方块
要求
拖拽的时候让方块A标签正常拖拽,
点击的时候方块A标签正常跳转
实现的关键点在哪?
拖拽是一个mousedown事件、一个mousemove事件、一个mouseup事件,
但一个mousedown、一个mouseup不管隔多长时间都算一个click事情,
就是拖拽不能跳转页面,怎么来区分拖拽和点击呢?
用户想拖拽的时候就拖,不执行点击事件,如果想执行点击的时候,不执行拖拽,简单的说就是拖拽不等于点击怎么办?
时间差的问题,
想一个问题,正常拖拽一个东西需要一点时长,不能点完之后蹭一下就起来了,没那么快至少要0.2秒以上才叫拖拽,
click点击没有那么慢,一般情况下点击是清脆的,点完一下之后基本0.2秒到0.3以内秒就抬起来了,所以可以用生物行为来区分是拖拽还是点击。
鼠标按下、抬起的时间差
1). 小于300毫秒(0.3秒)是点击
2). 大于300毫秒是拖拽
<div style="width:100px;height:100px;background-color:red;position:absolute;left:0;top:0"></div> <script> var div = document.getElementsByTagName('div')[0], firstTime = 0, lastTime = 0, key = false; div.onmousedown = function() { firstTime = new Date().getTime(); // 1. onmousedown的时候记载一个时间戳 } document.onmouseup = function(){ lastTime = new Date().getTime(); if(lastTime - firstTime < 300){ // 2. 小于300毫秒是点击 key = true; // 3. 小于300毫秒,开关打开key等于true } } document.onclick = function(){ if(key){ // 4. 小于300是点击,让click点击执行了,里面有一个开关key console.log('小于300毫秒是click点击事件'); key = false; // 5. 小于300进入判断,让key等于false } } </script>
普通点击鼠标,打印出小于300毫秒是click点击事件,按住鼠标左键不释放,再抬起来没有click事件,
这就区分开左右键了,然后把拖拽功能放到mousedown、mouseup里面。
mousedown触发了拖拽事件,mousedown事件之后在它里面写mousemove,也就是按下之后绑定mousemove事件,方块才跟着鼠标移动,鼠标抬起来之后把mousemove事件解除
<div style="width:100px;height:100px;background-color:red;position:absolute;left:0;top:0"></div> <script> var div = document.getElementsByTagName('div')[0], disX, disY, firstTime = 0, lastTime = 0, key = false; div.onmousedown = function(e) { var e = e || window.event; firstTime = new Date().getTime(); disX = e.pageX - parseInt(div.style.left); disY = e.pageY - parseInt(div.style.top); document.onmousemove = function(e){ // 按下之后绑定mousemove事件 var e = e || window.event; div.style.top = e.clientY - disX + 'px'; div.style.left = e.clientX - disY + 'px'; } } document.onmouseup = function(){ lastTime = new Date().getTime(); if(lastTime - firstTime < 300){ key = true; } document.onmousemove = null; // 抬起来之后mousemove事件就解除 } document.onclick = function(){ if(key){ console.log('小于300毫秒是点击'); key = false; } } </script>
8、事件练习作业
拖拽应用(已做)
应用 mousedown、mousemove、mouseup(已经做)
随机移动的方块
mouseover
随机移动的方块 :
一个方块在屏幕中间,当鼠标放到方块上面的时候就是mouseover事件的时候,方块随机向上、下、左、右、斜上、斜下,斜左、斜右八个方向随机挪动100像素的距离。
造成一个效果就是无论鼠标怎么往方块里面移都碰不到方块,鼠标移到方块上面方块就随机移动了,鼠标碰不到方块。
二、键盘事件
keydown、keyup、keypress
keydown > keypress > keyup
keydown和keypress的区别
keydown 可以响应任意键盘按键,keypress只可以相应字符类键盘按键
keypress返回ASCII码,可以转换成相应字符
还有几个移动端的事件目前比较超纲,移动端mousedown就不好使了,
移动端叫touch事件有三个touchstart、touchmove、touchend,
移动端的touchstart、touchmove、touchend和mousedown、mouseup是一样的。
1、keydown、keyup、keypress
键盘事件很关键,比如贪吃蛇游戏就需要键盘事件,键盘事件就三个keydown、keyup、keypress
依照鼠标的规律来看一个click相当于mousedown + mouseup,键盘这里大概是一个keypress等于keydown + keyup,猜一下是不是这样?
document.onkeypress = function(){ console.log('keypress'); } document.onkeydown = function(){ console.log('keydown'); } document.onkeyup = function(){ console.log('keyup'); }
随便点一个按键a测一下,一个keypress不等于keydown + keyup
而且按住键盘不动,没有keyup反复出现keydown、keypress
然后键盘抬起keyup才出来
keypress跟keydown、keyup两个没关系
2、keydown > keypress > keyup
首先keydown和keyup是一对,触发顺序是先keydown后keypress再keyup,其实keydown和keypress差不多,但是有点小区别。
连续按的时候不抬起,就一直触发keydown和keypress这样有什么好处呢,为什么鼠标没连续触发,键盘连续触发呢?
比如CS游戏,"w、s、a、d"四个键,控制上、下、左、右四个方向跑,如果游戏设定按一下键盘,只能触发一下不连续触发,要想连续跑,手要点抽筋了,
所以游戏设计的很合理,按住之后连续触发,能保证触发事件的时候有一个连贯性,游戏也应用了这一点。
不仅是js,所有语言都有键盘事件一样的,也是按住连续一直触发。
问大家一个问题,假如让我们设计一个游戏,先不用管keydown和keypress的区别,有个游戏叫《英雄联盟》非常简单就几个键q、w、e、r、f,
有的人为了打游戏拼反应速度,买专门的机器键盘,机器键盘的好处是键盘里面是机器轴承,抬起的速度特别快,按键的回馈特别快,按下去非常难按,按完之后就弹起来了速度非常快,
如果我们设计游戏,把游戏的事件设置在keydown/keypress上还是keyup上?肯定是绑定在keydown上,按下技能就释放不用抬起来,普通键盘就能玩的很好,只要不影响按下去的速度就行。
3、keydown和keypress的区别
keydown、keypress它俩到底有什么用?
专门对比keydown、keypress,分别打印它们的事件对象e
document.onkeypress = function(e){ console.log(e); } document.onkeydown = function(e){ console.log(e); }
触发顺序是先触发keydown按下键盘a键
第一个事件对象是keydown,点开看里面的charCode属性值是0
keypress事件对象里面的charCode返回的是97,小写a的ACSII码就是97
第一区别是charCode属
keydown事件charCode属性 没有值
keypress事件harCode属性 有值
charCode属性有什么用呢?
再按方向键上,发现少了一个事件,再按下也少了个事件,少的是keydown还是keypress?
点开第一个keydown的事件对象,charCode属性值依然是0,事件类型是type: keydown
也就是说上、下、左、右这些方向键keypress没有触发,还有一些键Ctrl、CapsLock、Alt、Shift这些控制类的键都不触发keypress
解释一下keypress和keydown是干什么的?
1). keydown能够监测到所有的键盘类按键事件(除fn以外)
2). keypress只能监测到字符类按键
字符类按键什么意思?
ACSII码表里面找,ACSII表里面没有的都不叫字符按键,所以keypress按出来的会对应ACSII码
keydown能做这么多事,所有按键都能监听,那为什么不用keydown?
它有一个小问题,比如按一下小写的a键,keydown和keypress都出现了,keydown知道按键按的是什么吗?
点开keydown的事件对象,charCode的值是0,往下看which的值是65
按一下小写的b键,keydowan事件对象里 which: 66
有个which属性一个是65一个是66,这不能判断是a吗?判断不了
看好按"shift + a",也就是大写的A
"shift + a"的keydown事件对象,which属性的值结果也是 65
我们直接把which属性打印出来
document.onkeypress = function(e){ console.log('keypress:' + ' charCode: ' + e.charCode); console.log('keypress:' + ' which: ' + e.which); } document.onkeydown = function(e){ console.log('keydown: ' + ' charCode: ' + e.charCode); console.log('keydown: ' + ' which: ' + e.which); console.log(' '); }
按一下小写的a键
keydown:charCode值是0,which的值还是65
keypress: cherCode值97,which的值是97
说明keydown检测字符类按键是不准的,大写A小写a都是which:65怎么检测,但字符类按键keypress监控的很准
这两个怎么配合呢?
如果想监控字符类按键并且区分大小写用keypress,如果操作类按键用只能用keydown,
操作类按键keydown也没什么不好,比如上、下、左、右对应的位置which属性都有值而且是唯一的。
document.onkeydown = function(e){ console.log(e.which); }
上 which:38
下 which:40
左 which:37
右 which:39
这个which属性到底是什么?
which不是ASCII码,他检测的是108个键的排位号,他的数字对应一个键,但对应不了shift+字母键(大写的字母),就看要求精准不精准。
如果键盘类事件不管大小写要求没那么高,只要检测到那个按下就行了,那keydown解决一切问题。
但是keydown解决的问题的不好,字母和数字这些键对应的位置不是按ACSII码表来的,要挨个对应挨个测,keypress直接是按照ACSII表直接能把键拿出来了看的更精准一些。
e.charCode是ACSII码,如何把ACSII码转换成字母?
String.fromCharCode() 方法直接往里面放uncoded编码,会把uncoded编码转换成对应的字符,uncoded编码是包含ASCII码的
document.onkeypress = function(e){ console.log(String.fromCharCode(e.charCode)); }
a、b、c、d都有,而且shift + a、shift + b都行
大任务都完成了,接下来就是边边角角了
三、文本类操作事件
input事件
focus事件
blur事件
change事件
1、input事件
写一个input文本框
<input type="text" style="border:1px solid grey;outline:none;"/> <script> var inp = document.getElementsByTagName('input')[0]; // 获取input文本框 inp.oninput = function(){ // 获取完后设置一个"oninput"事件 console.log(this.value); } </script>
写一个a打印出一个a,再写一个b打印出ab,输入一次就触发一次,然后删除一个b也触发了
但凡input框里面的文本有变化,都会触发input事件不论删还是新增
2、change事件
change事件的意思是,首先聚焦,发生改变,失去焦点,然后触发事件了
<input type="text" style="border:1px solid grey;outline:none;"/> <script type="text/javascript"> var inp = document.getElementsByTagName('input')[0]; inp.onchange = function(){ console.log(this.value); } </script>
change事件对比的是"鼠标聚焦"和"失去焦点"两个状态是否发生改变,如果两个状态没发生改变不触发事件,如果发生改变触发,
不管中间怎么操作,删了多次减了多少次,只要两个状态不一样就触发change,只要两个状态一样就不触发。
3、focus事件和blur事件
focus事件,鼠标聚焦
blur事件,鼠标失去焦点
模仿新浪写一个"请输入用户名"鼠标聚焦提示文字消失,鼠标失去焦点提示文字出来,用句柄方式写
首先下面代码是不完美的,但至少实现了点完后input框的提示文本没了,失去焦点提示文字"请输入用户名"又出来了
<input type="text" style="outline:none;" value="请输入用户名" onfocus="this.value=''" onblur="this.value='请输入用户名'" />
现在点击输入文字,失去焦点后刚刚输入的文字没了,又出现提示文字"请输入用户名",应该在什么情况下提示文字才回来或者在什么情况下提示文字失去?
在输入框为空的情况下,提示文字"请输入用户名"才回来
<input type="text" style="outline:none;" value="请输入用户名" onfocus="if(this.value=='请输入用户名'){this.value='';}" onblur="if(this.value==''){this.value='请输入用户名';}" />
改一下样式的颜色,互联网标准颜色#999,maxlength="40"最长字符不能超过40个
<input type="text" style="outline:none; border:1px solid rgba(0,0,0, .3); color:#999;" value="请输入用户名" onfocus="if(this.value=='请输入用户名'){this.value=''; this.style.color='#424242';}" onblur="if(this.value==''){this.value='请输入用户名'; this.style.color='#424242';}" />
缺陷是写"请输入用户名"就没了
四、窗体操作类(window上的事件)
scroll 滚动条滚动时间
load
小练习: fixed定位 js兼容版
当滚动条一滚动,scroll事件就触发了,scroll是window上的事件
<div style="height:3000px; width: 3000px; background-color: khaki;"></div> <script> window.onscroll = function(){ console.log(window.pageXOffset + " " + window.pageYOffset); // 当滚动条滚动的时候获取滚动条滚动位置。 } </script>
还记得有一个position:fixed意思相对可视区窗口,但是有一个问题IE6没有fixed定位,如何用js给IE6写一个fixed定位,用position:absolute来模拟
position:absolute有个问题是文档往上一托就托上去了,如何保持它相对视口的位置不变呢?top等于原来的top加上滚动条的位置
把页面滚动的距离加到top上,就相当于这个方块没动
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>模拟fixed定位</title> <style> body{ height:3000px; } </style> </head> <body> <div style="height:100px;width:100px;background-color:moccasin;position:absolute;top:30px;right: 30px;"></div> <script> var oBox = document.getElementsByTagName('div')[0]; window.onscroll = function(){ console.log(oBox.style.top); oBox.style.top = 30 + window.pageYOffset + 'px'; } </script> </body> </html>
加定时器有点延迟的效果
var oBox = document.getElementsByTagName('div')[0]; window.onscroll = function(){ setInterval(function(){ oBox.style.top = 30 + window.pageYOffset + 'px'; }, 100); }
load很重要,重要不是去用,是让不去用,
读到js的时候就阻断页面,所以把js写到div元素下面,才能把上面的div元素读出来
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>load</title> </head> <body> <div></div> <script> </script> </body> </html>
如果把div元素写在js标签下面,肯定选不出来这个div,因为页面还没渲染到那,就卡死了执行js了,js是读到它执行完再往下读
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>load</title> </head> <body> <script> var div = document.getElementsByTagName('div')[0]; console.log(div); // undefined </script> <div></div> </body> </html>
有些人这样写,可以选出div元素而且还能操作div
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>load</title> </head> <body> <script> window.onload = function(){ var div = document.getElementsByTagName('div')[0]; console.log(div); // <div></div> div.style.height = '100px'; div.style.width = '100px'; div.style.backgroundColor = 'red'; } </script> <div></div> </body> </html>
这个方法是最慢的没意义是最关键的一点