Skip to content

1. 事件监听

1.1 事件监听语法

dom.addEventListener('click', function(){})

事件监听三要素

  1. 事件源:谁,哪个元素触发的事件 btn
  2. 事件类型:触发的是什么事件 click
  3. 事件处理程序:当事件触发的时候,我们要做什么
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')
  })
}