1. 事件监听
1.1 事件监听语法
dom.addEventListener('click', function(){})
事件监听三要素
- 事件源:谁,哪个元素触发的事件 btn
- 事件类型:触发的是什么事件 click
- 事件处理程序:当事件触发的时候,我们要做什么
javascript
// 获取元素
const btn = document.querySelector('.btn')
btn.addEventListener('click', function(){
// 当我们点击了按钮之后,写我们要做什么事情
alert('秋香')
})
// 注意:这个函数,一开始是不执行的,当事件触发的时候,才会去执行。
// 回调函数: 回头再调用的函数
1.2 回调函数
把一个函数当做参数传入到另一个函数里面,那么这个函数就叫做回调函数 简单理解==> 回头再调用的函数。 英文:callback ===> 简写 cb
javascript
// 本质上:回调函数就是函数,只是作为函数的参数来使用了。
// 回调函数什么时候执行,当满足某些条件的时候才会执行 => 回调函数
// 应用场景:
// 1. 定时器里面
const cb = function(){
console.log('你礼貌吗')
}
setInterval(cb, 1000)
// 2. 事件监听、注册、绑定
const btn = document.querySelector('button')
btn.addEventListener('click', function(){
console.log('我很礼貌')
})
eg练习
javascript
<div class="pop">
<a href="javascript:;" class="close"></a>
</div>
<script>
// 点击关闭按钮可以关闭父盒子
// 获取元素
const closeBtn = document.querySelector('.close')
// 要关掉是父盒子
const box = document.querySelector('.pop')
// 注册事件
closeBtn.addEventListener('click', function(){
// 事件处理程序
box.style.display = 'none'
})
</script>
1.3 事件监听的版本
DOM 0 ==> onclick DOM 2 ==> addEventListener
javascript
// DOM L0 标准 老的,以前,
const btn = document.querySelector('button')
// btn.onclick = function(){
// console.log('饿了吗')
// }
// btn.onclick = function(){
// console.log('我饿了一点')
// }
btn.onclick = function(){
}
// L0 缺陷:同一个元素,注册了同一个事件,后一个事件会覆盖前面的一个
// L2 : 对同一个元素,注册相同的事件,会按注册顺序,依次触发
btn.addEventListener('click', function(){
console.log('我就不下课呢')
})
btn.addEventListener('click', function(){
console.log('下课了')
})
2. 事件类型
2.1 鼠标事件
click 点击 mouseenter / mouseleave 鼠标经过、离开 没有冒泡 mouseover / mouseout 鼠标经过 、离开 有事件冒泡 mousemove 鼠标移动
javascript
<style>
.box {
width: 200px;
height: 200px;
background-color: pink;
margin: 100px auto;
transition: all 0.5s;
}
.active{
transform: rotate(360deg);
background-color: orange;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
// 鼠标事件类型
// 1. 先获取事件源
const box = document.querySelector('.box')
// 2. 注册事件
box.addEventListener('click', function(){
console.log('我偷偷的点击了一下盒子')
})
// 鼠标经过事件 mouseenter
box.addEventListener('mouseenter', function(){
// 让box旋转
box.classList.add('active')
console.log('轻轻地我来了')
})
// 鼠标离开 mouseleave
box.addEventListener('mouseleave', function(){
box.classList.remove('active')
console.log('轻轻地我走了,不带走一片云彩')
})
// 鼠标一直动 mousemove
box.addEventListener('mousemove', function(){
console.log('我走来走去,停不下来')
})
// 需求:当鼠标经过这个盒子,让盒子旋转360度,加一个过渡动画
// 离开的时候,再转回去
</script>
eg.轮播图 点击版本
javascript
// 初始数据
const arr = [
{ url: './images/slider01.jpg', title: '奔涌吧,后浪!', color: 'rgb(37, 41, 60)' },
{ url: './images/slider02.jpg', title: '开启剑与雪的黑暗传说!', color: 'rgb(43, 35, 26)' },
{ url: './images/slider03.jpg', title: '八年的怀旧游戏', color: 'rgb(47, 23, 100)' },
{ url: './images/slider04.jpg', title: '真正的jo厨出现了!', color: 'rgb(36, 31, 33)' },
{ url: './images/slider05.jpg', title: '让世界通过B站看到东方大国文化', color: 'rgb(58, 91, 216)' },
{ url: './images/slider06.jpg', title: '快来分享你的寒假日常吧~', color: 'rgb(67, 90, 92)' },
{ url: './images/slider07.jpg', title: '哔哩哔哩小年YEAH', color: 'rgb(166, 131, 143)' },
{ url: './images/slider08.jpg', title: '一站式解决你的电脑配置问题!!!', color: 'rgb(53, 29, 25)' },
]
// 需求,点击右侧按钮,无缝切换图片
// 声明一个变量 i
let i = 0
// 获取元素
const img = document.querySelector('.slider-wrapper img')
const p = document.querySelector('.slider-footer p')
const footer = document.querySelector('.slider-footer')
console.log(img , p, footer)
const next = document.querySelector('.next')
next.addEventListener('click', function(){
// 切换图片
i++
// i = i >= arr.length ? 0 : i
i = i % arr.length // 取余的方式
render()
})
// 需求2.点击左侧按钮,让图片倒序播放 i--
const prev = document.querySelector('.prev')
prev.addEventListener('click', function(){
// 切换图片
i--
i = i < 0 ? arr.length - 1 : i
render()
})
// 需求3. 抽离公共部分的逻辑,封装到render函数中
const render = function(){
// 更换图片,文字,背景色,小圆点
img.src = arr[i].url
p.innerHTML = arr[i].title
footer.style.backgroundColor = arr[i].color
// 小圆点
// 先找到带有active类名的li标签,去掉类名
document.querySelector('.slider-indicator .active').classList.remove('active')
// 给当前的那个li添加active
// 当i等于1的时候,应该给第二个li标签添加类名
document.querySelector(`.slider-indicator li:nth-child(${i + 1})`).classList.add('active')
}
// 需求4,开启定时器,自动轮播
// ==> 每隔一秒,让程序自动模拟用户点击右侧按钮
let timerId = setInterval(function(){
next.click()
// 模拟用户点击事件,自动触发点击事件
// click / focus / blur
}, 1000)
// 需求5,当鼠标经过大盒子的时候,关掉定时器
const box = document.querySelector('.slider')
box.addEventListener('mouseenter', function(){
clearInterval(timerId)
})
// 鼠标离开大盒子的时候,重新开启定时器
box.addEventListener('mouseleave', function(){
timerId = setInterval(function(){
next.click()
}, 1000)
})
2.2 表单焦点事件
focus 获取焦点 blur 失去焦点
javascript
<input type="text" class="search-text">
<input type="text" class="search">
<script>
// 1. 焦点事件(手动触发)
// 1.1 获得焦点 focus
const iptLeft = document.querySelector('.search-text')
iptLeft.addEventListener('focus', function(){
console.log('获取了焦点')
})
// 1.2 失去焦点 blur
iptLeft.addEventListener('blur', function(){
console.log('失去了焦点')
})
// 2.自动触发获取焦点和失去焦点
const iptRight = document.querySelector('.search')
// 自动获取焦点 dom.focus()
// 自动失去焦点 dom.blur()
// 自动触发点击事件 dom.click()
iptRight.focus()
setInterval(function(){
iptRight.blur()
}, 2000)
</script>
eg.小米搜索框显示隐藏
javascript
<div class="mi">
<input type="search" placeholder="小米笔记本" class="search-text">
<ul class="result-list">
<li><a href="#">全部商品</a></li>
<li><a href="#">小米11</a></li>
<li><a href="#">小米10S</a></li>
<li><a href="#">小米笔记本</a></li>
<li><a href="#">小米手机</a></li>
<li><a href="#">黑鲨4</a></li>
<li><a href="#">空调</a></li>
</ul>
</div>
<script>
// 小米搜索框显示隐藏案例
// 1. 先隐藏我们的下拉菜单
const input = document.querySelector('.search-text')
const ul = document.querySelector('.result-list')
// 2. 获得焦点,显示下拉菜单,并且修改搜索框的边框颜色
input.addEventListener('focus', function(){
// 1. 显示下拉菜单
ul.style.display = 'block'
// 2. 加上边框颜色
input.classList.add('search')
})
// 3. 失去焦点,隐藏下拉菜单,并且修改搜索框的边框颜色为原先的灰色
input.addEventListener('blur', function(){
// 1. 显示下拉菜单
ul.style.display = 'none'
// 2. 加上边框颜色
input.classList.remove('search')
})
</script>
2.3 键盘事件
keydown 按下键盘 keyup 键盘抬起 input 用户输入
javascript
<textarea id="tx" placeholder="发一条友善的评论" rows="2"></textarea>
<script>
// 获取元素
const tx = document.querySelector('#tx')
// 1. 键盘事件
// 1.1 键盘按下事件 keydown 当我们按下键盘的时候就触发
// 注意:keydown的回调函数,在我们输入的内容还没有落入文本框的时候,就已经执行了
// 比如,输入a,a这个字母,还没有落入文本框,监听keydown的这个回调函数就已经执行了
// 打印的是a之前input框存的value值
tx.addEventListener('keydown', function(){
// console.log('键盘按下了')
console.log(tx.value)
})
// 1.2 键盘弹起事件 keyup 当我们键盘弹起的时候就触发
tx.addEventListener('keyup', function(){
console.log('键盘抬起了')
})
// 2. 用户输入事件 input ,是表单value的值发生变化的时候触发
tx.addEventListener('input', function(){
console.log(tx.value)
})
// 3. 注意事项
// 1. 执行顺序 keydown ==> input ==> keyup
// 2. keydown输入,我们打印文本域中的值,只能得到上一次输入的内容,
// input和keyup可以得到输入的内容
eg.统计用户输入字数
javascript
// 需求1: 文本域获得焦点则让 total 透明度改为1,失去焦点则改为0
const tx = document.querySelector('#tx')
const total = document.querySelector('.total')
tx.addEventListener('focus', function(){
total.style.opacity = 1
})
tx.addEventListener('blur', function(){
total.style.opacity = 0
})
// 需求2: 得到用户输入的字符长度,写到total盒子里面
tx.addEventListener('input', function(){
console.log(tx.value)
console.log(tx.value.length) // 可以获取到用户输入字符串的长度
total.innerHTML = `${tx.value.length}/200字`
})
3. 事件对象 e
当事件发生的时候, 和这个事件相关的所有信息, 都存在这个对象中. 这个对象就叫做事件对象
书写位置:
- 回调函数的第一个参数
常用属性
- e.type => 事件类型
- e.target => 触发事件的元素
- e.offsetX, e.offsetY => 点击鼠标相对于盒子的x,y坐标
- e.key => 针对键盘事件,判断按下的哪个键
javascript
<div class="box"></div>
<textarea id="tx" placeholder="发一条友善的评论" rows="2"></textarea>
const box = document.querySelector('.box')
box.addEventListener('click', function(e){
console.log(e.offsetX, e.offsetY) // 点击鼠标相对于盒子的x,y坐标
console.log(e.target) // 事件触发的元素
console.log(e.type) // 返回的是事件的类型
})
// 文本域
const tx = document.querySelector('#tx')
// 注意:如果要使用事件对象,一定不要忘了写e
tx.addEventListener('keyup', function(e){
// e.key 针对键盘事件,判断用户按下了哪一个键
if (e.key === 'Enter'){
console.log('按下了回车键')
}
})
eg.回车发布评论
javascript
// 需求1: 文本域获得焦点则让 total 透明度改为1,失去焦点则改为0
const tx = document.querySelector('#tx')
const total = document.querySelector('.total')
tx.addEventListener('focus', function(){
total.style.opacity = 1
})
tx.addEventListener('blur', function(){
total.style.opacity = 0
})
// 需求2: 得到用户输入的字符长度,写到total盒子里面
tx.addEventListener('input', function(){
console.log(tx.value)
console.log(tx.value.length) // 可以获取到用户输入字符串的长度
total.innerHTML = `${tx.value.length}/200字`
})
// 需求3: 用户按下回车,可以发表评论
const item = document.querySelector('.item')
const text = document.querySelector('.text')
tx.addEventListener('keyup', function(e){
// 这里是回调函数,当我们注册事件的时候,这个里面的代码
// 是不执行的,当键盘抬起并且按的是enter键的时候才执行
if(e.key === 'Enter'){
btn.click() // 自动触发
// 为什么可以取到btn?
}
})
// 需求4: 点击发布按钮可以发布评论
const btn = document.querySelector('.wrapper button')
btn.addEventListener('click', function(){
// 1. 显示下面的评论
item.style.display = 'flex'
// 2. 把输入的内容放到text中
text.innerHTML = tx.value
// 清空文本域中的文字
tx.value = ''
// 文字复原 0/200
total.innerHTML = `0/200字`
})
4. this
javascript
// this 粗略规则: 谁调用函数,this就指向谁
// this: 是JS程序运行时自动生成的一个对象,和执行环境有关。(黑马叫做环境对象)
// 实际开发 我们只关心 this 指向谁? 指向 ==> 可以理解为等于或者说代表谁
// 1. 全局环境中 this指向window
console.log(this)
// 2. 普通函数调用时,this指向的还是window
function fn(){
console.log(this)
}
fn()
// window.fn() ==> 省略了window
// 3. 对象的方法 this指向谁?
// 函数/方法
const obj = {
name:'测试',
fn:function(){
console.log(this)
}
}
// 粗略规则:谁调用fn,fn里面的this就指向谁
obj.fn() // obj.fn() => 相当于是obj这个对象调用的fn
// 4. 事件绑定的时候 this指向绑定事件的元素,事件源
const btn = document.querySelector('button')
btn.addEventListener('click', function(){
console.log(this)
})
// 粗略规则:谁调用,this指向谁
// 5. 定时器中,this指向window
setInterval(function(){
console.log(this)
}, 1000)
// this 面试题中80%都会考到
// this是一个关键字,是一个对象,指向 => 等于 => 代表
e.target 和 this
javascript
// 事件对象:当事件触发的时候,和这个事件相关的所有信息,都存到这个对象中
// event ev, e => e
// e里面有很多常见的属性
// 1. e.type ==> 事件类型
// 2. e.target ==> 触发事件的元素
// 3. e.offsetX , e.offsetY => 相对父盒子的一个坐标
// 4. 针对键盘事件 e.key => 判断用户按下了哪个键
// 书写位置 ==> 回调函数的第一个参数
// 事件注册的时候,this指向的是绑定事件的元素,事件源
// 1. this 和 e.target 一样的情况
const btn = document.querySelector('button')
btn.addEventListener('click', function(e){
console.log(this)
console.log(e.target)
})
// 2. this 和 e.target 不一样的情况
const ul = document.querySelector('ul')
ul.addEventListener('click', function(e){
// console.log(this) // 事件源,给谁绑定,this就指向谁
console.log(e.target) // 触发事件的元素,我点的是谁,就是谁
})
// this 指向的是绑定事件的元素,事件源
// e.target 触发事件的元素
5. 排他思想
javascript
<button>click me</button>
<button>click me</button>
<button>click me</button>
<button>click me</button>
<button>click me</button>
<script>
// 排它思想
// 1. 精确的排他,精确的找到某个带有active类名的元素,移出类名,给自己添加
// 2. 循环遍历,干掉其他所有人,再给自己添加样式
// 需求,点击按钮,让被点击的哪个按钮,字体颜色变化,其余的不变
const btns = document.querySelectorAll('button')
for(let i = 0; i < btns.length; i++){
btns[i].addEventListener('click', function(){
// 排他思想
// 1.干掉其他所有人,
for(let i = 0; i < btns.length; i++){
btns[i].style.color = 'black'
}
// 2. 给自己添加
this.style.color = 'orange'
})
}
</script>
javascript
<button class="pink">按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<script>
// 需求: 点击哪个按钮,哪个按钮高亮,其余不高亮
// 1. 获取所有的按钮
const btns = document.querySelectorAll('button')
// 2. 给每一个按钮都要绑定事件
// btns[0].addEventListener('click', function(){})
// btns[1].addEventListener('click', function(){})
// btns[2].addEventListener('click', function(){})
// btns[3].addEventListener('click', function(){})
// btns[4].addEventListener('click', function(){})
// let声明的
for(let i = 0; i < btns.length; i++){
btns[i].addEventListener('click', function(){
// 排他思想:
// 1. 先干掉其他人
// 先找到带有高亮类名的哪个元素,移出类名
document.querySelector('.pink').classList.remove('pink')
// 2. 留下我自己 点击了谁,让谁添加类名
this.classList.add('pink')
})
}
</script>
6. tab栏切换
- 不是事件委托的版本,之后不建议使用这版了哈
javascript
// 1. 获取5个a标签
const as = document.querySelectorAll('.tab-nav a')
// 获取所有的items
const items = document.querySelectorAll('.tab-content .item')
console.log(as)
// 2. 循环遍历注册事件 mouseenter
for(let i = 0; i < as.length; i++){
as[i].addEventListener('mouseenter', function(){
// 3. 上面tab选项卡的高亮
// 排他思想
document.querySelector('.tab-nav .active').classList.remove('active')
// 再给自己添加 this指向的是事件源,给谁绑定的,就指向谁
this.classList.add('active')
// 4. 下面大盒子的排他
// 找到带有active类名的那个盒子,移除类名
document.querySelector('.tab-content .active').classList.remove('active')
// this 不可以,this指向的是a标签,我们要类名为item的div加active
console.log(i)
items[i].classList.add('active')
})
}