js学习(十四)-- 事件

HTML DOM Event 对象(https://www.w3school.com.cn/jsref/dom_obj_event.asp)

本节要点记录

  • callback.call(obj)
    callback函数有call和apply方法,可以在执行函数是动态的绑定上下文
    callback.call(obj);就是绑定其上下文为obj
  • 事件对象、
    使用函数汇总传递的事件对象event可以获取target,用来指定事件的绑定对象
    event.target.className == "link";

1. 事件对象

事件对象,当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数
在事件对象中封装了当前事件相关的一切信息。比如,鼠标的坐标、键盘、鼠标滚轮滚动的方向。。。

  • onmousemove
    该事件将会在鼠标在元素中移动时被触发
areaDiv.onmousemove = function(){}
  • clientX可以获取鼠标指针的水平坐标
    clientY可以获取鼠标指针的垂直坐标
//只需要定义一个形参event就可以使用事件对象了
areaDiv.onmousemove = function(event){
      /*
      * clientX可以获取鼠标指针的水平坐标
      * clientY可以获取鼠标指针的垂直坐标
      */
      var x = event.clientX;
      var y = event.clientY;
      alert("x = "+x+",Y = "+y);
}

在IE8中,响应函数被触发时,浏览器不会传递事件对象
在IE8及以下的浏览器中,是将事件对象作为window对象的属性保存的

window.event.clientX;

火狐没有window.event,IE没有event
针对火狐和IE做出代码优化

//只需要定义一个形参event就可以使用事件对象了
areaDiv.onmousemove = function(event){
      if(!event){
            event = window.event;
      }
      event = event || window.event;//利用了||的短路,如果event为true则直接返回,如果为false就返回后者,是前面代码的改进版
      /*
      * clientX可以获取鼠标指针的水平坐标
      * clientY可以获取鼠标指针的垂直坐标
      */
      var x = event.clientX;
      var y = event.clientY;
      alert("x = "+x+",Y = "+y);
}

练习--div跟随鼠标移动

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			#box1{
				100px;
				height:100px;
				background-color:red;
				/**
				 * 开启box1的绝对定位
				 */
				position:absolute;
			}
		</style>
		<script type="text/javascript">
			window.onload = function(){
				/**
				 * 使div可以跟随鼠标移动
				 */
				//获取box1 
				var box1 = document.getElementById("box1");
				//绑定鼠标移动事件
				document.onmousemove = function(event){
					//解决兼容问题
					event = event||window.event;
					//获取到鼠标的坐标
					/**
					 * clientX和clientY
					 *   用于获取鼠标在当前的可见窗口的坐标
					 * 	div的偏移量,是相对于整个页面的
					 * pageX和pageY可以获取鼠标相对于当前页面的坐标
					 * 但是这两个属性在IE8中不支持
					 */
					var left = event.pageX;
					var top = event.pageY;
					
					
					//设置div的偏移量
					box1.style.left = (left-50)+"px";
					box1.style.top = (top-50)+"px";
				}
			}
		</script>
	</head>
	<body>
		<div id="box1">
			
		</div>
	</body>
</html>

上述方法使用pageX和pageY不被IE8所支持
解决方法

<script type="text/javascript">
			window.onload = function(){
				/**
				 * 使div可以跟随鼠标移动
				 */
				//获取box1 
				var box1 = document.getElementById("box1");
				//绑定鼠标移动事件
				document.onmousemove = function(event){
					//解决兼容问题
					event = event||window.event;
					//获取到鼠标的坐标
					/**
					 * clientX和clientY
					 *   用于获取鼠标在当前的可见窗口的坐标
					 * 	div的偏移量,是相对于整个页面的
					 */
					var left = event.clientX;
					var top = event.clientY;
					
					//获取滚动条滚动的距离
					/**
					 * chrome认为浏览器的滚动条是body的,可以通过body.scrollTop来获取
					 * 火狐等浏览器认为浏览器的滚动条是html的
					 */
					//var st = document.body.scrollTop;//火狐不支持
					//var st = document.documentElement.scrollTop;//chrome不支持 
					
					var st = document.body.scrollTop || document.documentElement.scrollTop;
					var sl = document.body.scrollLeft || document.documentElement.scrollLeft;
					//设置div的偏移量
					box1.style.left = (left-50+sl)+"px";
					box1.style.top = (top-50+st)+"px";
				}
			}
		</script>

2.事件的冒泡(Bubble)

  • 所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发
  • 在开发中,大部分情况冒泡都是有用的
  • 如果不希望发生事件冒泡可以通过事件对象来取消冒泡
<script type="text/javascript">
			window.onload = function(){		
				/*
				 * 事件的冒泡(Bubble)
				 * 	- 所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发
				 * 	- 在开发中大部分情况冒泡都是有用的,如果不希望发生事件冒泡可以通过事件对象来取消冒泡
				 * 
				 */
				
				//为s1绑定一个单击响应函数
				var s1 = document.getElementById("s1");
				s1.onclick = function(event){
					event = event || window.event;
					alert("我是span的单击响应函数");
					
					//取消冒泡
					//可以将事件对象的cancelBubble设置为true,即可取消冒泡
					event.cancelBubble = true;
				};
				
				//为box1绑定一个单击响应函数
				var box1 = document.getElementById("box1");
				box1.onclick = function(event){
					event = event || window.event;
					alert("我是div的单击响应函数");
					
					event.cancelBubble = true;
				};
				
				//为body绑定一个单击响应函数
				document.body.onclick = function(){
					alert("我是body的单击响应函数");
				};
	
			};
	
		</script>

2.1事件的委派(冒泡应用)

希望只绑定一次事件,即可应用到多个的元素上,即使元素是后添加的
我们可以尝试将其绑定给元素的共同的祖先元素

事件的委派

  • 指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件。
  • 事件委派就是利用了冒泡,通过委派可以减少事件绑定的次数,提高程序的性能
  • 使用事件对象可以指定触发的事件对象
if(event.target.className == "link"){
      alert("hello world!");
}

事件委派练习

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<script type="text/javascript">
			
			window.onload = function(){
				
				var u1 = document.getElementById("u1");
				
				//点击按钮以后添加超链接
				var btn01 = document.getElementById("btn01");
				btn01.onclick = function(){
					//创建一个li
					var li = document.createElement("li");
					li.innerHTML = "<a href='javascript:;' class='link'>新建的超链接</a>";
					
					//将li添加到ul中
					u1.appendChild(li);
				};
				
				
				/*
				 * 为每一个超链接都绑定一个单击响应函数
				 * 这里我们为每一个超链接都绑定了一个单击响应函数,这种操作比较麻烦,
				 * 	而且这些操作只能为已有的超链接设置事件,而新添加的超链接必须重新绑定
				 */
				//获取所有的a
				var allA = document.getElementsByTagName("a");
				//遍历
				/*for(var i=0 ; i<allA.length ; i++){
					allA[i].onclick = function(){
						alert("我是a的单击响应函数!!!");
					};
				}*/
				
				/*
				 * 我们希望,只绑定一次事件,即可应用到多个的元素上,即使元素是后添加的
				 * 我们可以尝试将其绑定给元素的共同的祖先元素
				 * 
				 * 事件的委派
				 * 	- 指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素
				 * 		从而通过祖先元素的响应函数来处理事件。
				 *  - 事件委派是利用了冒泡,通过委派可以减少事件绑定的次数,提高程序的性能
				 */
				
				//为ul绑定一个单击响应函数
				u1.onclick = function(event){
					event = event || window.event;
					
					/*
					 * target
					 * 	- event中的target表示的触发事件的对象
					 */
					//alert(event.target);
					
					
					//如果触发事件的对象是我们期望的元素,则执行否则不执行
					if(event.target.className == "link"){
						alert("我是ul的单击响应函数");
					}
					
				};
				
			};
			
		</script>
	</head>
	<body>
		<button id="btn01">添加超链接</button>
		
		<ul id="u1" style="background-color: #bfa;">
			<li>
				<p>我是p元素</p>
			</li>
			<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>
		
	</body>
</html>

2.2事件的绑定

使用 对象.事件=函数 的形式绑定响应的函数

他只能同时为一个元素的一个事件绑定一个响应的函数
不能绑定多个,如果绑定多个,则后边会覆盖掉前边的

addEventListener
IE8不支持

  • 通过这个方法也可以为元素绑定响应函数
  • 参数:
    • 事件的字符串,不要on
    • 回调函数,当事件触发时该函数会被调用
    • 是否在捕获阶段触发事件,需要一个布尔值,一般都传false
可以为按钮绑定n个事件而不会产生覆盖
btn01.addEventListener("click",function(){
      alert(1),
},false);
btn01.addEventListener("click",function(){
      alert(2),
},false);
btn01.addEventListener("click",function(){
      alert(3),
},false);

attachEvent()
IE8特殊的

attachEvent()

  • 在IE8中可以使用attachEvent()来绑定事件
  • 参数:
    事件的字符串,要on
    回调函数
btn01.attachEvent("onclick",function(){
      alert(1);
});
btn01.attachEvent("onclick",function(){
      alert(2);
});
  • 不同的是他是后绑定先执行,执行顺序和addEventListener()相反

addEventListener()中的this,是绑定事件的对象
attachEvent()中的this,是window
需要统一两个方法的this

兼容处理
函数优化

  • 参数:
    • obj 要绑定事件的对象
    • eventStr 事件的字符串(不要on)
    • callback 回调函数
fucntion bind(obj,eventStr,callback){
      if(obj.addEventListener){
            //大部分浏览器兼容的方式
            obj.addEventListener(eventStr,callback,false);
      }else{
            //IE8及以下
            obj.attachEvent("on"+eventStr,function(){
                  //在匿名函数中调用回调函数
                  callback.call(obj);//这样attachEvent()也能返回绑定事件的对象
            })
      }
}

3.事件的传播

  • 关于时间的传播网景公司和微软公司有不同的理解
  • 微软公司认为事件应该是由内向外传播,也就是当前事件触发时,应该先触发当前元素上的事件,然后再想当前元素的祖先元素上传播,也就是说事件应该在冒泡阶段执行。
  • 网景公司认为事件应该是由外向内传播的,也就是当前事件触发时,应该先触发当前元素的最外层的祖先元素的事件,然后再向内传播给后代元素
  • W3C综合了两个公司的方案,将事件的传播分成了三个阶段
    • 捕获阶段
      在捕获阶段时,从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
    • 目标阶段
      事件捕获到目标元素,捕获结束开始在目标玉山路上触发事件
    • 冒泡阶段
      事件从目标元素向他的祖先运输传递,依次触发祖先元素上的事件

如果希望在捕获阶段就触发事件,可以将addEventListener()的第三个参数设置为true
一般情况下我们不会希望在捕获阶段触发事件,所以这个参数一般都是false

  • IE8及以下的浏览器中没有捕获阶段

4.事件练习

4.1拖拽 P118-120

  • 拖拽的流程
  • 当鼠标在被拖拽元素上按下时,开始拖拽
  • 当鼠标移动时被拖拽元素跟随鼠标移动
  • 单鼠标松开时,被拖拽元素固定在当前位置
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<style type="text/css">
			#box1 {
				 100px;
				height: 100px;
				background-color: red;
				position: absolute;
			}

			#box2 {
				 100px;
				height: 100px;
				background-color: yellow;
				position: absolute;

				left: 200px;
				top: 200px;
			}
		</style>

		<script type="text/javascript">
			window.onload = function() {
				/*
				 * 拖拽box1元素
				 *  - 拖拽的流程
				 * 		1.当鼠标在被拖拽元素上按下时,开始拖拽  onmousedown
				 * 		2.当鼠标移动时被拖拽元素跟随鼠标移动 onmousemove
				 * 		3.当鼠标松开时,被拖拽元素固定在当前位置	onmouseup
				 */

				//获取box1
				var box1 = document.getElementById("box1");
				var box2 = document.getElementById("box2");
				var img1 = document.getElementById("img1");

				//开启box1的拖拽
				drag(box1);
				//开启box2的
				drag(box2);

				drag(img1);




			};

			/*
			 * 提取一个专门用来设置拖拽的函数
			 * 参数:开启拖拽的元素
			 */
			function drag(obj) {
				//当鼠标在被拖拽元素上按下时,开始拖拽  onmousedown
				obj.onmousedown = function(event) {

					//设置box1捕获所有鼠标按下的事件
					/*
					 * setCapture()
					 * 	- 只有IE支持,但是在火狐中调用时不会报错,
					 * 		而如果使用chrome调用,会报错
					 */
					/*if(box1.setCapture){
						box1.setCapture();
					}*/
					obj.setCapture && obj.setCapture();


					event = event || window.event;
					//div的偏移量 鼠标.clentX - 元素.offsetLeft
					//div的偏移量 鼠标.clentY - 元素.offsetTop
					var ol = event.clientX - obj.offsetLeft;
					var ot = event.clientY - obj.offsetTop;


					//为document绑定一个onmousemove事件
					document.onmousemove = function(event) {
						event = event || window.event;
						//当鼠标移动时被拖拽元素跟随鼠标移动 onmousemove
						//获取鼠标的坐标
						var left = event.clientX - ol;
						var top = event.clientY - ot;

						//修改box1的位置
						obj.style.left = left + "px";
						obj.style.top = top + "px";

					};

					//为document绑定一个鼠标松开事件
					document.onmouseup = function() {
						//当鼠标松开时,被拖拽元素固定在当前位置	onmouseup
						//取消document的onmousemove事件
						document.onmousemove = null;
						//取消document的onmouseup事件
						document.onmouseup = null;
						//当鼠标松开时,取消对事件的捕获
						obj.releaseCapture && obj.releaseCapture();
					};

					/*
					 * 当我们拖拽一个网页中的内容时,浏览器会默认去搜索引擎中搜索内容,
					 * 	此时会导致拖拽功能的异常,这个是浏览器提供的默认行为,
					 * 	如果不希望发生这个行为,则可以通过return false来取消默认行为
					 * 
					 * 但是这招对IE8不起作用
					 */
					//return false;

				};
			}
		</script>
	</head>
	<body>

		我是一段文字

		<div id="box1"></div>

		<div id="box2"></div>

		<img src="img/an.jpg" id="img1" style="position: absolute;" />
	</body>
</html>

return false让浏览不能访问复制稳了拖拽信息
box1.setCapture和releaseCapture作用为获取所有焦点,即当alt后移动对应的部分并不会移动整体,而是会只移动选中部分

4.2滚轮事件

当鼠标滚轮向下滚动时,box1变长
当滚轮向上滚动时,box1变短

  • onmousewheel 鼠标滚轮事件,会在滚轮滚动时触发,但是火狐不支持该属性

在火狐中需要使用DOMMouseScroll来绑定滚动事件
注意该事件需要通过addEventListener()函数来绑定

  • 判断鼠标滚轮的滚动方向

IE和谷歌:
event.wheelDelta 可以获取鼠标滚轮滚动的方向
向上120,向下-120
wheelDelta这个值我们不看大小只看正负

火狐:
event.detail
向上-3 , 向下3

  • 当滚轮滚动时,如果浏览器有滚动条,滚动条会随之滚动,
    这是浏览器的默认行为,如果不希望发生,则可以取消默认行为
    需要在函数最后加上false

使用addEventListener()方法绑定响应函数,取消默认行为时不能使用return false,而需要使用event.preventDefault();来取消默认行为

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<style type="text/css">
			#box1 {
				 100px;
				height: 100px;
				background-color: red;
			}
		</style>
		<script type="text/javascript">
			window.onload = function() {


				//获取id为box1的div
				var box1 = document.getElementById("box1");

				//为box1绑定一个鼠标滚轮滚动的事件
				/*
				 * onmousewheel鼠标滚轮滚动的事件,会在滚轮滚动时触发,
				 * 	但是火狐不支持该属性
				 * 
				 * 在火狐中需要使用 DOMMouseScroll 来绑定滚动事件
				 * 	注意该事件需要通过addEventListener()函数来绑定
				 */


				box1.onmousewheel = function(event) {

					event = event || window.event;


					//event.wheelDelta 可以获取鼠标滚轮滚动的方向
					//向上滚 120   向下滚 -120
					//wheelDelta这个值我们不看大小,只看正负

					//alert(event.wheelDelta);

					//wheelDelta这个属性火狐中不支持
					//在火狐中使用event.detail来获取滚动的方向
					//向上滚 -3  向下滚 3
					//alert(event.detail);


					/*
					 * 当鼠标滚轮向下滚动时,box1变长
					 * 	当滚轮向上滚动时,box1变短
					 */
					//判断鼠标滚轮滚动的方向
					if (event.wheelDelta > 0 || event.detail < 0) {
						//向上滚,box1变短
						box1.style.height = box1.clientHeight - 10 + "px";

					} else {
						//向下滚,box1变长
						box1.style.height = box1.clientHeight + 10 + "px";
					}

					/*
					 * 使用addEventListener()方法绑定响应函数,取消默认行为时不能使用return false
					 * 需要使用event来取消默认行为event.preventDefault();
					 * 但是IE8不支持event.preventDefault();这个玩意,如果直接调用会报错
					 */
					event.preventDefault && event.preventDefault();


					/*
					 * 当滚轮滚动时,如果浏览器有滚动条,滚动条会随之滚动,
					 * 这是浏览器的默认行为,如果不希望发生,则可以取消默认行为
					 */
					return false;




				};

				//为火狐绑定滚轮事件
				bind(box1, "DOMMouseScroll", box1.onmousewheel);

                                //为chrome绑定滚轮事件
                                bind(box1, "onmousewheel", box1.onmousewheel);

			};


			function bind(obj, eventStr, callback) {
				if (obj.addEventListener) {
					//大部分浏览器兼容的方式
					obj.addEventListener(eventStr, callback, false);
				} else {
					/*
					 * this是谁由调用方式决定
					 * callback.call(obj)
					 */
					//IE8及以下
					obj.attachEvent("on" + eventStr, function() {
						//在匿名函数中调用回调函数
						callback.call(obj);
					});
				}
			}
		</script>
	</head>
	<body style="height: 2000px;">
	<div id="box1"></div>
      </body>
</html>

4.3键盘事件

键盘事件一般都会绑定给一些可以获取到焦点的对象或者是document

  • onkeydown
  • 按键按下
  • 对于onkeydown来说如果一直按着某个按键不松手,则事件会一直触发
  • 当onkeydown连续触发时,第一次和第二次之间会间隔稍微长一点,其他的会非常快
document.onkeydown = function(event){
      event = event|| window.event;
      /*
      * 可以通过keyCode来获取按键的编码
      * 通过它可以判断哪个按键被按下
      */

      //判断一个y是否被按下
      if(event.keyCode === 89){
            alert("y被按下了");
      }
}
  • 监听按键
  • altKey、ctrlKey、shiftKey
    这三个用来判断alt、ctrl、shift是否被按下,如果按下则返回true
  • keyCode
    每个按键都对应有code
判断y和ctrl是否被同时按下
if(event.keyCode === 89 && event.ctrlKey){
      console.log("ctrl和y都被按下了");
}

//在文本框中输入内容,输入onkeyDown的默认行为
//如果在onkeydown中取消了默认行为,则输入的内容,不会出现在文本框中

  • onkeyup
    按键被松开

练习-- 键盘移动div

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<style type="text/css">
			#box1{
				 100px;
				height: 100px;
				background-color: red;
				position: absolute;
			}
			
			
		</style>
		
		<script type="text/javascript">
			
			//使div可以根据不同的方向键向不同的方向移动
			/*
			 * 按左键,div向左移
			 * 按右键,div向右移
			 * 。。。
			 */
			window.onload = function(){
				
				//为document绑定一个按键按下的事件
				document.onkeydown = function(event){
					event = event || window.event;
					
					//定义一个变量,来表示移动的速度
					var speed = 10;
					
					//当用户按了ctrl以后,速度加快
					if(event.ctrlKey){
						speed = 500;
					}
					
					/*
					 * 37 左
					 * 38 上
					 * 39 右
					 * 40 下
					 */
					switch(event.keyCode){
						case 37:
							//alert("向左"); left值减小
							box1.style.left = box1.offsetLeft - speed + "px";
							break;
						case 39:
							//alert("向右");
							box1.style.left = box1.offsetLeft + speed + "px";
							break;
						case 38:
							//alert("向上");
							box1.style.top = box1.offsetTop - speed + "px";
							break;
						case 40:
							//alert("向下");
							box1.style.top = box1.offsetTop + speed + "px";
							break;
					}
					
				};
				
			};
			
			
		</script>
	</head>
	<body>
		<div id="box1"></div>
	</body>
</html>

这里第一次运行时会有轻微的卡顿,后面的知识可以解决这个问题

原文地址:https://www.cnblogs.com/psyduck/p/14274532.html