Web APIs 事件对象(一)

157 阅读8分钟

Web APIs 第四天

DOM- 事件高级
事件对象是什么?
  • 也是个对象,这个对象里有事件触发时的相关信息
事件对象在哪里
  • 在事件绑定的回调函数的第一个参数就是事件对象
  • 能够使用常见事件对象属性一般命名为event、ev、e
元素.addEventLIstener('click',function(e)){
}

常用属性

  • type: 获取当前的事件类型
  • clientX/clientY:获取光标相对于浏览器可见窗口左上角的位置
  • key: 用户按下的键盘键的值 ,现在不提倡使用keyCode

案例 跟随鼠标案例

  • 需求:一张图片一直跟着鼠标移动 分析:
  • 鼠标在页面中移动,用到 mousemove 事件
  • 不断把鼠标在页面中的坐标位置给图片left和top值即可
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
    />
    <title>04-小鸟跟随鼠标.html</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      /* div {
        width: 100px;
        height: 100px;
        background-color: aqua;
        position: fixed;
      } */
      img {
        position: fixed;
        /* width: 50px; */
        /* 移动自身宽度和高度的一半 */
        transform: translate(-50%, -50%);
      }
      body {
        height: 100vh;
        /* 隐藏鼠标 */
        cursor: none;
      }
    </style>
  </head>
  <body>
    <h1>用户鼠标不要移动那么快</h1>
    <!-- <div></div> -->
    <img src="./images/xn.png" alt="" />
    <script>
      /* 
      1 给body标签 绑定 鼠标移动事件  
        body也是一个普通的块级元素 高度由内容撑开 同时 div使用了定位-脱标 body标签没有高度 
      2 事件触发了 获取 事件对象的坐标 clientX 
      3 把坐标设置给div的left top
       */

      // const div = document.querySelector('div');
      const img = document.querySelector('img');
      document.body.addEventListener('mousemove', function (event) {
        const left = event.clientX;
        const top = event.clientY;

        img.style.left = left + 'px';
        img.style.top = top + 'px';
      });
    </script>
  </body>
</html>

按下回车发布微博案例

  • 需求:按下回车键盘,可以发布信息 分析:

  • 用到按下键盘事件 keydown 或者 keyup 都可以

  • 如果用户按下的是回车键盘,则发布信息

  • 按下键盘发布新闻,其实和点击发布按钮效果一致 send.click()

代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
    />
    <title>06-微博发布.html</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <a href="http://www.baidu.com"></a>
    <textarea id="area"></textarea>
    <button>发布</button>
    <ul></ul>
    <script>
      /* 
      步骤
      1 给发布按钮 绑定点击事件
        1 获取文本域的内容 
        2 拼接到一个新创建的li标签中
        3 把新创建的li标签插入到ul中

      2 给文本域 绑定键盘按下事件

        按下键盘之后  先判断当下按下的是不是回车  也去执行点击按钮的工作 即可 
       */
      const btn = document.querySelector('button');
      const area = document.querySelector('#area');
      const ul = document.querySelector('ul');

      btn.addEventListener('click', function (event) {
        // 判断一下当前的文本域的内容 是不是空字符串
        if (area.value.trim() === '') {
          console.log('返回不再往下执行');
          return;
        }

        // area.value
        const li = document.createElement('li');
        li.innerText = area.value;
        ul.appendChild(li);
      });

      area.addEventListener('keydown', function (event) {
        // 判断按键是不是回车键
        if (event.key === 'Enter') {
          // console.log("执行和按钮一样的功能");

          // 给btn按钮绑定过点击事件,点击事件也是可以主动触发
          btn.click(); // 你点击了一下按钮

          // 解决按下回车 文本换行的效果
          // 文本域的默认的行为
          // 处理标签的默认行为
          // return false
          event.preventDefault(); // 阻止浏览器标签的默认行为  阻止a标签默认跳转行为
        }
      });

      document.querySelector('a').addEventListener('click', function (e) {
        e.preventDefault(); // 阻止a标签的默认 跳转行为
      });
    </script>
  </body>
</html>

事件流

事件流经过的2个阶段

事件流指的是事件完整执行过程中的流动路径

  • 捕获和冒泡

  • 捕获阶段: 父节点 流动到 子节点

  • 冒泡节点: 子节点 流动到 父节点

  • 可以修改触发事件,让他选择使用 捕获还是冒泡节点

  • 总结

    1.捕获和冒泡

    ​ 2.默认情况 冒泡 如果想要修改 可以 addEventListener 第三个参数 传入 true即可

    3 以后的代码开发过程中,还是继续使用默认的 冒泡阶段

假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段
捕获阶段:从父到子。 冒泡阶段:从子到父

事件捕获

事件捕获是指事件被触发时从DOM根元素开始去执行对应的事件(从里到外)

捕获阶段是 从父到子 冒泡阶段是从子到父

事件捕获需要写相应的代码才能看到效果:

DOM.addEventListener(事件类型,事件处理函数,是否使用捕获机制)

若在是否使用捕获机制处传入 true 则代表是捕获阶段触发,默认为false,代表冒泡阶段触发 若是用L0事件监听(元素.onclick)则只有冒泡阶段,没有捕获

代码



<!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>Document</title>
    <style>
        .father {
            margin: 100px auto;
            width: 500px;
            height: 500px;
            background-color: pink;
        }
        .son {

            width: 200px;
            height: 200px;
            background-color: purple;
        }
    </style>
</head>
<body>
    <div class="father">
        <div class="son"></div>
    </div>
    <script>
        let fa = document.querySelector('.father')
        let son = document.querySelector('.son')
        fa.addEventListener('click', function () {
            alert('我是爸爸')
        }, true)
        son.addEventListener('click', function () {
            alert('我是儿子')
        }, true)
        document.addEventListener('click', function () {
            alert('我是爷爷')
        }, true)

        // btn.onclick = function() {}
    </script>
</body>
</html>

事件冒泡

在一个对象上触发某类事件(比如单击onclick事件),如果此对象定义了此事件的处理程序,那么此事件就会调用这个处理程序,如果没有定义此事件处理程序或者事件返回true,那么这个事件会向这个对象的父级对象传播,从里到外,直至它被处理(父级对象所有同类事件都将被激活),或者它到达了对象层次的最顶层,即document对象(有些浏览器是window)。

打个比方说:你在地方法院要上诉一件案子,如果地方没有处理此类案件的法院,地方相关部门会帮你继续往上级法院上诉,比如从市级到省级,直至到中央法院,最终使你的案件得以处理。

事件冒泡有什么作用

事件冒泡允许多个操作被集中处理(把事件处理器添加到一个父级元素上,避免把事件处理器添加到多个子级元素上),它还可以让你在对象层的不同级别捕获事件

当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡

当一个元素被触发事件后,会依次向上调用所有父级元素的同名事件

事件捕获和事件冒泡属于两个相反的过程。

阻止冒泡

因为默认就有冒泡模式的存在,所以就容易导致事件影响到父级。

组织事件流动需要拿到事件对象,语法:事件对象.stopPropagation()

此方法可以阻断事件流动传播,不仅阻断了冒泡阶段,在捕获阶段也有效

  • mouseovermouseout 会有冒泡效果
  • mouseentermouseleave 没有冒泡效果

阻止默认行为

比如点击链接不跳转,点击表单不提交等。语法:e.preventDefault()

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
    />
    <title>03-阻止冒泡</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      div {
        width: 200px;
        height: 200px;
        padding: 20px;
        overflow: hidden;
      }
      .a {
        background-color: red;
      }
      .b {
        background-color: blue;
      }
      .c {
        background-color: green;
      }
      p {
        width: 200px;
        height: 200px;
        background-color: yellow;
      }
    </style>
  </head>
  <body>
    <p>1</p>
    <div class="a"><div class="b"><div class="c"></div>
      </div>
    </div>
    <script>
      const p = document.querySelector('p');

      const a = document.querySelector('.a');
      a.addEventListener(
        'click',
        function () {
          p.style.backgroundColor = 'red';
          p.innerText = +p.innerText + 1;
          console.log('a');
        }
      ); 
      const b = document.querySelector('.b');
      b.addEventListener(
        'click',
        function (event) {
          console.log('b');
          p.innerText = +p.innerText + 10;
          p.style.backgroundColor = 'blue';
          // 阻止事件冒泡
          event.stopPropagation();
        }
      );
      const c = document.querySelector('.c');
      c.addEventListener(
        'click',
        function (event) {
          console.log('c');
          p.innerText = +p.innerText + 100;
          p.style.backgroundColor = 'green';


          // 阻止事件冒泡
          event.stopPropagation();
        }
      );


      /* 
      引出新的知识 阻止冒泡! 下一节课 优雅的方式来解决刚才的问题! 
      在事件对象中 event 找到一个方法 停止冒泡  event.stopPropagation();
      
       */
    </script>
  </body>
</html>

阻止标签的默认行为

使用 event.preventDefault()

      const a=document.querySelector("a");
      const form=document.querySelector("form");
      const button=document.querySelector("button");
      a.addEventListener("click",function (event) {
        console.log("a标签的点击触发啦");
        // 阻止a标签的默认行为,让他不要跳转
        event.preventDefault();
      })

案例:鼠标右键-菜单

<!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>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body{
            height: 100vh;
        }
        .menu{
            list-style: none;
            padding:10px;
            border-radius: 5px;
            border: 1px solid #ccc;
            width: 150px;
            position: fixed;
            display: none;
        }
        li{
            height: 40px;
            display: flex;
            align-items: center;
            padding-left: 10px;
            border-bottom: 1px solid #ccc;
        }
        li:hover{
            background-color: green;
            color:#fff;
            cursor: pointer;
        }
        li:last-child{
            border-bottom: none;
        }


    </style>
</head>
<body>
    <ul class="menu">
        <li>添加图标</li>
        <li>切换壁纸</li>
        <li>下载壁纸</li>
        <li>设置</li>
    </ul>

    <script>
        const menu = document.querySelector('.menu')
        // 
        document.body.addEventListener('contextmenu',function(event){
            event.preventDefault()

            const left=event.clientX
            const top =event.clientY
            menu.style.display='block'
            menu.style.left =left+'px'
            menu.style.top =top+'px'
        })
        document.body.addEventListener('click',function(){
            menu.style.display='none'
        })
    </script>
</body>
</html>

事件委托

事件委托是指利用事件流的特征解决一些开发需求的技巧

优点:给父级元素加事件(可以提高性能) 原理:事件委托其实是利用事件冒泡的特点,给父元素添加事件,子元素可以触发 实现:事件对象.target可以获得真正触发事件的元素

适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。(所有用到按钮的事件,多数的鼠标事件和键盘事件) 值得注意的是,mouseover和mouseout虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。 不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。

      // 事件委托
      //  本来想要给li标签绑定事件实现业务
      //  把事件绑定写在 父元素上  把li标签应该要做的事情 委托给父元素来做! 
        const ul=document.querySelector("ul");
       
      ul.addEventListener("click",function (event) {
        //  event.target 有可能是ul标签,有可能是li标签,还有可能是 a标签 
        // 一会再来解决这个问题。
        event.target.style.backgroundColor="red";// 不够完美的
        // 事件委托  绑定写在父元素  把li标签 要在
        console.log(event.target);
        // event.target 你当前点击的是哪个标签(点击最深最底层的那个标签即可)
        // console.log(event.target);// 获取到被点击的li标签 
      })

event.target.nodeName

    <script>
        const ul = document.querySelector('ul')
        ul.addEventListener('click',function(event){
           
            // 点击li标签才触发
            if(event.target.nodeName ==="LI"){
                console.log('改变颜色')
                
                event.target.style.backgroundColor='green'
            }
        })
    </script>
综合案例
    <h1>新增学员</h1>
    <div class="info">
      姓名:<input type="text" class="uname" /> 年龄:<input
        type="text"
        class="age"
      />
      性别:
      <select name="gender" id="" class="gender">
        <option value="男"></option>
        <option value="女"></option>
      </select>
      薪资:<input type="text" class="salary" /> 就业城市:<select
        name="city"
        id=""
        class="city"
      >
        <option value="北京">北京</option>
        <option value="上海">上海</option>
        <option value="广州">广州</option>
        <option value="深圳">深圳</option>
        <option value="曹县">曹县</option>
      </select>
      <button class="add">录入</button>
    </div>

    <h1>就业榜</h1>
    <table>
      <thead>
        <tr>
          <th>学号</th>
          <th>姓名</th>
          <th>年龄</th>
          <th>性别</th>
          <th>薪资</th>
          <th>就业城市</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <!-- <tr>
          <td>1</td>
          <td>这是名称</td>
          <td>这是年龄</td>
          <td>这是性别</td>
          <td>这是工资</td>
          <td>这是所在城市</td>
          <td>
            <a href="javascript:" class="del">删除</a>
          </td>
        </tr> -->
      </tbody>
    </table>
    <script>
      let arr=[]
      let tbody = document.querySelector('tbody')
      let uname =document.querySelector('.uname')
      let age = document.querySelector('.age')
      let gender = document.querySelector('.gender')
      let salary = document.querySelector('.salary')
      let city = document.querySelector('.city')
      let  add = document.querySelector('.add')
      renderTableByArr()
      // 按钮绑定点击事件
      add.addEventListener('click',function(){

        const data ={
          // 学号
              id:Date.now(),
              uname:uname.value,
              age:age.value,
              gender:gender.value,
              salary:salary.value,
              city:city.value,
        }
        arr.push(data);
        // console.log(arr);
        renderTableByArr()
        uname.value=''
        age.value=''
        gender.value='男'
        salary.value=''
        city.value='北京'
      })

      tbody.addEventListener('click',function(event){
        // console.log('ee');
        if(event.target.nodeName==='A'){
          // console.log('a');
          const index =event.target.dataset.index
          arr.splice(index,1);
          renderTableByArr()
        }
      })



      function  renderTableByArr(){
        let html =``;
        for(let index=0;index<arr.length;index++){
          html+=`
          <tr>
          <td>${arr[index].id}</td>
          <td>${arr[index].uname}</td>
          <td>${arr[index].age}</td>
          <td>${arr[index].gender}</td>
          <td>${arr[index].salary}</td>
          <td>${arr[index].city}</td>
          <td>
            <a href="javascript:" class="del">删除</a>
          </td>
        </tr>`;
        console.log(arr);

        }
        console.log(html);
        tbody.innerHTML=html
        console.log(arr+'1');
      }
      
     

    </script>

滚动事件和加载事件

滚动事件

当页面滚动时触发的事件

事件名:scroll

样例,监听整个页面滚动:

监听页面所有资源加载完毕

window.addEventListener('scroll',function(){
    //执行操作
})
加载事件

加载外部资源(如图片、外联css和javaScript等) 加载完毕是触发的事件

事件名:load

注意:不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件

//页面加载事件
window.addEventListener('load',function(){
 	//执行的操作   
})
两个加载事件的区别

load事件

给window加,监听整个页面资源

DOMContentLoaded

给document加,当初始的HTML文档被完全加载和解析完成之后,DOMContentLoaded事件被触发,而无需等待样式表、图像等完全加载。

document.addEvenListener('DOMContentLoaded',function(){
    //执行的操作
})
注意事项
document.documentElement HTML 文档返回对象为HTML元素
scroll家族

使用场景: 我们想要页面滚动一段距离,比如100px,就让某些元素

显示隐藏,那我们怎么知道,页面滚动了100像素呢?

获取宽高
  • 获取元素的内容总宽高(不包含滚动条)返回值不带单位

  • scrollWidth和scrollHeight

获取位置
  • 获取元素内容往左、往上滚出去看不到的距离

  • scrollLeft和scrollTop

  • 这两个属性是可以修改的

div.addEvenListener('scroll',function(){
    console.log(this.scrollTop)
})

样例代码:

let div = document.querySelector('div')
console.log(div.scrollWidth)

div.addEventListener('scroll',function(){
	console.log(this.scrollTop)
})

检测页面滚动事件
window.addEventListener('scroll',function(){
	let num = document.documentElement.scrollTop
	console.log(num)
})
  • documentElement 也就是指 html
offset
获取宽高
  • 获取元素的自身宽高、包含元素自身设置的宽高、padding、border

  • offsetWidth和offsetHeight

获取位置
  • 获取元素距离自己定位父级元素的左、上距离

  • offsetLeft和offsetTop 注意是只读属性

offsetWidth和offsetHeight是得到元素什么的宽高

  • 内容 + padding + border

offsetTop和offsetLeft 得到位置以谁为准?

  • 带有定位的父级
  • 如果都没有则以 文档左上角 为准
client家族

clientWidth和clientHeight

  • 获取元素的可见部分宽高(不包含边框,滚动条等)

  • 获取左边框和上边框宽度

  • clientLeft和clientTop 注意是只读属性

window.addEventListener('resize',function(){
let w = document.documentElement.clientWidth
if(w >= 1920){
	document.body.style.backgroundColor = 'pink'
}
else if(w?540){
	document.body.style.backgroundColor = 'hotpink'
}
else{
	document.body.style.backgroundColor = 'deeppink'
}
})