# 事件进阶
目前已知:
- 事件是可以被JS所侦听到的一个行为。
- 事件分为事件源,事件类型,事件处理函数三部分。
- 事件可以作用在DOM元素上(onclick),可以作用在浏览器上(onload,onscroll)。
# - 注册事件的三种方式与移除
注册事件:为元素增加上事件,注册事件也被称为添加事件,监听事件,绑定事件。
移除事件:将元素增加的事件进行移除。移除事件也被称为删除事件,销毁事件,解除事件。
# - 内联属性注册与移除
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 增加了单击事件:当该事件触发后,会将其值作为JS代码执行。 -->
<button onclick="alert(123);">按钮下标为0</button>
<!-- 事件属性是不区分大小写,建议全部小写 -->
<button ONcLICK = "console.log('按钮下标为1');">按钮下标为1</button>
<!-- 同样的事件属性可以写多个,但只有第一个起作用 -->
<button onclick="console.log(1);" onclick="console.log(2);">按钮下标为2</button>
<!-- 事件属性的值,可以是多条JS语句 -->
<button onclick="console.log(1);console.log(2);">按钮下标为3</button>
<!-- 事件属性的值,常被用来调用函数。 -->
<button onclick="fn()">按钮下标为4</button>
<!-- 传递参数:函数体内的this为window -->
<button onclick="fn5(1,2,3,4,5)">按钮下标为5</button>
<!-- 将函数体内的this指向当前事件源:
内联属性注册实现的原理:
1-会将事件属性的值作为隐式创建函数体内的语句.
function eventFn(){fn5(1,2,3,4,5,this)};
2-点点击按钮时,会执行隐式创建的函数
onclick = eventFn();
-->
<button onclick="fn6(1,2,3,4,5,this)">按钮下标为6</button>
<button onclick="fn71.call(this,1,2,3,4)">按钮下标为7</button>
<!-- 如果事件属性的值为调用函数,调用的函数必须全局定义,即可以通过window.fn8 -->
<button onclick="fn8()">按钮下标为8</button>
<button onclick="move();">移除事件</button>
</body>
<script>
// 内联属性注册事件即是在编写HTML代码时,直接写入元素的事件属性(以on开头,不区分大小写),
// 事件属性的值是JS代码。
function fn(){
console.log("按钮下标为4")
}
function fn5(a,b,c,d,e){
console.log(a,b,c,d,e);
console.log(this);// window
}
function fn6(a,b,c,d,e,_this){
console.log(a,b,c,d,e);// 1 2 3 4 5 6
console.log(_this);// 事件源
console.log(_this.getAttribute("onclick"));
console.log(_this.onclick)// onclick(event) {fn6(1,2,3,4,5,this)};
}
// 总结:内联属性与之前的事件定义:
// 1-内联属性注册
// <button onClick="fn6(1,2,3,4,5,this)">按钮下标为6</button>
// 2-以上代码相当于:
// var btn = document.querySelectorAll("button")[6];
// btn.onclick = function (){
// fn6(1,2,3,4,5,this)
// }
// var btn = document.querySelectorAll("button")[7];
// btn.onclick = function (){
// // console.log(this);// button
// // fn7(this);
// fn71.call(this,1,2,3,4);// call的第一个参数指定fn71的this指向
// }
// function fn7(_this){
// console.log(this,_this);// window button
// }
function fn71(a,b,c,d){
console.log(this,a,b,c,d);// button
}
function fn8(){
console.log("fn8")
}
var obj = {
fn8:function (){
console.log("obj->fn8")
}
}
function move(){
// console.log("move");
// 移除下标为1的按钮
var btn = document.querySelectorAll("button")[1];
// btn.onclick = null;// 移除事件方式1
// btn.setAttribute("onclick",null);// // 移除事件方式2
// 移除所有按钮的事件
document.querySelectorAll("button").forEach(function (item){
item.onclick = null;
})
}
</script>
</html>
# - DOM属性注册与移除
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button>点我增加事件</button>
<button>移除事件</button>
</body>
<script>
// DOM元素属性注册的流程:
// 1- 获取DOM元素
// 2- 指定事件的类型
// 3- 必须指定事件处理函数。
// 特点:
// 1- 必须以on开头,且必须全部小写。
// 2- 同一元素上的同一事件,处理函数只允许写一个。如果出现多个,后者会将前者覆盖。
// 3- 事件处理函数中的this指向事件源
var btns = document.querySelectorAll("button");
btns[0].onclick = function(){
console.log("单击执行了1")
}
btns[0].onclick = function(){
console.log("单击执行了2")
}
btns[0].onclick = function(){
console.log("单击执行了3",this,this===btns[0])
}
btns[0].ondblclick = function (){
console.log("双击执行了")
}
// 移除btns[0]事件
btns[1].onclick = function(){
// btns[0].onclick = null;// 移除单击事件
// btns[0].ondblclick = null;// 移除双击事件
// 将上方两行代码可以写为一行代码:
btns[0].onclick = btns[0].ondblclick = null;
}
</script>
</html>
# - 事件监听注册与移除
注册
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <button id="btn">注册事件</button> </body> <script> // 监听注册事件的流程: // 1- 获得事件源 // 2- 指定事件类型 // 3- 指定处理函数 // addEventListener方法有三个参数: // 第一个参数:事件类型。不需要带”on" // 第二个参数:事件处理函数。 // 第三个参数是一个布尔值,也可以设置为对象(后面再学) // 特点: // 1- 指定事件类型,不要带on,且必须小写。 // 2- 可以为同一个事件源,同一种事件类型,增加多个事件处理函数。 // 哪个事件处理函数先指定,会先执行哪一个。 // 3- 不支持IE678 var button = document.getElementById("btn"); // 增加单击事件:当点击按钮后,会触发事件处理函数 button.addEventListener("click",function (){ console.log("点击啦1!") }) button.addEventListener("click",function (){ console.log("点击啦2!") }) button.addEventListener("click",function (){ console.log("点击啦3!") }) </script> </html>
移除
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <button id="btn">注册事件</button> <button id="move">移除事件</button> </body> <script> var btn = document.getElementById("btn"); var move = document.getElementById("move"); var fn = function (){ console.log("执行啦,我是先定义的") } // btn.addEventListener("click",function (){ // console.log("执行啦") // }) btn.addEventListener("click",fn) move.addEventListener("click",function (){ // 将id为btn元素的监听事件移除掉。 // btn.removeEventListener("click");// 无法删除 // removeEventListener需要传递二个参数:参数1事件类型,参数2事件处理函数(注册事件时指定的函数) // 无法移除:原因是指定的第二个参数与注册事件时指定的参数的引用不同。 // btn.removeEventListener("click",function (){ // console.log("执行啦") // }) // 正确方案: // 1- 注册事件时,需要将指定的事件处理函数先进行定义,然后再指定 // 2- 移除时,第二个参数直接写成定义的函数即可 btn.removeEventListener("click",fn); }) // // 函数也是引用类型 // var fn = function (){ // // } // var fn2 = function (){ // // } // console.log(fn === fn2);// false </script> </html>
# - 事件对象
官方解释:事件对象代表的是事件的状态。
比如键盘事件,那么代表的是键盘事件的状态。
理解:事件发生后,事件对象代表的是与该事件相关的数据集合。
# - 事件对象的获取
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button>dom属性注册事件</button>
<button onclick="fn(event)">标签内联属性注册事件</button>
<button>dom监听注册事件</button>
</body>
<script>
/*总结:
* 1- 获取事件对象除内联属性事件以外有两种方法
* 1-1:在函数体内的event即是事件对象(三种注册事件的方法均支持)
* 2-2:事件处理函数接收的第一个参数即是事件对象(内联属性事件不支持);
* 注意:接收事件对象的名字一般设置为 event/e/eve/_e/_event
* 2- 在获得事件对象时,建议采用形参接收的形式。
* 如果事件是通过内联属性注册的,那么建议传递event
* */
var btns = document.querySelectorAll("button");
btns[0].onclick = function(e){
// console.log(event);// 不推荐使用。
// console.log(e);
// console.log(e === event);// true
}
function fn(e){
// console.log(event);// 事件对象
// console.log(e);
console.log(e===event);// true
}
btns[2].addEventListener("click",function (e){
// console.log(event);// 事件对象
// console.log(e);
console.log(e===event);// true
})
</script>
</html>
# - 事件对象常见属性和方法
事件对象的属性和方法 | 说明 |
---|---|
e.target | 返回触发事件的对象 |
e.currentTarget | 始终指向添加监听事件的那个对象(事件源) |
e.type | 返回事件的类型,不带on |
e.preventDefault() | 该方法阻止默认事件(默认行为)标准比如不让链接跳转 |
e.stopPropagation() | 阻止冒泡标准 |
e.clientX | 返回鼠标相对于浏览器窗口可视区的X坐标 |
e.clientY | 返回鼠标相对于浏览器窗口可视区的Y坐标 |
e.pageX | 返回鼠标相对于文档页面的X坐标 |
e.pageY | 返回鼠标相对于文档页面的Y坐标 |
e.screenX | 返回鼠标相对于电脑屏幕的X坐标 |
e.screenY | 返回鼠标相对于电脑屏幕的Y坐标 |
e.offsetX | 返回鼠标相对于事件源的X坐标 |
e.offsetY | 返回鼠标相对于事件源的Y坐标 |
type target currentTarget
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> *{ padding:0; margin:0; } #app{ width: 300px; height: 300px; background: skyblue; } #child { width: 100px; height: 100px; background: gray; } </style> </head> <body> <div id="app"> <div id="child"></div> </div> </body> <script> var app = document.querySelector("#app"); var child = document.querySelector("#child"); // 1-e.type // function fn(e){ // // 通过该属性可以判断事件类型。 // console.log(e.type);// 获得事件的类型。 click // if(e.type === "click") console.log("单击") // else if(e.type === "dblclick") console.log("双击") // } // // 注册单击事件 // app.onclick = fn; // app.ondblclick = fn; // 2- e.target 与 e.currentTarget // e.target:可以用于事件委托。 // e.currentTarget与this相同,一般写this即可 app.onclick = function(e){ // 指向触发该事件的DOM元素 // console.log(e.target);// 指向的不是事件源对象,而是触发该事件的dom元素 // this指向的是事件源对象 // console.log(this); // 与this相同,指向的也是事件源对象 console.log(e.currentTarget,this === e.currentTarget); } </script> </html>
通过事件对象获得鼠标位置
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> *{ padding:0; margin:0; } #app{ width: 200px; height: 300px; background: skyblue; margin:100px; } </style> </head> <body style="height:1000px;"> <div id="app"></div> </body> <script> var app = document.querySelector("#app"); app.onclick = function(e){ // e.clientX/e.clientY:事件触发时鼠标在浏览器内容区域的坐标。原点在浏览器视口左上角 // console.log("clientX:"+e.clientX,"clientY:"+e.clientY); // e.screenX/e.screenY:事件触发时鼠标所在电脑屏幕的坐标。原点在屏幕左上角 // console.log("screenX:"+e.screenX,"screenY:"+e.screenY); // e.pageX/e.pageY:事件触发时鼠标所在页面的坐标。原点在页面的左上角 // console.log("pageX:"+e.pageX,"pageY:"+e.pageY); // e.offsetX/e.offsetY:事件触发时鼠标所在事件源的坐标。原点在事件源的左上角 console.log("offsetX:"+e.offsetX,"offsetY:"+e.offsetY); } </script> </html>
preventDefault
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> a{ margin:10px; } </style> </head> <body> <a href="http://www.baidu.com">百度</a> <a href="http://www.taobao.com">淘宝</a> <a href="http://www.jd.com" onclick="go(event,this)">京东</a> </body> <script> // a标签默认会跳转至href地址。 var a = document.querySelectorAll("a"); // 单击之后,会执行事件处理函数,事件处理函数执行完毕之后,会执行默认行为。 // 去除默认事件。 // 1- 可以使用事件对象中的.preventDefault。 // preventDefault是一个函数,该函数可以旋转在函数体内的任何位置 。 // 2- 使用return false;(只有在DOM元素属性注册的事件中有效果) // 后续代码不会执行。 a[0].onclick = function(e){ return false; alert(this.innerText); // e.preventDefault();// 去除默认事件。 } a[1].addEventListener("click",function (e){ // return false;// 不支持取消默认事件。 alert(e.currentTarget.innerText); // e.preventDefault(); // return false;// 不支持取消默认事件。 }) function go(event,_this){ // return false;// 不支持取消默认事件。 // alert(_this.innerText); event.preventDefault(); } </script> </html>
# - 事件委托
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button>点我增加LI</button>
<ul>
<li>高大帅</li>
<li>白富美</li>
<li>帅气</li>
<li>漂亮</li>
<li>强大</li>
</ul>
</body>
<script>
// 事件委托:也被称为事件代理,在JQ称为事件委派。
// 1-不会将事件定义在自身,而是定义在父级,
// 2-通过事件冒泡来判断点击的具体是哪一个元素来实现相对应的功能。
// 将li的单击事件定义在ur上,那么点击li,通过冒泡会触发ul的单击事件
// 在ul的单击事件处理函数中可以通过e.target判断点击的是哪个li
// e.target:触发事件的元素,比如点击li,通过冒泡会触发ul的点击事件,
// 在事件的处理函数中,通过e.target得到的即是该li
// 由于 只是增加了一个事件,效率会更高。
var ul = document.querySelector("ul");
var btn = document.querySelector("button");
ul.onclick = function(e){
// console.log("ul");
// console.log(e.target)
// 判断标签名是否为li
console.log(e.target.nodeName)
if(e.target.nodeName === "LI"){
e.target.style.background = "green";
}
}
btn.onclick = function(){
var li = document.createElement("li");
li.innerText = "四库全书";
ul.appendChild(li);
}
</script>
</html>
# - DOM事件流
为DOM绑定好事件,事件被触发时,会在元素之间产生一个执行的顺序,那么执行事件的顺序称为事件流。
事件流:可以认为是事件的传播,传播的过程:
1- 事件会从最外层向最内层进行遍历,直到所要操作的元素(捕获阶段),
2-所要操作的元素会触发事件(目标阶段)
3-会向回进行事件的触发。(冒泡阶段)
顺序:捕获-》目标-》冒泡
# - 事件冒泡(重点)
IE提出的。事件开始时由一个具体的元素来接收,然后逐层向上传播到最顶层这个过程称为冒泡。
产生冒泡
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> *{ padding:0; margin:0; } body,html{ width: 100%; height: 100%; } #father{ width: 300px; height: 300px; background: red; } #son{ width: 200px; height: 200px; background: yellow; } </style> </head> <body> <div id="father"> <div id="son"></div> </div> </body> <script> // 注意注意注意:DOM属性注册的事件只会产生冒泡,不会产生捕获。 // 1- 通过onclick增加事件,产生冒泡 // 事件冒泡:事件向上传递 // var son = document.querySelector("#son"); // var father = document.querySelector("#father"); // son.onclick = function(){ // console.log("son"); // } // father.onclick = function(){ // console.log("father"); // } // document.body.onclick = function(){ // console.log("body"); // } // document.documentElement.onclick = function(){ // console.log("html"); // } // 2- 通过addEventListener产生冒泡 // 需要省略第三个参数或将第三个参数设置为false var son = document.querySelector("#son"); var father = document.querySelector("#father"); son.addEventListener("click",function (){ console.log("son"); }) father.addEventListener("click",function (){ console.log("father"); }) document.body.addEventListener("click",function (){ console.log("body"); }) document.documentElement.addEventListener("click",function (){ console.log("html"); }) </script> </html>
# - 事件捕获
网景提出的。由DOM最顶层节点开始,然后逐层向下传播到具体的元素接收的过程,称为捕获
事件捕获
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> *{ padding:0; margin:0; } body,html{ width: 100%; height: 100%; } #father{ width: 300px; height: 300px; background: red; } #son{ width: 200px; height: 200px; background: yellow; } </style> </head> <body> <div id="father"> <div id="son"></div> </div> </body> <script> // 1- 通过addEventListener产生捕获。 // 需要将第三个参数设置为true var son = document.querySelector("#son"); var father = document.querySelector("#father"); son.addEventListener("click",function (){ console.log("son"); },true) father.addEventListener("click",function (){ console.log("father"); },true) document.body.addEventListener("click",function (){ console.log("body"); },true) </script> </html>
阻止捕获
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
padding:0;
margin:0;
}
body,html{
width: 100%;
height: 100%;
}
#father{
width: 300px;
height: 300px;
background: red;
}
#son{
width: 200px;
height: 200px;
background: yellow;
}
</style>
</head>
<body>
<div id="father">
<div id="son"></div>
</div>
</body>
<script>
// 1- 通过addEventListener产生捕获。
// 需要将第三个参数设置为true
var son = document.querySelector("#son");
var father = document.querySelector("#father");
son.addEventListener("click",function (){
console.log("son");
},true)
father.addEventListener("click",function (){
console.log("father");
},true)
document.body.addEventListener("click",function (e){
e.stopPropagation();
console.log("body");
},true)
</script>
</html>
# - 常用事件类型
鼠标事件
事件 说明 onclick 点击某个对象时触发 ondblclick 双击某个对象时触发 onmouseover 鼠标移入某个元素时触发 onmouseout 鼠标移出某个元素时触发 onmouseenter 鼠标进入某个元素时触发 onmouseleave 鼠标离开某个元素时触发 onmousedown 鼠标按下时触发 onmouseup 鼠标抬起时触发 onmousemove 鼠标被移动时触发 onwheel 鼠标滚轮滚动时触发 oncontextmenu 点击鼠标右键时触发
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button>单击</button>
<button>右击</button>
<button>双击</button>
<button>按下 抬起</button>
<button>移入 移出 1</button>
<button>移入 移出 2</button>
<button>移动</button>
<button>滚轮</button>
</body>
<script>
var btns = document.querySelectorAll("button");
btns[0].addEventListener("click",function (){
console.log("单击")
})
btns[1].addEventListener("contextmenu",function (e){
// 可以通过事件对象去除默认事件
e.preventDefault();
console.log("右击")
})
btns[2].ondblclick = function(){
console.log("双击")
}
btns[3].onmouseup = function(){
console.log("抬起")
}
btns[3].onmousedown = function(){
console.log("按下")
}
// 移入移出1
btns[4].onmouseenter = function(){
console.log("当鼠标进入该元素会触发,移入")
}
btns[4].onmouseleave = function(){
console.log("当鼠标离开该元素会触发,移出")
}
// 移入移出2
btns[5].onmouseover = function(){
console.log("当鼠标进入该元素会触发,移入")
}
btns[5].onmouseout = function(){
console.log("当鼠标离开该元素会触发,移出")
}
btns[6].onmousemove = function(){
console.log("当鼠标在该元素上进行移动时触发")
}
btns[7].onwheel =function (){
console.log("当滚轮进行滚动时触发")
}
</script>
</html>
键盘事件
事件 说明 onkeydown 键盘的键按下时触发 onkeyup 键盘的键放开时触发 onkeypress 在键盘按键被按下并释放一个键时发生。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
<script>
// 执行顺序:keyup-->keypress->keydown
// keypress:只有按下输入键时触发。
// keydown:不管按下的是哪一个键均会触发。
// 也是在键盘按下时触发:按下不放会一直触发
document.onkeypress = function(){
console.log("onkeypress按下")
}
// 监听键盘按下:按下不放会一直触发。
document.onkeydown = function(){
console.log("onkeydown按下")
}
// 监听键盘抬起
document.onkeyup = function(){
console.log("onkeyup抬起")
}
</script>
</html>
窗口事件
事件 说明 onresize 浏览器窗口大小改变时触发 onscroll 文档滚动时触发 onload 当页面中所有资源加载完毕触发 加载事件
事件 说明 onload 元素加载完时触发 表单事件
事件 说明 onfocus 元素获得焦点时触发 onblur 元素失去焦点时触发 onchange 离开输入框后且内容改变时触发 oninput 元素获取用户输入时触发 onsubmit 提交按钮时触发 onreset 重置按钮时触发 onselect 文本被选中时触发 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form name="myForm" target="_blank" action="https://www.baidu.com/s"> <p>关键字:<input autocomplete="off" name="wd" type="text"></p> <p> <select name="city" id=""> <option value="1">北京</option> <option value="2">上海</option> </select> </p> <!-- 点击button默认会提交表单,会触发表单的submit事件,提交至form->action --> <button>提交表单1</button> <!-- type值为submit也会提交表单,会触发表单的submit事件 --> <input type="submit" value="提交表单2"> <!-- type值为button不会提交表单,不会触发表单的submit事件 --> <input type="button" name="btn" value="提交表单3"> <!-- type值为reset会触发表单当中reset事件 --> <input type="reset" value="重置表单"> </form> </body> <script> // 获得表单 var myForm = document.myForm; myForm.onsubmit = function(e){ // 当提交表单时,会执行该处理函数。 e.preventDefault();// 去除默认事件:跳转至action地址 alert("执行啦") } myForm.btn.onclick =function(){ // myForm.onsubmit(); myForm.onreset(); } myForm.onreset = function(e){ console.log(123) // e.preventDefault();// 去除默认事件:重置 表单。 // alert("reset"); } // 获得焦点事件 myForm.wd.onfocus = function(){ // console.log("获得焦点事件") myForm.wd.style.background = "red"; } // 失去焦点事件 myForm.wd.onblur = function(){ // console.log("失去焦点事件") myForm.wd.style.background = ""; } myForm.wd.onchange = function(){ // 写入的内容必须为小写 this.value = this.value.toLowerCase(); console.log("当写入的内容发生变化且失去焦点后执行。") } myForm.wd.onselect = function(){ console.log("文本被选中执行") } myForm.wd.oninput = function(){ console.log("元素获取用户输入的信息时触发"); } myForm.city.onchange = function(){ console.log("当下拉列表的内容发生变化后执行",this.value) } </script> </html>
多媒体事件
事件 说明 onplay 在视频/音频开始播放时触发 onended 在视频/音频播放结束时触发 onpause 在视频/音频暂停时触发
# - 案例-滚轮放大缩小
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>缩放图片-前端猫</title> <style> *{ padding:0; margin:0; } img{ height: 200px; position: absolute; left:0; top:0; bottom: 0; right: 0; margin: auto; } </style> </head> <body> <img src="./img/10.jpg" alt=""> </body> <script> var img = document.querySelector("img"); img.addEventListener("mousewheel",function (e){ if(e.wheelDelta>0){ if(this.offsetHeight+10>500) return; this.style.height = (this.offsetHeight+10)+ "px"; }else{ if(this.offsetHeight-10<50) return; this.style.height = (this.offsetHeight-10)+ "px"; } },{ passive:true }) </script> </html>
# - 案例-键盘事件控制人物行走
效果
# - 案例-拖拽
效果
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>案例-拖拽</title> <style> * { padding: 0; margin: 0; } body { user-select: none; } #app { position: relative; margin: 0 auto; width: 500px; height: 500px; border: 1px solid green; } #ball { width: 50px; height: 50px; line-height: 50px; font-size: 30px; text-align: center; background: gray; border-radius: 100%; position: absolute; color:green; top: 0; left: 0; cursor: move; } </style> </head> <body> <div id="app"> <div id="ball">猫</div> </div> </body> <script> // 获得ID为ball的元素。 var ball = document.querySelector("#ball"); var app = document.querySelector("#app"); var ballOffsetX = 0;// 鼠标按下时所在ball的横坐标初始值 var ballOffsetY = 0;// 鼠标按下时所在ball的纵坐标初始值 function move(e) { var leftV = e.clientX - ballOffsetX - app.offsetLeft; var topV = e.clientY - ballOffsetY - app.offsetTop; if (leftV < 0) leftV = 0; else if (leftV > (app.clientWidth - ball.offsetWidth)) leftV = app.clientWidth - ball.offsetWidth; if (topV < 0) topV = 0; else if (topV > (app.clientHeight - ball.offsetHeight)) topV = app.clientHeight - ball.offsetHeight; console.log(ballOffsetX) ball.style.left = leftV + "px"; ball.style.top = topV + "px"; } function up() { ball.style.background = ""; // 移除move事件 document.removeEventListener("mousemove", move); // 移除抬起事件 document.removeEventListener("mouseup", up) } // 按下事件 ball.addEventListener("mousedown", function (e) { this.style.background = "skyblue"; ballOffsetX = e.offsetX;// 鼠标按下时所在ball的横坐标 ballOffsetY = e.offsetY;// 鼠标按下时所在ball的纵坐标 document.addEventListener("mousemove", move); document.addEventListener("mouseup", up) }) </script> </html>
# - 案例-360度旋转汽车
效果
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>案例-360度旋转汽车</title> <style> * { padding: 0; margin: 0; } #app { position: relative; width: 780px; height: 520px; margin: 0 auto; border: 1px solid green; cursor: move; } #loading{ position: absolute; left:0; top:0; width: 100%; height: 100%; background: gray; color:white; text-align: center; font-size: 20px; line-height: 520px; } #app img { width: 100%; height: 100%; } </style> </head> <body> <div id="app"> <div id="loading">已加载<span>0</span>%</div> <img alt=""> </div> </body> <script> var span = document.querySelector("span"); var loading = document.querySelector("#loading"); var img = document.querySelector("img"); // 在使用鼠标移动事件以及抬起事件,一般需要写在按下事件内。 var app = document.querySelector("#app"); // 记录图片的下标 var index = 0; var startX = 0;// 按下鼠标的起始位置初始值 var maxImgNum = 72;// 小汽车的数量 var saveImgNum = 0;// 加载完成的数量 for(var i=0;i<maxImgNum;i++){ var nImg = new Image(); nImg.src = "../img/golf"+i+".jpg"; // onload是异步的。 nImg.onload = function(){ saveImgNum++; // console.log("进度:"+(saveImgNum/maxImgNum*100).toFixed(2)+"%") span.innerText = (saveImgNum/maxImgNum*100).toFixed(2); if(saveImgNum === maxImgNum){// 图片加载完毕 img.src = "../img/golf0.jpg" // 将loading元素隐藏掉 loading.style.display = "none"; // console.log("图片加载完毕"); app.addEventListener("mousedown", function (e) { e.preventDefault();// 移除默认事件。 // 记录鼠标在app的横坐标位置 startX = e.offsetX; app.addEventListener("mousemove", move) document.addEventListener("mouseup", up) }) } } } function move(e) { var endX = e.offsetX;// 移动的坐标点 // console.log(endX-startX);// 移动的距离 var distance = endX - startX;// 移动的距离 // if (distance < 0) console.log("向左", distance); // else console.log("向右",); index+=distance; if(index<0) index=71; if(index>71) index=0; img.src = "../img/golf"+index+".jpg" startX = endX; } function up() { app.removeEventListener("mousemove", move); document.removeEventListener("mouseup", up); } </script> </html>
# - 案例-省市区三级联动
效果
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>三级联动-前端猫</title> </head> <body> <select name="provice"> <option value="-1">--请选择省份--</option> </select> <select name="city"> <option value="-1">--请选择城市--</option> </select> <select name="county"> <option value="-1">--请选择区域--</option> </select> </body> <script src="js/data.js"></script> <script> var provice = document.querySelector("select[name=provice]"); var city = document.querySelector("select[name=city]"); var county = document.querySelector("select[name=county]"); city.onchange = function(){ var _this = this; county.querySelectorAll("option").forEach(function (item){ // 如果value不等于-1,那么删除 if(item.value/1 !== -1) county.removeChild(item); }) if(this.value/1 === -1) return; county.innerHTML += Object.keys(cityJson[this.value]).map(function (item){ return `<option value="${item}">${cityJson[_this.value][item]}</option>` }) } provice.onchange = function(){ // 通过this.value 将city中增加对应城市信息 // console.log(cityJson[this.value]); var _this = this; city.querySelectorAll("option").forEach(function (item){ // 如果value不等于-1,那么删除 if(item.value/1 !== -1) city.removeChild(item); }) county.querySelectorAll("option").forEach(function (item){ // 如果value不等于-1,那么删除 if(item.value/1 !== -1) county.removeChild(item); }) // 如果未选择省份,那么直接返回 if(this.value/1 === -1) return; city.innerHTML+= Object.keys(cityJson[this.value]).map(function (item){ return `<option value="${_this.value},${item}">${cityJson[_this.value][item]}</option>` }) } provice.innerHTML += Object.keys(cityJson["0"]).map(function (item){ return `<option value="0,${item}">${cityJson["0"][item]}</option>` }).join("") </script> </html>
# - 案例-锅打灰太郎
效果
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>锅打灰太郎-前端猫</title> <style> *{ padding:0; margin:0; } #app { position: relative; width: 320px; height: 480px; background:url("./img/game_bg.jpg"); margin:0 auto; } .btn{ position: absolute; top:200px; left:50%; font-size:40px; cursor: pointer; transform: translateX(-50%); color:white; } .btn:hover{ color:red; } /*进度条*/ #progress{ position: absolute; left:63px; top:66px; width: 180px; height: 16px; background:url("./img/progress.png"); } /*狼图片定位*/ #wolf img{ position: absolute; } #score{ position: absolute; left:60px; top:10px; font-size: 20px; color:skyblue; } </style> </head> <body> <div id="app"> <!-- 进度条 --> <div id="progress"></div> <!-- 灰太狼或小灰灰 --> <div id="wolf"></div> <div id="score">0</div> <!-- 开始按钮 --> <div class="btn" id="start">开始</div> <!-- 继续按钮 --> <div class="btn" id="continue">继续</div> </div> </body> <script> // 伪代码: // 当用户将浏览器最小化,或重新打开一个视口,游戏应该中止, // 当用户再次进入游戏时,需要点击继续才可以。 // 当游戏开始后,监听用户的行为:是否离开当前页面。 // 如果离开需要让游戏暂停 // 1- 将倒计时与狼动画定时器移除 // 2- 显示 继续 按钮 // 3- 点击继续,可以继续玩。 // 3-1: 将倒计时与狼动画的定时器添加上 // 3-2: 继续按钮隐藏掉。 // 当游戏结束(时间到了)之后,记得清理监听离开事件 // 获得点击按钮 var startBtn = document.querySelector("#start"); // 分数 var score = document.querySelector("#score"); // 得到进度条 var progress = document.querySelector("#progress"); // 得到包裹狼图片的元素 var wolfBox = document.querySelector("#wolf"); // 继续按钮 var continueBtn = document.querySelector("#continue"); continueBtn.style.display = "none";// 隐藏继续按钮 // 存储倒计时定时器返回的值 var countTime; // 狼出现在洞口的定位下标 var wolfStartArr = [ {left: "98px", top: "115px"}, {left: "17px", top: "160px"}, {left: "15px", top: "220px"}, {left: "30px", top: "293px"}, {left: "122px", top: "273px"}, {left: "207px", top: "295px"}, {left: "200px", top: "211px"}, {left: "187px", top: "141px"}, {left: "100px", top: "191px"} ]; // 创建一个图片元素,狼 var wolfImg = document.createElement("img"); // 为继续按钮增加事件 continueBtn.onclick = function(){ // 计时器 countTime = setInterval(countDown,10); // 创建狼的动画定时器 wolfImg.timer = setInterval(wolfMove,200); // 隐藏继续按钮 this.style.display = "none"; } // 为按钮增加事件 startBtn.onclick = function(){ // 将分值设置为0 score.innerHTML = 0; // 将时间进度条宽度设置为180px; progress.style.width = "180px"; // 隐藏开始按钮 this.style.display = "none"; // 通过定时器控制进度条的宽度 countTime = setInterval(countDown,10); // 调用产生狼的方法 createWolf(); // 判断用户是否离开 当前页面 document.onvisibilitychange = function(){ // 如果离开或进入到该页面会触发该函数。 // hidden:离开 visible进入。 // console.log(document.visibilityState);// hidden visible if(document.visibilityState === "hidden"){ // 离开 // 1- 将倒计时与狼动画计时器移除 // 2- 显示 继续 按钮 clearInterval(countTime); clearInterval(wolfImg.timer); continueBtn.style.display = "block"; } } } // 为狼增加点击事件 wolfImg.onclick = function(){ this.state = 3;// 将动画的状态设置为3,说明锅打 this.index = 5; if(this.wolfType === "h"){ // 灰太狼加10 score.innerHTML = score.innerHTML/1+10; }else{ // 小灰灰减10 score.innerHTML = score.innerHTML/1-10; } } // 去除选中的默认行为 document.onmousedown = function (e){ e.preventDefault(); } // 倒计时 function countDown(){ // 每秒钟需要将进度条的宽度进行减法运行 progress.style.width = (progress.getBoundingClientRect().width-0.3)+"px"; // 判断是否倒计时结束 if(progress.getBoundingClientRect().width-0.3 <= 0){ document.onvisibilitychange = null; // 清除定时器 clearInterval(countTime); // 清除狼动画 clearInterval(wolfImg.timer); // 清除狼图片 wolfBox.removeChild(wolfImg); // 显示按钮,并将按钮的文字修改为重新开始 startBtn.innerHTML = "重新开始"; startBtn.style.display = "block"; console.log("游戏结束") } } // 让狼出现 function createWolf(){ // 获得狼出现洞穴的位置 var wolfStart = wolfStartArr[getRandom(0,8)]; // 左 wolfImg.style.left = wolfStart.left; // 上 wolfImg.style.top = wolfStart.top; // 通过随机函数生成1---100 数字,如果得到的数字<=80认为是灰太狼,否则是小灰灰 wolfImg.wolfType = getRandom(1,100)<=80?"h":"x"; // 指定初始图片位置 wolfImg.index = 0; // 指定图片的状态 1- 出洞 2-回洞 3- 锅打 wolfImg.state = 1; // 指定具体图片: 狼的类型+图片下标 wolfImg.src = "./img/"+(wolfImg.wolfType+wolfImg.index)+".png"; // 将图片放置到指定的位置 wolfBox.appendChild(wolfImg); // 播放动画 wolfImg.timer = setInterval(wolfMove,200); } // 狼进行移动,播放动画 function wolfMove(){ // 出洞 if(wolfImg.state === 1){ wolfImg.index++; // 当下标如果等于5,那么应该回洞 if(wolfImg.index === 5) wolfImg.state = 2; }else if(wolfImg.state === 2){ wolfImg.index--; }else if(wolfImg.state === 3){ wolfImg.index++; } // 已经回洞了 if(wolfImg.index<0 || wolfImg.index>9){ // 清除定时器 clearInterval(wolfImg.timer); // 将狼从wolfBox中移除。(注意:只是从容器中移除了) wolfBox.removeChild(wolfImg); createWolf(); return; } wolfImg.src = "./img/"+(wolfImg.wolfType+wolfImg.index)+".png" } // 生成两个数之间的随机数字 function getRandom(min,max){ return Math.floor(Math.random()*(max-min+1)+min); } </script> </html>