事件介绍
当我们点击一个按钮的时候,会弹出一个对话框。在JavaScript中,
“点击”这个事情就看作一个事件。“弹出对话框”其实就是我们在点击事件中做的一些事。
事件组成部分
- 事件源: 触发谁的事件
- 事件类型: 这个事件是干啥的,是点击呢,还是移动呢
- 事件处理函数: 事件过程做的一些事
执行事件的步骤
- 获取事件源
- 绑定事件
- 添加事件处理函数
各种事件
常见的有:浏览器事件(load : 页面全部资源加载完毕 scroll :浏览器滚动的时候触发 resize“页面大小事件) / 鼠标事件 / 键盘事件 / 表单事件 / 触摸事件
使用时都需要在前面on
1. 鼠标事件
- click :点击事件
- dbclick :双击事件
- mouseover 鼠标移入
- mouseout 鼠标移出
- contextmenu : 右键单击事件
- mousedown :鼠标左键按下事件
- mouseup :鼠标左键抬起事件
- mousemove :鼠标移动
- mouseenter :鼠标移入事件
- mouseleave :鼠标移出事件
- e.offsetX,e.offsetY 鼠标坐标
mouseover与mouseenter区别
1、onmouseover、onmouseout:鼠标经过时自身触发事件,经过其子元素时也触发该事件;(父亲有的东西,儿子也有) ,支持冒泡
2、onmouseenter、onmouseleave:鼠标经过时自身触发事件,经过其子元素时不触发该事件。(父亲的东西就是父亲的,不归儿子所有) ,不支持冒泡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>移块移动案例</title>
<style>
* {
padding: 0;
margin: 0;
}
div {
width: 500px;
height: 500px;
background-color: skyblue;
margin: 100px;
position: relative;
}
p {
position: absolute;
width: 100px;
height: 100px;
background-color: pink;
text-align: center;
line-height: 100px;
display: none;
/* 鼠标移入当前元素事件不起作用 */
/* 解决闪烁问题;光标位置与提示信息位置重叠一起, 当光标移入div区块,显示p区域,这时光标相当于落在p区域,移出div区块,提示信息隐藏. */
pointer-events: none;// 光标(鼠标)事件,值设置none之后当前元素事件无效
}
</style>
</head>
<body>
<div>
<p>提示信息</p>
</div>
<script>
/*
鼠标移入div显示p区域
*/
var divEle = document.querySelector('div')
var pEle = document.querySelector('p')
divEle.onmouseover = function () {
pEle.style.display = 'block'
}
/*
移出隐藏
*/
divEle.onmouseout = function () {
pEle.style.display = 'none'
}
/*
p小区块随光标在大区块div中移动
获取光标位置坐标赋值给小区块
1. 获取光标位置
当光标在大区块移动时,获取坐标
2. 如何让元素到指定位置
父 相对定位
子 绝对定位
新需求
1.鼠标在保持小区域中间--小区块向上和向左移动宽度一半
2. 小区块不能移出大区块
*/
divEle.onmousemove = function (e) {
e = e || window.event //事件对象
//只能改变块的位置,不能改变光标的位置
//元素尺寸 内容宽+padding+border = offsetWidth
var x = e.offsetX -pEle.offsetWidth/2 // 相对自身位置
var y = e.offsetY-pEle.offsetHeight/2
//左边边界判断
if(x<0){
x=0
}
//右边边界,当x光标位置>大区块宽-小区块宽,表示小区块移出大区块
if(x>(divEle.offsetWidth-pEle.offsetWidth)){
x=divEle.offsetWidth-pEle.offsetWidth
}
pEle.style.left = x + 'px'
//上边边界判断
if(y<0){
y=0
}
//右边边界,当x光标位置>大区块宽-小区块宽,表示小区块移出大区块
if(y>(divEle.offsetHeight-pEle.offsetHeight)){
y=divEle.offsetHeight-pEle.offsetHeight
}
pEle.style.top = y + 'px'
}
</script>
</body>
</html>
鼠标按下,盒子跟着移动
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>拖动效果案例</title>
<style>
*{padding: 0;margin: 0;}
div{
position: relative;
width: 100px;
height: 100px;
background-color: skyblue;
}
</style>
<!--
拖动效果案例
按在div上,div区块随光标一起移动,抬起div区块停止移动
-->
</head>
<body>
<div></div>
<script>
var divEle = document.querySelector('div')
// 左键按下
divEle.onmousedown = function () {
e=e ||window.event
//记录初始位置坐标
var initX=e.offsetX
var initY=e.offsetY
// 鼠标移动事件
document.onmousemove = function (e) {
e = e || window.event
var x = e.clientX -initX //确保点击哪就从哪移动
var y = e.clientY -initY
console.log('x : ', e.clientX, ' y :', e.clientY)
divEle.style.left = x + 'px'
divEle.style.top = y + 'px'
}
}
// 左键抬起
document.onmouseup = function () {
console.log('mouseup')
document.onmousemove = null
}
</script>
</body>
</html>
2. 表单事件
- change : 表单内容改变事件 input : 表单内容输入事件 submit : 表单提交事件
表单提交事件–默认行为
1、触发表单提交事件 submit 是表单form元素
作用:
表单验证–非空验证、
2、默认行为
执行action属性指定的url地址跳转,同时获取表单输入框内容以名称值对形式作为参数传递
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>表单事件</title>
<style>
* {
padding: 0;
margin: 0;
}
form {
width: 500px;
background-color: skyblue;
margin: 100px auto;
padding: 50px;
}
form input {
width: 100%;
line-height: 40px;
margin-top: 20px;
}
#login {
height: 40px;
}
</style>
<!--
表单事件
表单提交事件
submit
表单提交事件 - 默认行为
1. 触发表单提交事件
submit 是表单form元素
作用:
表单验证
- 非空验证
表单输入框内容不能为空
2. 默认行为
执行action属性指定的url地址跳转,同时获取表单输入框内容以名称值对形式做为参数传递
https://www.xxx.com/?username=admin&password=123
阻止表单默认行为
e.preventDefault()
-->
</head>
<body>
<form action="">
<input type="text" placeholder="请输入用户名" name="username" /><br />
<input type="password" placeholder="请输入密码" name="password" /><br />
<input type="submit" value="确定" id="login" />
</form>
<script>
var formEle = document.querySelector('form')
var usernameInput = document.querySelector('input[name="username"]')
var passwordInput = document.querySelector('input[name="password"]')
//表单form提交事件
formEle.onsubmit = function (e) {
e = e || window.event // 事件对象
e.preventDefault() // 阻止表单form默认行为
//表单非空验证
var username = usernameInput.value
var password = passwordInput.value
// 用户名密码验证
if(username == 'admin' && password == '123'){
// 表单验证通过 跳转到主界面
location.href = './index.html'
}else{
alert('用户名或密码出错!')
}
}
usernameInput.onblur = function(){
console.log('失去焦点')
var username = usernameInput.value
if (username === '') {
alert('用户名不能为空!')
return
}
}
passwordInput.onblur = function(){
console.log('失去焦点')
var password = passwordInput.value
if (password === '') {
alert('密码不能为空!')
return
}
}
</script>
</body>
</html>
表单项内容转变事件 onchange
内容改变会触发事件
<form action="">
<input type="file" name="headerimg">
</form>
<script>
var headerInput=document.querySelector('input[name="headerimg"]')
headerInput.onchange=function(){
alert('change')
}
</script>
添加图片组件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片预览组件</title>
<style>
*{padding: 0; margin: 0;}
.preview-wrap{
position: relative;
margin: 100px;
width: 128px;
height: 128px;
border: 1px dotted #666666;
}
.preview-wrap input{
opacity: 0;
}
.preview-wrap label{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
font-size: 38px;
color: #666666;
}
.preview-wrap img{
position: absolute;
top: 0;
left: 0;
width: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div class="preview-wrap">
<input type="file" name="previewfile" id="previewfile">
<div>
<img src="" />
<label for="previewfile">+</label>
</div>
</div>
<script >
let previewInput = document.getElementById('previewfile');
let imgEle = previewInput.nextElementSibling.firstElementChild;
let labelEle = imgEle.nextElementSibling;
previewInput.onchange = function (event) {
let file = event.target.files[0];
let fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = function (e) {
imgEle.src = e.target.result;
labelEle.style.opacity = '0';
}
}
</script>
</body>
</html>
3. 焦点事件
- 失去焦点onblur
- 获取焦点onfocus
可以用时在失去焦点时进行表单的判断
<form action="">
<input type="text" placeholder="请输入用户名" name="username" /><br />
<input type="password" placeholder="请输入密码" name="password" /><br />
</form>
<script>
var usernameInput = document.querySelector('input[name="username"]')
usernameInput.onfocus = function(){
console.log('获取焦点')
}
usernameInput.onblur = function(){
console.log('失去焦点')
var username = usernameInput.value
if (username === '') {
alert('用户名不能为空!')
return
}
}
passwordInput.onblur = function(){
console.log('失去焦点')
var password = passwordInput.value
if (password === '') {
alert('密码不能为空!')
return
}
}
4. 键盘事件
onkeyup 键盘弹起 onkeydown 键盘按下 keypress 键盘按住 键码:keycode
只给能在页面上选中的元素(表单元素) 和 document 来绑定键盘事件
注:不能给页面上一个 div 元素,设置键盘事件的
确定按键
//直接按enter就提交了
<h2>键盘事件</h2>
<script>
document.onkeyup = function(e){
e = e || window.event // 兼容ie
var keyCode = e.keyCode || e.which // 兼容FireFox2.0
if(e.keyCode ===13){ //13是enter键
alert('登录成功')
}
}
</script>
常见的键盘码(了解)
8: 删除键(delete)
9: 制表符(tab)
13: 回车键(enter)
16: 上档键(shift)
17: ctrl 键
18: alt 键
27: 取消键(esc)
32: 空格键(space)
案例–回车事件
<h2>键盘事件</h2>
<script>
document.onkeydown = function(e){
e = e || window.event // 兼容ie
var keyCode = e.keyCode || e.which || e.charCode; // 兼容FireFox2.0
if(e.keyCode ===13){ //13是enter键
location.href='跳转的网址'
}
}
</script>
组合按键
组合案件最主要的就是 alt / shift / ctrl 三个按键
在我点击某一个按键的时候判断一下这三个键有没有按下,有就是组合了,没有就是没有组合
浏览器事件
load : 页面全部资源加载完毕 后执行
<script>
window.onload = function () {
// 这里面的代码,是html文档加载完成之后执行
var divEle = document.querySelector('div')
console.log(divEle)
divEle.innerHTML = '新内容'
}
</script>
<body>
<div>元素一</div>
scroll :浏览器滚动的时候触发
// 浏览器窗口滚动事件
window.onscroll = function(){
console.log(document.documentElement.scrollTop)
}
resize:浏览器窗口尺寸改变事件
// 浏览器窗口尺寸改变事件 - 移动端适配
window.onresize = function(){
// 显示窗口尺寸
console.log('w ',window.innerWidth, ' h ',window.innerHeight)
}
触摸事件
- touchstart 触摸按下开始事件
- touchmove 触摸抬起结束事件
- touchend 触摸移动事件
div{
width: 100%;
height: 100vh;
background-color: skyblue;
}
</style>
</head>
<body>
<div>元素一</div>
<script>
document.ontouchstart = function(){
console.log('touchstart');
}
document.ontouchmove = function(){
console.log('touchmove');
}
document.ontouchend = function(){
console.log('touchend');
}
</script>
事件的绑定方式
- DOM0级
一、把事件写在标签的属性里面
<input type="button" onclick="alertMessage()" value="按钮"/>
好处:大家都会,几乎所有的浏览器都支持
坏处:夹杂在HTML代码中,代码不简洁;
这种事件写法效率不高;不符合“行为,样式,结构”相分离。
二、事件赋值式DOM0
缺点:
只能给一个元素注册一个事件,绑定一个事件,后一个会覆盖前一个
好处:
符合“行为,样式,结构”相分离;便于操作当事对象;方便读取事件对象
- 事件监听DOM2
使用方法:addEventListener 非 IE 7 8 下使用,默认冒泡:false;捕获:true;
attachEvent :IE 7 8 下使用
兼容性和区别:
事件解绑
- 1、解绑赋值式绑定事件
btn.onclick = null - 2、解绑事件监听
btn.removeEventListener(‘click’,事件处理函数名)
需如下方式写才能找到函数名,些许麻烦
事件对象event
-
是什么?处理事件,与事件相关,包含对事件的描述信息
-
每触发一个事件都会自动生成事件对象
window–打开浏览器窗口生成window对象 -
获取事件对象
在事件处理函数中获取名为 event
更改事件对象名
事件处理函数定义一个形参,接收事件对象
-
事件对象兼容性
IE浏览器兼容写法
window.event
事件对象位置属性
- 相对自身的(你点击的元素的边框内)offsetX,offsetY
- 相对于浏览器窗口(clientX,clientY) 滚动时,浏览器窗口原点时固定的
- 相对于页面(pageX,pageY) 原点是没滚动前的,滚动时,原点会被卷在窗口里,这是y就会变化
![4. !](https://img-blog.csdnimg.cn/5dfce6971fed43b38a58a333e9232502.png)
事件目标对象target
target 这个属性是事件对象里面的属性,表示你点击的目标(类似this,但this在事件处理函数中表示事件源)
当你触发点击事件的时候,你点击在哪个元素上,target 就是哪个元素
这个 target 也不兼容,在 IE 下要使用 srcElement
事件传播
即触发子元素,父元素也会触发相同事件
- 嵌套的元素,事件会传播(继承)
- 传播方向由内往外传,即事件冒泡方式(默认传播行为)false
由外向内传播,叫做事件捕获,true - 事件监听第三个参数设置true表示事件捕获,默认false表示事件冒泡
事件传播三个阶段:
捕获阶段 + 目标对象 + 冒泡阶段
事件捕获:
<div>
<h4>
<p></p>
</h4>
</div>
<script>
/*
分别给p元素和div元素绑定click事件, 点击p标签,div会不会触发事件呢?
*/
var divEle = document.querySelector('div')
var pEle = document.querySelector('p')
var h4Ele = document.querySelector('h4')
divEle.addEventListener('click', function (e) {
e = e || window.event // 事件对象兼容
var target = e.target || e.srcElement // 事件目标对象兼容
console.log('div元素 ', target)
},true)
pEle.addEventListener('click', function (e) {
console.log('p元素')
},true)
h4Ele.addEventListener('click', function (e) {
e.stopPropagation?e.stopPropagation():e.cancelBubble = true // 阻止事件传播
console.log('h4元素')
},true)
</script>
阻止事件传播
e.stopPropagation() 标准浏览器 e.cancelBubble=true IE低版本
兼容性写法:e.stopPropagation?e.stopPropagation():e.cancelBubble = true
事件原型图
事件委托
事件代理(委托) 利用冒泡实现
事件委托使用场景
当要给多个元素循环绑定事件的时候可以考虑使用事件委托简化操作
注: 不支持事件冒泡的不能使用 如:焦点事件
好处:
减少了事件绑定的数量;
对后来动态创建的元素依然有效,
解决动态添加的元素节点无法绑定事件的问题
缺点:
事件委托基于冒泡,对于不冒泡的事件不支持。层级过多,冒泡过程中,可能会被某层阻止掉。
理论上委托会导致浏览器频繁调用处理函数,虽然很可能不需要处理。所以建议就近委托,比如在table上代理td,而不是在document上代理td。
把所有事件都用代理就可能会出现事件误判。比如,在document中代理了所有button的click事件,另外的人在引用改js时,可能不知道,造成单击button触发了两个click事件。
<body>
<ul>
<li>张同学</li>
<li>李同学</li>
<li>王同学</li>
</ul>
<script>
/*
循环遍历所有同学,绑定收外买这个事情
自己的事情自己处理,没有使用代理
*/
function test1() {
var liEles = document.querySelectorAll('li')
for (var i = 0; i < liEles.length; i++) {
liEles[i].addEventListener('click', function () {
alert(this.innerHTML + '收到外买')
})
}
}
/**
* 每个同学收外买的事情,交给ul代理接收
*/
function test2(){
var ulEle = document.querySelector('ul')
ulEle.addEventListener('click',function(e){
e = e || window.event // 事件对象
var target = e.target || e.srcElement // 事件目标对象
alert(target.innerHTML + '代收到外买成功')
})
}
test2()
</script>
todoList3.0.事件代理处理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>todoList</title>
<!--
todoList1.0
节点操作 了解
todoLiST2.0
数据操作 重点
todoList3.0
事件委托改造事件
-->
</head>
<body>
<div class="container">
<input type="text" placeholder="请输入内容" /><button>确定</button>
<ul>
</ul>
</div>
<script>
var arr = ['html','css'] // 数据
/*
数据操作-实现显示列表
遍历数组,拼接字符串,将字符串作用内容设置给显示元素
点击确定按钮,获取输入框内容,添加数组
*/
function showList() {
var liArr = arr.map(function (item,index) {
return `<li data-index="${index}">${item}</li>`
})
var liStr = liArr.join('')
var ulEle = document.querySelector('ul')
ulEle.innerHTML = liStr
}
// 删除元素
function onDelete(){
// var liEles = document.querySelectorAll('ul>li')
// //循环遍历所有li绑定点击事件
// for(var i = 0; i < liEles.length; i++){
// var liEle = liEles[i]
// liEle.onclick = function(){
// //删除数据
// // [10,20,30,40]
// // arr.splice(index,1)
// var index = this.dataset.index
// arr.splice(index,1)
// showList() // 刷新
// }
// }
// 删除事件操作委托给ul上级元素处理
var ulEle = document.querySelector('ul')
ulEle.addEventListener('click',function(e){
e = e || window.event
var target = e.target || e.srcElement // 事件目标对象
var index = target.dataset.index
arr.splice(index,1)
showList()
})
}
//添加元素
var btn = document.querySelector('button')
btn.onclick = function () {
var inputEle = document.querySelector('input')
var inputValue = inputEle.value
arr.push(inputValue)
inputEle.value = '' // 清空输入框
showList() // 刷新
}
showList() // 初始化执行
onDelete() // 初始化执行
</script>
</body>
</html>
选项卡的事件委托处理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>选项卡</title>
<style>
* {
padding: 0;
margin: 0;
}
ul,
li {
list-style: none;
}
.container {
width: 400px;
margin: 100px;
}
.container ul {
height: 50px;
background-color: skyblue;
text-align: center;
line-height: 50px;
display: flex;
}
.container ul li {
flex: 1;
border-right: 1px solid pink;
}
.container ul li:hover {
cursor: pointer; /*触摸小手*/
}
.container ol {
position: relative;
height: 300px;
background-color: blueviolet;
}
.container ol li {
position: absolute;
top: 0;
left: 0;
text-align: center;
width: 400px;
line-height: 300px;
display: none;
}
.container ul .active {
background-color: rgb(223, 57, 16);
}
.container ol .on {
display: block;
}
</style>
</head>
<body>
<div class="container">
<ul>
<li class="active">选项1</li>
<li>选项2</li>
<li>选项3</li>
</ul>
<ol>
<li class="on">区域1</li>
<li>区域2</li>
<li>区域3</li>
</ol>
</div>
<script>
var ulLis = document.querySelectorAll('ul>li')
var olLis = document.querySelectorAll('ol>li')
// 循环给所有选项绑定事件
for (var i = 0; i < ulLis.length; i++) {
ulLis[i].setAttribute('index',i) // 动态添加索引号
ulLis[i].addEventListener('click',function(){
// 1. 清除所有样式
clearStyle()
// 2. 当前选设置样式
this.className = 'active'
// 3. 确定当前选项对应的区域
var index = this.getAttribute('index') //取到当前索引号
olLis[index].className = 'on' //索引号给ol,将ul和ol对应起来
})
}
/**
* 清除样式
*/
function clearStyle(){
for (var i = 0; i < ulLis.length; i++) {
ulLis[i].className = '' // 清除选项样式
olLis[i].className = '' //清除区域样式
}
}
</script>
</body>
</html>
默认行为 e.preventDefault() : 非 IE e.returnValue = false :IE
默认行为,就是不用我们注册,它自己就存在的事情
比如我们点击鼠标右键的时候,会自动弹出一个菜单
比如我们点击 a 标签的时候,我们不需要注册点击事件,他自己就会跳转页面
这些不需要我们注册就能实现的事情,我们叫做 默认事件
- a标签 默认行为 href执行链接跳转 这种也可以 确定 href=‘#’ 有些不兼容
- form表单action
- 右键事件 contextmenu
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>默认行为</title>
</head>
<body>
<a href="">按钮</a>
<a href="javascript:void(0)" >确定</a>
<div>
<form>
<input type="text" placeholder="请输入内容" name="message"><br>
<input type="submit" value="提交">
</form>
</div>
<script>
function test1() {
var aEle = document.querySelector('a')
aEle.addEventListener('click', function (e) {
e = e || window.event // 事件对象
e.preventDefault() // 阻止a标签默认行为
alert('事件监听a标签')
})
}
function test2(){
var formEle = document.querySelector('form')
formEle.addEventListener('submit',function(e){
e = e || window.event // 事件对象
e.preventDefault() // 阻止a标签默认行为
alert('表单提交事件')
})
}
// test2()
function test3(){
document.addEventListener('contextmenu',function(e){
e = e || window.event // 事件对象
e.preventDefault() // 阻止a标签默认行为
alert('右键事件')
})
}
test3()
</script>
</body>
</html>