JS-11事件处理、鼠标/键盘/表单元素/文档加载/CSS 事件、事件冒泡和捕获、事件对象event(常见属性+常见方法+this)、EventTarget、事件委托模式

1_认识事件处理

Web页面经常和用户进行交互,需要捕捉交互的过程:

  • 比如用户点击了某个按钮、用户在输入框里面输入了某个文本、用户鼠标经过了某个位置;

  • 浏览器需要搭建一条JavaScript代码和事件之间的桥梁;

  • 当某个事件发生时,让JavaScript可以相应(执行某个函数),所以需要针对事件编写处理程序(handler);

如何进行事件监听?

  • 事件监听方式一:在script中直接监听(很少使用);

  • 事件监听方式二:DOM属性,通过元素的on来监听事件;

  • 事件监听方式三:通过EventTarget中的addEventListener来监听【推荐使用】

<body>
  
  <!-- 事件监听方式一:直接在html中编写JavaScript代码(了解) -->
  <button onclick="console.log('按钮1发生了点击~');">按钮1</button>

  <button class="btn2">按钮2</button>
  <button class="btn3">按钮3</button>

  <script>
      
    //事件监听方式二

    // 1.获取元素对象
    var btn2El = document.querySelector(".btn2")
    var btn3El = document.querySelector(".btn3")

    // 2.onclick属性
    // function handleClick01() {
    //   console.log("按钮2发生了点击~")
    // }
    // function handleClick02() {
    //   console.log("按钮2的第二个处理函数")
    // }
    // btn2El.onclick = handleClick01
    // btn2El.onclick = handleClick02

    // 3.事件监听方式三: addEventListener(推荐)
    btn3El.addEventListener("click", function() {
      console.log("第一个btn3的事件监听~")
    })
    btn3El.addEventListener("click", function() {
      console.log("第二个btn3的事件监听~")
    })
    btn3El.addEventListener("click", function() {
      console.log("第三个btn3的事件监听~")
    })

  </script>

</body>

1.1_常见的事件列表

鼠标事件:

  • click —— 当鼠标点击一个元素时(触摸屏设备会在点击时生成)。

  • mouseover / mouseout —— 当鼠标指针移入/离开一个元素时。

  • mousedown / mouseup —— 当在元素上按下/释放鼠标按钮时。

  • mousemove —— 当鼠标移动时。

键盘事件: keydown 和 keyup —— 当按下和松开一个按键时。

表单(form)元素事件:

  • submit —— 当访问者提交了一个 form 时。

  • focus —— 当访问者聚焦于一个元素时,例如聚焦于一个 input

文档加载Document 事件: DOMContentLoaded —— 当 HTML 的加载和处理均完成,DOM 被完全构建完成时。

CSS 事件: transitionend —— 当一个 CSS 动画完成时。


1.2_认识事件流

产生事件流?

  • 一个问题:当在浏览器上对着一个元素点击时,点击的不仅仅是这个元素本身;

  • 这是因为HTML元素是存在父子元素叠加层级的;

  • 比如一个span元素是放在div元素上的,div元素是放在body元素上的,body元素是放在html元素上的;



2_事件冒泡和捕获

默认情况下事件是从最内层的span向外依次传递的顺序,这个顺序称之为事件冒泡(Event Bubble)

另外一种监听事件流的方式就是从外层到内层(body -> span),这个顺序称之为事件捕获(Event Capture);

为什么会产生两种不同的处理流?

  • 早期浏览器开发时,不管是IE还是Netscape公司都发现了这个问题;
  • 但是他们采用了完全相反的事件流来对事件进行了传递;
  • IE采用了事件冒泡的方式,Netscape采用了事件捕获的方式;

事件捕获和冒泡的过程

(1)捕获阶段(Capturing phase): 事件(从 Window)向下走近元素。

(2)目标阶段(Target phase): 事件到达目标元素。

(3) 冒泡阶段(Bubbling phase): 事件从元素上开始冒泡。

通过event对象来获取当前的阶段: eventPhase。

开发中通常会使用事件冒泡,所以事件捕获了解即可

 <style>
    .box {
      display: flex;
      justify-content: center;
      align-items: center;
      width: 200px;
      height: 200px;
      background-color: orange;
    }

    .box span {
      width: 100px;
      height: 100px;
      background-color: red;
    }
  </style>

<body>
  
  <div class="box">
    <span></span>
  </div>

  <script>

    // 1.获取元素
    var spanEl = document.querySelector("span")
    var divEl = document.querySelector("div")
    var bodyEl = document.body


    // 2.绑定点击事件
    // spanEl.onclick = function() {
    //   console.log("span元素发生了点击~")
    // }
    // divEl.onclick = function() {
    //   console.log("div元素发生了点击~")
    // }
    // bodyEl.onclick = function() {
    //   console.log("body元素发生了点击~")
    // }

    // 默认情况下是事件冒泡
    spanEl.addEventListener("click", function() {
      console.log("span元素发生了点击~冒泡")
    })
    divEl.addEventListener("click", function() {
      console.log("div元素发生了点击~冒泡")
    })
    bodyEl.addEventListener("click", function() {
      console.log("body元素发生了点击~冒泡")
    })

    // 设置希望监听事件捕获的过程
    spanEl.addEventListener("click", function() {
      console.log("span元素发生了点击~捕获")
    }, true)
    divEl.addEventListener("click", function() {
      console.log("div元素发生了点击~捕获")
    }, true)
    bodyEl.addEventListener("click", function() {
      console.log("body元素发生了点击~捕获")
    }, true)

  </script>

</body>



3_事件对象event

当一个事件发生时,会有这个事件相关的很多信息:

  • 比如事件的类型是什么,点击的是哪一个元素,点击的位置是哪里等等相关的信息;

  • 这些信息会被封装到一个Event对象中,这个对象由浏览器创建,称之为event对象;

  • 该对象提供了想要的一些属性,以及可以通过该对象进行某些操作;

如何获取这个event对象呢?

  • event对象会在传入的事件处理(event handler)函数回调时,被系统传入;

  • 在回调函数中拿到这个event对象;


3.1_event常见的属性:

  • type:事件的类型;

  • target:当前事件发生的元素;

  • currentTarget:当前处理事件的元素;

  • eventPhase:事件所处的阶段;

  • offsetX、offsetY:事件发生在元素内的位置;

  • clientX、clientY:事件发生在客户端内的位置;

  • pageX、pageY:事件发生在客户端相对于document的位置;

  • screenX、screenY:事件发生相对于屏幕的位置;

  <style>
    .box {
      display: flex;
      width: 200px;
      height: 200px;
      background-color: orange;
    }

    span {
      width: 100px;
      height: 100px;
      background-color: #f00;
    }
  </style>

<body>

  <div class="box">
    <span class="btn">
      <button>按钮</button>
    </span>
  </div>

  <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
  <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
  <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
  
  <script>

    var divEl = document.querySelector("div")
    var btnEl = document.querySelector(".btn")

    // btnEl.onclick = function() {
    //   console.log("按钮发生了点击~")
    // }

    divEl.onclick = function(event) {
      // 1.偶尔会使用
      console.log("事件类型:", event.type)  //click
      console.log("事件阶段:", event.eventPhase)  //3

      // 2.比较少使用
      console.log("事件元素中位置", event.offsetX, event.offsetY)  //12  7
      console.log("事件客户端中位置", event.clientX, event.clientY)  //21 17
      console.log("事件页面中位置", event.pageX, event.pageY)  // 21 7
      console.log("事件在屏幕中位置", event.screenX, event.screenY)  // 903 225

      // 3.target/currentTarget
      console.log(event.target)  //<button>按钮</button>
      console.log(event.currentTarget)  // <div class="box">    <span class="btn">      <button>按钮</button>    </span>  </div>
      console.log(event.currentTarget === event.target)   //false
    }

  </script>

</body>

3.2_event常见的方法:

  • preventDefault:取消事件的默认行为;

  • stopPropagation:阻止事件的进一步传递(冒泡或者捕获都可以阻止);

 <style>
    .box {
      display: flex;
      width: 200px;
      height: 200px;
      background-color: orange;
    }

    .box span {
      width: 100px;
      height: 100px;
      background-color: #f00;
    }
  </style>

<body>
  
  <a href="http://www.baidu.com">百度一下</a>

  <div class="box">
    <span>
      <button>按钮</button>
    </span>
  </div>

  <script>
    // 1.阻止默认行为
    // var aEl = document.querySelector("a")
    // aEl.onclick = function(event) {
    //   console.log("a元素发生了点击~")
    //   event.preventDefault()
    // }

    // 2.阻止事件进一步传递
    var btnEl = document.querySelector("button")
    var spanEl = document.querySelector("span")
    var divEl = document.querySelector("div")
    
    divEl.addEventListener("click", function(event) {
      console.log("div的事件捕获监听~")
      // event.stopPropagation()
    }, true)
    spanEl.addEventListener("click", function() {
      console.log("span的事件捕获监听~")
    }, true)
    btnEl.addEventListener("click", function(event) {
      console.log("button的事件捕获监听~")
      // event.stopPropagation()
    }, true)
    
    divEl.addEventListener("click", function() {
      console.log("div的事件冒泡监听~")
    })
    spanEl.addEventListener("click", function(event) {
      console.log("span的事件冒泡监听~")
      event.stopPropagation()
    })
    btnEl.addEventListener("click", function() {
      console.log("button的事件冒泡监听~")
    })

  </script>

</body>

3.3_事件处理中的this

函数中,通过this来获取当前的发生元素,在浏览器内部,调用event handler是绑定到当前的target上的

<body>
  
  <div>
    <button>按钮</button>
  </div>

  <script>

    var btnEl = document.querySelector("button")
    var divEl = document.querySelector("div")

    divEl.onclick = function(event) {
      console.log(this)  //<div>    <button>按钮</button>  </div>
      console.log(event.currentTarget)  //<div>    <button>按钮</button>  </div>
      console.log(divEl)	 //<div>    <button>按钮</button>  </div>
      console.log(this === divEl)  //true
    }
    // divEl.addEventListener("click", function() {
    //   console.log(this)
    // })

  </script>

</body>



4_EventTarget使用

所有的节点、元素都继承自EventTarget,事实上Window也继承自EventTarget。

EventTarget是一个DOM接口,主要用于添加、删除、派发Event事件;

EventTarget常见的方法:

  • addEventListener:注册某个事件类型以及事件处理函数;

  • removeEventListener:移除某个事件类型以及事件处理函数;

  • dispatchEvent:派发某个事件类型到EventTarget上;

<body>

  <button>按钮</button>
  
  <script>

    var btnEl = document.querySelector("button")

    // 1.将监听函数移除的过程
    // var foo = function() {
    //   console.log("监听到按钮的点击")
    // }
    // btnEl.addEventListener("click", foo)

    // // 需求: 过5s钟后, 将这个事件监听移除掉
    // setTimeout(function() {
    //   btnEl.removeEventListener("click", foo)
    // }, 5000)

    // 下面这种做法是无法移除的
    btnEl.addEventListener("click", function() {
      console.log("btn监听的处理函数~")
    })
   //因为  function不明确
    setTimeout(function() {
      btnEl.removeEventListener("click", function() {})
    }, 5000)
      
      
      
       // eventtarget就可以实现类似于事件总线的效果,但是不推荐使用
      
      //事件实现效果
    window.addEventListener("code", function() {
      console.log("监听到code的呼唤~")
    })

      //5秒钟后派发一个事件
    setTimeout(function() {
      window.dispatchEvent(new Event("code"))
    }, 5000)



  </script>

</body>



5_事件委托模式

事件冒泡在某种情况下可以帮助实现强大的事件处理模式 –事件委托模式(也是一种设计模式)

事件委托模式?

  • 当子元素被点击时,父元素可以通过冒泡可以监听到子元素的点击;

  • 并且通过event.target获取到当前监听的元素;

案例:一个ul中存放多个li,点击某一个li会变成红色

  • 方案一:监听每一个li的点击,并且做出相应;

  • 方案二:在ul中监听点击,并且通过event.target拿到对应的li进行处理; 因为这种方案并不需要遍历后给每一个li上添加事件监听,所以它更加高效;

 <style>
    .active {
      color: red;
      font-size: 20px;
      background-color: orange;
    }
  </style>

<body>
  
  <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
  </ul>

  <script>

    // 1.每一个li都监听自己的点击, 并且有自己的处理函数【性能一般】
    // var liEls = document.querySelectorAll("li")
    // for (var liEl of liEls) {
    //   // 监听点击
    //   liEl.onclick = function(event) {
    //     event.currentTarget.classList.add("active")
    //   }
    // }

    // 2.统一在ul中监听【性能较好】
    // var ulEl = document.querySelector("ul")
    // ulEl.onclick = function(event) {
    //   console.log("点击了某一个li", event.target)
    //   event.target.classList.add("active")
    // }

    // 3.新需求: 点击li变成active, 其他的取消active
    var ulEl = document.querySelector("ul")
    var activeLiEl = null
    ulEl.onclick = function(event) {
      // 1.第一种思想:for循环遍历所有元素,将之前的active移除掉
      // for (var i = 0; i < ulEl.children.length; i++) {
      //   var liEl = ulEl.children[i]
      //   if (liEl.classList.contains("active")) {
      //     liEl.classList.remove("active")
      //   }
      // }

      // 1. 第二种思想:用querySector遍历找到active的li, 移除掉active
      // var activeLiEl = ulEl.querySelector(".active")
      // if (activeLiEl) {
      //   activeLiEl.classList.remove("active")
      // }

      // 1.第三种思想:拿出一个变量记录cative的li,不用遍历
      if (activeLiEl) {
        activeLiEl.classList.remove("active")
      }
      
      // 2.给点击的元素添加active
      event.target.classList.add("active")

      // 3.记录最新的active对应的li
      activeLiEl = event.target
    }

  </script>

</body>

事件委托的标记:某些事件委托可能需要对具体的子组件进行区分,这个时候我们可以使用data-*对其进行标记,比如多个按钮的点击,区分点击了哪一个按钮

<body>
  
  <div class="box">
    <button data-action="search">搜索~</button>
    <button data-action="new">新建~</button>
    <button data-action="remove">移除~</button>
    <button>1111</button>
  </div>

  <script>

    var boxEl = document.querySelector(".box")
    boxEl.onclick = function(event) {
      var btnEl = event.target
      var action = btnEl.dataset.action
      switch (action) {
        case "remove":
          console.log("点击了移除按钮")
          break
        case "new":
          console.log("点击了新建按钮")
          break
        case "search":
          console.log("点击了搜索按钮")
          break
        default:
          console.log("点击了其他")
      }
    }
  </script>

</body>



6_常见的事件

6.1_常见的鼠标事件

(不仅仅是鼠标设备,也包括模拟鼠标的设备,比如手机、平板电脑)

在这里插入图片描述


mouseover和mouseenter的区别

mouseenter和mouseleave

  • 不支持冒泡

  • 进入子元素依然属于在该元素内,没有任何反应

mouseover和mouseout

  • 支持冒泡【应用广泛】

  • 进入元素的子元素时

    • 先调用父元素的mouseout

    • 再调用子元素的mouseover

    • 因为支持冒泡,所以会将mouseover传递到父元素中;

  <style>
    .box {
      display: flex;
      justify-content: center;
      align-items: center;
      width: 200px;
      height: 200px;
      background-color: orange;
    }

    .box span {
      width: 100px;
      height: 100px;
      background-color: red;
    }
  </style>

<body>
  
  <div class="box">
    <span></span>
  </div>

  <script>
    var boxEl = document.querySelector(".box")
    var spanEl = document.querySelector("span")

    // 1.第一组  鼠标进入子元素,不会冒泡,即不会触发子元素事件
    boxEl.onmouseenter = function() {
      console.log("box onmouseenter")
    }
    boxEl.onmouseleave = function() {
      console.log("box onmouseleave")
    }

    spanEl.onmouseenter = function() {
      console.log("span onmouseenter")
    }
    spanEl.onmouseleave = function() {
      console.log("span onmouseleave")
    }

    // 第二组   鼠标进入子元素,冒泡,触发子元素事件
    // boxEl.onmouseover = function() {
    //   console.log("box onmouseover")
    // }
    // boxEl.onmouseout = function() {
    //   console.log("box onmouseout")
    // }

  </script>

</body>
  <style>
    .box {
      background-color: orange;
    }

    .box button {
      flex: 1;
      height: 50px;
    }
  </style>
</head>
<body>
  
  <div class="box">
    <button>删除</button>
    <button>新增</button>
    <button>搜索</button>
  </div>

  <script>
    // 方案一: 监听的本身就是button元素
    // var btnEls = document.querySelectorAll("button")
    // for (var i = 0; i < btnEls.length; i++) {
    //   btnEls[i].onmouseover = function(event) {
    //     console.log(event.target.textContent)
    //   }
    // }

    // 方案二: 事件委托
    var boxEl = document.querySelector(".box")
    //不能用 mouseenter  因为不会单独触发子元素事件
    boxEl.onmouseover = function(event) {
      console.log(event.target.textContent)
    }
    
  </script>

6.2_常见的键盘事件

在这里插入图片描述

事件的执行顺序是 onkeydown、onkeypress、onkeyup

  • down事件先发生;

  • press发生在文本被输入;

  • up发生在文本输入完成;

通过key和code来区分按下的键:

  • code:“按键代码”(“KeyA”,“ArrowLeft” 等),特定于键盘上按键的物理位置。

  • key:字符(“A”,“a” 等),对于非字符(non-character)的按键,通常具有与 code 相同的值。)

<body>
  
  <input type="text">
  <button>搜索</button>

  <script>

    var inputEl = document.querySelector("input")
    var btnEl = document.querySelector("button")

    // inputEl.onkeydown = function() {
    //   console.log("onkeydown")
    // }
    // inputEl.onkeypress = function() {
    //   console.log("onkeypress")
    // }
    // inputEl.onkeyup = function(event) {
    //   console.log(event.key, event.code)
    // }

    // 1.搜索功能
    btnEl.onclick = function() {
      console.log("进行搜索功能", inputEl.value) //点击按钮
    }

    inputEl.onkeyup = function(event) {
      if (event.code === "Enter") {
        console.log("进行搜索功能", inputEl.value)   //敲击回车键
      }
    }

    // 2.按下键盘s键的时候, 搜索自动获取焦点
    document.onkeyup = function(event) {
      if (event.code === "KeyS") {
        inputEl.focus()
      }
    }

  </script>

</body>

6.3_常见的表单事件

大致了解,等到开发用到去查文档

在这里插入图片描述

<body>
  
  <form action="/abc">
    <input type="text">
    <textarea name="" id="" cols="30" rows="10"></textarea>

    <button type="reset">重置</button>
    <button type="submit">提交</button>
  </form>

  <script>
    var inputEl = document.querySelector("input")

    // 1.获取焦点和失去焦点
    // inputEl.onfocus = function() {
    //   console.log("input获取到了焦点")
    // }
    // inputEl.onblur = function() {
    //   console.log("input失去到了焦点")
    // }

    // 2.内容发生改变/输入内容
    // 输入的过程: input
    // 内容确定发生改变(离开): change
    // inputEl.oninput = function() {
    //   console.log("input事件正在输入内容", inputEl.value)
    // }
    // inputEl.onchange = function() {
    //   console.log("change事件内容发生改变", inputEl.value)
    // }

    // 3.监听重置和提交
    var formEl = document.querySelector("form")
    formEl.onreset = function(event) {
      console.log("发生了重置事件")
      event.preventDefault()
    }

    formEl.onsubmit = function(event) {
      console.log("发生了提交事件")
      // axios库提交  跳转到新页面
      event.preventDefault()
    }

  </script>

</body>

6.4_文档加载事件

DOMContentLoaded:浏览器已完全加载 HTML,并构建了 DOM 树,但像 和样式表之类的外部资源可能尚未加载完成。

load:浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等。

<body>
  
  <script>

    // 注册事件监听
    window.addEventListener("DOMContentLoaded", function() {
      // 1.这里可以操作box, box已经加载完毕
      // var boxEl = document.querySelector(".box")
      // boxEl.style.backgroundColor = "orange"
       console.log("HTML内容加载完毕")

      // 2.获取img对应的图片的宽度和高度
      var imgEl = document.querySelector("img")
      console.log("图片的宽度和高度:", imgEl.offsetWidth, imgEl.offsetHeight)
    })

    window.onload = function() {
      console.log("文档中所有资源都加载完毕")
      // var imgEl = document.querySelector("img")
      // console.log("图片的宽度和高度:", imgEl.offsetWidth, imgEl.offsetHeight)
    }

    window.onresize = function() {
      console.log("创建大小发生改变时")
    }

  </script>

  <div class="box">
    <p>哈哈哈啊</p>
  </div>
  <a href="#">百度一下</a>
  <img src="../images/test.jpg" alt="">

</body>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值