笔记十一、JavaScript——js事件

140 阅读10分钟

js事件

  1. 什么是事件? 一个事件的组成:
  • 触发谁的事件: 事件源
  • 触发什么事件: 事件类型
  • 触发后做什么: 事件处理函数 btn.onclick = function () {} 分析:
  • 触发谁的事件 -> btn -> 事件源就是 btn
  • 触发什么事件 -> onclick -> 事件类型就是 click
  • 触发后做什么事 -> function () {} -> 这个事件的处理函数
  1. 常见的事件(了解)
  • 浏览器事件
    • onload: 页面全部资源加载完毕
    • onscroll: 浏览器页面滚动的时候
  • 鼠标事件
  • 键盘事件
  • 表单事件

1. 鼠标事件

<style>
    * {
        padding: 0;
        margin: 0;
    }
    div {
        width: 400px;
        height: 400px;
        background-color: pink;
    }
    .sBox {
        width: 100px;
        height: 100px;
        background-color: green;
    }
</style>

<div>
    <div class="sBox"></div>
</div>

// 0. 获取元素
var oDiv = document.querySelector('div')

// 1. 左键单击
oDiv.onclick = function () {
    console.log('单击元素时触发')
}
// 2. 双击事件          300ms 内连续点击两次鼠标
oDiv.ondblclick = function () {
    console.log('双击元素时触发')
}

// 3. 右键事件
oDiv.oncontextmenu = function () {
    console.log('鼠标右键单击时触发')
}

// 4. 鼠标按下事件      鼠标左键按下的时候触发(哪怕鼠标没有抬起也会触发)
oDiv.onmousedown = function () {
    console.log('鼠标按下时触发')
}

// 5. 鼠标抬起事件      鼠标左键抬起的时候触发
oDiv.onmouseup = function () {
    console.log('鼠标抬起时触发')
}

// 6. 鼠标移入事件      鼠标移入元素的时候触发      注意: 移入子级盒子时, 也会触发
oDiv.onmouseover = function () {
     console.log('onmouseover 移入事件触发')
}
// 7. 鼠标移出事件      鼠标移出元素的时候触发      注意: 移入子级盒子, 也会触发
oDiv.onmouseout = function () {
     console.log('onmouseout 移出事件触发')
}

// 8. 鼠标移入事件2     鼠标移入元素的时候触发      注意: 移入子级盒子时, 并不会触发
oDiv.onmouseenter = function () {
    console.log('onmouseenter 移入事件触发')
}
// 9. 鼠标移出事件2     鼠标移出元素的时候触发      注意: 移出鼠标到子盒子时并不会触发
oDiv.onmouseleave = function () {
    console.log('onmouseleave 移出事件触发')
}

// 10. 鼠标移动事件     鼠标在 oDiv 元素内部 移动的时候会触发
oDiv.onmousemove = function () {
    console.log('鼠标移动事件触发~~~')
}

2. 键盘事件

键盘事件

    1. document 当前文档
    1. input 输入框
// 1. 键盘抬起事件
document.onkeyup = function () {
    /console.log('任意按下一个按键然后抬起时会触发')
}

// 2. 键盘按下事件
document.onkeydown = function () {
    console.log('键盘任意一个按键被按下')
}

// 3. 键盘按下抬起事件
document.onkeypress = function () {
    console.log('键盘任意按键按下抬起时触发')
}

3. 表单事件

<input type="text">
<script>
// 0. 获取元素
var inp = document.querySelector('input')

// 1. 获得焦点事件
inp.onfocus = function () {
    console.log('当前文本框获得焦点')
}

// 2. 失去焦点事件
inp.onblur = function () {
    console.log('当前文本框失去焦点')
}

// 3. 文本框内容改变时触发
inp.onchange = function () {
    console.log('当前文本框内容发生改变')
}

// 4. 文本框输入内容时触发
inp.oninput = function () {
    console.log('当前文本框正在输入内容')
}
</script>

4. 选项卡案例

5. 事件对象

  • 什么是事件对象:当触发一个事件以后, 对该事件的一些描述信息,比如:=> 鼠标点击时的坐标=> 触发键盘事件时按下的那个按键,每一个事件都会有一个对象来描述这信息, 我们就把这个对象叫做事件对象
  • 浏览器给我们一个 "黑盒子", 叫做 window.event 就是对事件信息的所有描述
  • 语法: 元素.onclick = function () { console.log(window.event.X轴的坐标点信息) console.log(window.event.Y轴的坐标点信息) }
  • 缺点: 这个东西有兼容性问题, 在 低版本的 IE 里边能用, 但是在高版本 IE 和 chrome 里边是不好使 所以我们需要换一种使用方式:就是在每一个事件处理函数的形参位置, 默认第一个就是事件对象

6. 点击事件光标位置的获取

<style>
    body {
        height: 5000px;
    }
    div {
        width: 300px;
        height: 300px;
        padding: 20px;
        border: 10px solid #333;
        margin: 50px;
        position: fixed;
        top: 300px;
    }
</style>

<div></div>

<script>
/**
 *  点击事件的光标位置
 * 
 *      坐标一定是一个相对的数据
 * 
 *      比如:
 *          => 相对于事件源 (就是我们点击的那个元素)
 *          => 相对于页面
 *          => 相对于浏览器窗口
*/

// 0. 获取元素
var box = document.querySelector('div')

box.onclick = function (e) {
    // 1. 相对于事件源 的鼠标坐标点     相对于点击的元素的边框内测开始计算
    console.log('==========================')
    console.log('相对于事件源 X 轴', e.offsetX)
    console.log('相对于事件源 Y 轴', e.offsetY)

// 2. 相对于页面
console.log('==========================')
console.log('相对于页面 X 轴', e.pageX)
console.log('相对于页面 Y 轴', e.pageY)

// 3. 相对于浏览器窗口
console.log('==========================')
console.log('相对于浏览器窗口 X 轴', e.clientX)
console.log('相对于浏览器窗口 Y 轴', e.clientY)
}
</script>

7. 获取键盘按键

键盘的每一个按键都有一个自己的编码,我们可以通过 事件对象.keyCode 来获取,e.keyCode已经被移除了, 但是很多主流浏览器还支持使用,现在推荐我们使用: e.key

<input type="text">

// 0. 获取元素
var inp = document.querySelector('input')
inp.onkeyup = function (e) {
    // console.log(e.ctrlKey)
    // console.log(e.altKey)
    // console.log(e.keyCode)  // q === 81

    if (e.altKey && e.keyCode === 81) {
        console.log('按下了组合按键 alt + q')
    }
}

8. onXXX绑定事件的弊端

  • 问题:
  • 我们现在注册事件就是通过 onXXX,只能给元素注册一个事件, 如果写了第二个, 那么第一个就会被覆盖掉
  • 解决:
  • 如果想要两个事件全都存在, 我们可以使用 事件监听的方式去给元素绑定事件,使用 addEventListener 去给元素通过事件监听的方式,在 IE 中需要使用 attachEvent (了解即可)
<div></div>

// 0. 获取元素
var box = document.querySelector('div')

// 1. 给 box 元素添加一个点击事件
box.onclick = function () {
    console.log('点击事件 1, 触发了~~~')
}

// 2. 给 box 元素添加第二个点击事件
box.onclick = function () {
    console.log('点击事件 2, 触发了~~~')
}

9. 事件监听

addEventListener

  • 语法: 元素.addEventListener('事件类型', 事件处理函数, 第三个形参)
  • 第三个形参先欠着, 后续会详细讲解, 因为有默认值, 所以我们暂时先不传递第三个形参
  • 注意: 这里的事件类型, 全部都一样不需要加 on
  • 执行顺序会按照我们的注册的顺序执行(也就是代码书写的顺序)
<div></div>

// 0. 获取元素
var box = document.querySelector('div')
box.addEventListener('click', function () {
    console.log('绑定的第一个事件')
})

box.addEventListener('click', function () {
    console.log('绑定的第三个事件')
})

box.addEventListener('click', function () {
    console.log('绑定的第二个事件')
})

10. 渲染表格案例

需求:

    1. 接收到后端给到的数据
    1. 将数据渲染到页面 思路:
    1. 将数组内的数据, 转换为我们需要的字符串
    1. 通过 innerHTML 插入到页面渲染出来
      • `` 包裹起来的内容和 '' 包裹起来的一样, 都是一个字符串
      • 两者的区别:
        • ``可以换行, ''不能换行
        • `` 内部 书写 ${} 这个大括号内部可以书写变量
//html:
<table>
    <thead>
        <tr>
            <th>学号</th>
            <th>姓名</th>
            <th>性别</th>
            <th>年龄</th>
            <th>班级</th>
        </tr>
    </thead>
    <tbody>
    </tbody>
</table>

//script:
// 1. 写一个数组, 模拟接收到了后端给到的数据
var users = [
    {
        id: 1,
        name: '张三',
        age: 67,
        gender: '男',
        classRoom: '2XX'
    },
    {
        id: 2,
        name: '李四',
        age: 66,
        gender: '女',
        classRoom: '2XX'
    },
    {
        id: 3,
        name: '王五',
        age: 18,
        gender: '男',
        classRoom: '2XX'
    }
]
// 0. 获取元素
var tBodyEle = document.querySelector('tbody')

// 1. 将数组内的数据, 转换为我们需要的字符串
var str = ''
users.forEach(function (item) {
    // console.log(item)
    // str += '<tr><td>1</td><td>张三1</td><td>男</td><td>67</td><td>2XX</td></tr>'
    str += `<tr>
        <td> ${item.id} </td>
        <td> ${item.name} </td>
        <td> ${item.gender} </td>
        <td> ${item.age} </td>
        <td> ${item.classRoom} </td>
    </tr>`
})

// 2. 通过 innerHTML 插入到页面渲染
tBodyEle.innerHTML = str

11. 事件传播

假设我们在浏览器内用一个宽高 500px盒子, 内部有一个水平垂直都居中的子级盒子宽高为100px当我点击在 子盒子上的时候, 其实也是点击在了这个 父盒子的身上,这种情况就叫做 事件传播

  • => 当元素触发一个事件的时候, 这个元素的父级节点也会触发相同的事件, 父元素的父元素也会触发相同的事件
  • => 点击了子盒子, 会触发子盒子的点击事件
  • => 也是点在了父盒子上, 也会触发父盒子的点击事件
  • => 也是点在了 body 上, 也会触发 body 的点击事件
  • => 也是点在了 html 上, 也会触发 html 的点击事件
  • => 也是点在了 document 上, 也会触发 document 的点击事件
  • => 也是点在了 window 上, 也会触发 window 的点击事件
  • => 页面上任何一个元素触发事件, 都会一层一层的最终导致 window 的相同事件触发

注意点:

    1. 只会传播同类事件, 如果是点击事件, 那么只会触发父级或者父级的父级他们注册的点击事件, 其他类型的事件不会得到触发
    1. 只会从点击的元素开始, 按照 html 的结构, 逐层向上触发同类型的事件
    1. 内部元素不管有没有该事件, 只要上层元素有该事件, 那么上层元素的事件就会触发
//html:
<div class="box">
    <div class="sBox"></div>
</div>

//css:
<style>
    .box {
        width: 500px;
        height: 500px;
        background-color: pink;
    }
    .sBox {
        width: 100px;
        height: 100px;
        background-color: green;
    }
</style>

//script:
// 0. 获取元素
var sBox = document.querySelector('.sBox')
var box = document.querySelector('.box')
var oBody = document.body

// sBox.onclick = function () {
//     console.log('我是 内层 DIV, 我的点击事件被触发了')
// }

box.onclick = function () {
    console.log('我是 外层 DIV, 我的点击事件被触发了')
}

oBody.onclick = function () {
    console.log('我是 body, 我的点击事件被触发了')
}

12. 冒泡与捕获(面试题) 与目标

  1. 目标: 你点击在那个元素上, 那么这个事件的目标 就是这个元素

  2. 冒泡: 就是从 目标 的事件处理函数开始, 依次向上, 一直到 window 的事件处理函数触发 也就是说从下向上的执行 事件处理函数

  3. 捕获: 就是从 window 的事件处理函数开始, 依次向下, 一直到 目标 的事件处理函数触发 也就是说从上向下的执行 事件处理函数

  4. 区别: 就是在事件的传播中, 多个同类型的事件处理函数的执行顺序不同, 仅此而已

//html:
<div class="box">
    <div class="sBox"></div>
</div>

//css:
<style>
    .box {
        width: 500px;
        height: 500px;
        background-color: pink;
    }

    .sBox {
        width: 100px;
        height: 100px;
        background-color: green;
    }
</style>

//script:
// 0. 获取元素
var sBox = document.querySelector('.sBox')
var box = document.querySelector('.box')
var oBody = document.body

// 1. 按照事件冒泡的形式传播
sBox.onclick = function () {
    console.log('我是 内层 DIV, 我的点击事件被触发了')
}
box.onclick = function () {
    console.log('我是 外层 DIV, 我的点击事件被触发了')
}
oBody.onclick = function () {
    console.log('我是 body, 我的点击事件被触发了')
}

/**
*  2. 按照事件捕获的形式传播
* 
*      addEventListener 第三个参数就是决定当前传播方式为 捕获还是冒泡
* 
*      默认第三个参数为 false, 代表传播方式为 冒泡
* 
*      如果想按照捕获的形式传播, 那么给第三个参数 传递一个 true
*/
sBox.addEventListener('click', function () {
    console.log('我是 内层 DIV, 我的点击事件被触发了')
}, true)

box.addEventListener('click', function () {
    console.log('我是 外层 DIV, 我的点击事件被触发了')
}, true)

oBody.addEventListener('click', function () {
    console.log('我是 body, 我的点击事件被触发了')
}, true)

13. 事件委托

就是要把我自己做的事, 委托给别人, 帮我完成.因为我们的冒泡机制, 点击子元素的时候, 也会同步触发父元素的相同事件,所以我们可以把子元素的事件委托给父元素来做,点击子元素的时候, 不管子元素有没有点击事件, 只要父元素有点击事件, 那么就可以触发父元素的点击事件

target

  • 这个属性是事件对象里的属性, 表示你点击的目标
  • 这个 target 兼容性有问题, 在 IE 浏览器内 需要使用 srcElement 语法: e.srcElement (了解即可)
var oUl = document.querySelector('ul')
oUl.onclick = function (e) {
    if (e.target.nodeName === 'LI') {
        console.log('我是 li 标签, 我被点击了~~~')
    }
}

事件委托的优点

  • 页面上本身没有 li, 通过 代码添加了一些 li
  • 这些 li 是没有点击事件的, 每次动态的添加 li, 还需要从新给 li 绑定一次点击事件
  • 这时候使用事件委托就比较合适
  • 因为: 新加进来的 li 也是 ul 的子元素, 点击的时候也可以触发 ul 的点击事件

14. 案例

需求: 给 Ul 内部的 所有 li 添加一个 点击事件, 但是不能使用事件委托

<style>
    ul {
        width: 500px;
        background-color: pink;
    }

    li {
        width: 200px;
        background-color: green;
        margin-top: 5px;
    }
</style>

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>

// 0. 获取元素
// var lis = [...document.querySelectorAll('li')]
var oUl = document.querySelector('ul')

// 1. 遍历数组, 给目前结构内所有的 li 添加一个点击事件
// lis.forEach(function (item) {
//     item.onclick = function () {
//         console.log('我点击了 li')
//     }
// })

// 1.1 利用事件委托解决
oUl.onclick = function (e) {
    if (e.target.nodeName === 'LI') {
        console.log('我点击了 li')
    }
}

// 2. 创建一个 li 节点并添加到 ul 内
var newLi = document.createElement('li')
newLi.innerText = '我是通过 JS 创建的 li 节点'
oUl.appendChild(newLi)

15. 默认行为

  1. 默认行为:不用我们注册, 但是自己存在的事情
  • 比如:鼠标右键单击, 会弹出一个菜单; 点击 a 标签后, 自己会跳转到页面,这些不需要我们注册就是能实现的事情, 我们叫做 默认事件
  1. 阻止默认事件
  • 不希望浏览器执行默认事件时, 比如 点击 a 标签不跳转页面, 那么就需要阻止默认事件
  • 两种方式:
      1. e.preventDefault() -> 非 IE 浏览器
      1. e.returnValue = false -> IE 浏览器 (了解即可)
<a href="https://www.baidu.com/">点击之后跳转百度</a>

var oA = document.querySelector('a')

oA.onclick = function (e) {
    console.log('该函数执行, 拦截掉了 a 标签的默认行为')
    e.preventDefault()
}

16. 开光案例

/**
 * 功能:    
 *      点击一下按钮, 让按钮变成 X秒后可以重新执行, 并且在 5秒内不能重新执行
 * 
 *      点击一下按钮, 按钮变成  5秒后可以重新执行, 在着 5秒内点击这个 按钮, 不能重复触发
*/

<button>点击获取验证码</button>

// 0. 获取元素
var btn = document.querySelector('button')

// 0. 准备变量
var flag = true

// 1. 给元素添加点击事件
btn.onclick = function () {
    if (flag === false) return

    console.log('当前点击事件执行')
    var count = 5
    btn.innerText = `${count} 秒后可以重新执行`

    flag = false

    var timeId = setInterval(function () {
        count--
        btn.innerText = `${count} 秒后可以重新执行`

        if (count === 0) {
            btn.innerText = '点击获取验证码'
            clearInterval(timeId)
            flag = true
        }
    }, 1000)
}

/**
*  第一次点击按钮时
*          flag === true,  所以 31 行 if 判断不可能会执行, 函数会正常往下执行
*              执行到 37 行的时候, 会将 变量 flag 赋值 为 false;   然后再 5 秒钟以后重新赋值为 true
* 
*  第二次点击按钮时    (与第一次点击完成时间间隔只有 3 秒钟)
*          flag === false, 所以 31 行 if 判断条件成立, 执行代码 return, 所以函数就此中断
* 
*  第三次点击按钮时    (与第一次点击完成时间间隔有 30 秒钟)
*          flag === true, 所以 31 行 if 判断不可能会执行, 函数会正常往下执行
*              执行到 37 行的时候, 会将 变量 flag 赋值为 false;        然后 5 秒钟以后重新赋值为 true
*/