前端之页面元素的API(DOM) 3

深度阅读@

事件流 :https://www.w3.org/TR/DOM-Level-3-Events/#events-uievents

1. 注册事件的其他方式

回顾:

刚开始我们给元素注册事件的方式,是通过on+事件名的方式,如下面的示例代码

//html
<div id="box"></div>

//js
var box = document.getElementById('box');
box.onclick = function(){
    //code...
}

后来,W3C DOM 规范中提供了注册事件监听的另外一种方式 : addEventListener

那么为什么要使用addEventListener呢?

优点:

  • 它允许给一个事件注册多个 listener

  • 它提供了一种更精细的手段控制 listener 的触发阶段。(即可以选择捕获或者冒泡)。

  • 它对任何 DOM 元素都是有效的,而不仅仅只对 HTML 元素有效。

语法:

element.addEventListener('事件名', 事件处理函数, useCapture);

  • 事件名不需要写on

  • useCapture 是一个boolean值,用于控制事件触发的阶段,不写默认是false

    • false 是在冒泡阶段执行

    • true 是在捕获阶段执行

    • 第三个参数: 要求传入一个布尔值 ,如果是true,则事件处理函数在事件捕获阶段执行,如果是false,则在事件冒泡阶段执行.如果这个元素是事件目标,那么true/false就无效了

//html
<div id="box"></div>

//js
var box = document.getElementById('box');
box.addEventListener('click', function(){
    //code...
}, false);
 box.addEventListener('click', function(){
   console.log('假装这是一百行代码');
 }, false);
 box.addEventListener('click', function(){
   console.log('新增的逻辑');
 }, false);
  
  box.addEventListener('click', fn, false);
  function fn(){
    console.log('ahah ');
  }

 

2. 移除事件的方式

2.1 on+事件名 的移除方式

on + 事件名 = null;

//html
<div id="box"></div>

//js
var box = document.getElementById('box');
box.onclick = function(){}  //注册点击事件
box.onclick = null; //移除点击事件

2.2 addEventListener的移除方式

removeEventListener()

注意:

如果在代码中使用addEventListener注册的事件,后面的逻辑中要移除对应的事件,那么在注册时,事件处理函数就不能使用匿名函数的方式

//html
<div id="box"></div>

//js
var box = document.getElementById('box');
box.addEventListener('click', boxClick, false); //注册的代码,注册时不能使用匿名函数
box.removeEventListener('click', boxClick, false); //移除的代码
function boxClick (){
    //code...
}

小结:

  • 注册事件的两种方式

    • on + 事件名 = function(){}

    • addEventListener('事件名', 事件处理函数, useCapture )

  • 移除事件对应的两种方式

    • on + 事件名 = null;

    • removeEventListener('事件名', 事件处理函数, useCapture )

      • 注意 : 注册时,事件处理函数不能使用匿名函数的形式

3.事件对象

当用户触发了我们注册的事件之后,我们在开发中需要获取用户触发事件的一些信息,比如鼠标的坐标,键盘的按键等...

那么我们在代码中如何获取这些信息呢?

当用户触发我们注册的事件之后,浏览器会创建一个事件对象,这个事件对象就包含了触发事件时的一些信息,比如时间戳,鼠标坐标,事件目标等等.然后浏览器会将这个事件对象,传递给事件处理函数,那么我们只需要在事件处理函数中申明一个形参,接收一下即可.如下面的示例代码所示:

//html
<div id="box"></div>

//js
var box = document.getElementById('box');
box.onclick = function(event){
    console.log(event) // 事件对象
}

3.1 事件对象的常用属性

  • event.type 返回事件类型(也就是事件名);

  • event.target 返回事件目标(触发了谁的事件,谁就是事件目标)

  • clientX/clientY 返回鼠标在浏览器可视窗口的坐标

  • pageX/pageY 返回鼠标在当前页面的坐标

  • keyCode 返回键盘按键对应的数字

3.2 事件对象的常用方法

  • event.preventDefault() 取消默认行为

    给a标签注册点击事件的时候,要在事件处理函数的最后一行写return false ,来阻止a标签的默认行为.

    但是如果使用addEventListener注册事件的话,return false 是无效的.

    所以我们需要使用 event.preventDefault()来阻止a标签的默认行为 ,如下面的代码

//html
<a id="link" href="">点击在控制台打印1</a>

//js
var link = document.getElementById('link');
link.addEventListener('click', function(){
    console.log(1);
    // return false;    //无效
    e.preventDefault(); //有效
},false)
  • event.stopPropagation() 阻止事件传递

3.3 键盘事件

keydown 按下时触发 不区分大小写 大写的ASCII码

 keyup 抬起时触发

keypress 按下时触发  区分大小写

注:把ASCII码转换成对应的字符——String.fromCharCode(49)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<input type="text" id="txt">
<script>
//  keydown   按下时触发  不区分大小写 大写的ASCII码
//  keyup     抬起时触发
//  keypress  按下时触发  区分大小写
  
  var txt = document.getElementById('txt');
  txt.onkeydown = function(e){
    console.log('keydown:' + e.keyCode); //返回的是对应的按下的键的数字 虽然是标准里移除的,但是浏览器都支持,放心使用
//      e.key这个属性,在mdn中推荐使用,但是由于是新标准,所以存在兼容性问题,很多浏览器不支持
//    console.log(e);
  }
  
  txt.onkeypress = function(e){
    console.log('keypress:' + e.keyCode);
  }
  
  //把ASCII码转换成对应的字符
console.log(String.fromCharCode(49));
console.log(String.fromCharCode(113));
</script>
</body>
</html>

keyCode案例:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<input type="text">
<input type="text">
<input type="text">
<input type="text">
<input type="text">
<script>
  // 按tab键,浏览器有默认的行为,会让下一个input获得焦点
//  当我们按回车键的时候,也要让下一个input获取的焦点
//  1. 获取元素 input
    var inputs = document.querySelectorAll('input');
//  2. 给每一个input注册鼠标按下的事件
    for(var i = 0; i < inputs.length; i++) {
      inputs[i].onkeydown = fn;
    }
    function fn(e){
      //  3. 在事件处理函数中判断是否是回车键
      console.log(e.keyCode);
      if(e.keyCode == 13){
        //  4. 如果是回车键,就下一个input获得焦点
//        4.1 获取到下一个input
        console.log(this.nextElementSibling);
//         4.2 让下一个input获得焦点 focus方法会获得焦点
        this.nextElementSibling.focus();
      }

    }

</script>
</body>
</html>

keyCode案例增强版:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<input type="text">
<div></div>
<input type="text"><br>
<input type="text"><br><br><br><br><br>
<input type="text"><br><br><br>
<input type="text"><br>
<script>
  // 按tab键,浏览器有默认的行为,会让下一个input获得焦点
  //  当我们按回车键的时候,也要让下一个input获取的焦点
  //  1. 获取元素 input
  var inputs = document.querySelectorAll('input');
  //  2. 给每一个input注册鼠标按下的事件
  for(var i = 0; i < inputs.length; i++) {
    inputs[i].onkeydown = fn;
  }
  function fn(e){
    //  3. 在事件处理函数中判断是否是回车键
//      console.log(e.keyCode);
    if(e.keyCode == 13){
      //  4. 如果是回车键,就下一个input获得焦点
//        4.1 获取到下一个input
           var result =  getNextInput(this);
            console.log(result);
//         4.2 让下一个input获得焦点 focus方法会获得焦点
      //    判断result如果不是null才调用focus方法
          if(result){ //如果result是元素,转布尔一定是true,如果是null,转布尔一定是false
            result.focus();
          }
    }
  }
  //用于获取下一个input
//  ele: 获取ele的下一个input
  function getNextInput(ele){
    // 获取ele的下一个兄弟元素
    var next = ele.nextElementSibling;
    //判断有没有下一个了,如果没有就直接结束了
    if(next == null){
      return null;
    }
    // 判断是否是input
    if(next.nodeName === 'INPUT'){
      return next;
    }else{
      //进入到这里,证明此时next不是input
     return getNextInput(next);
    //return undefined
    }
  }
</script>
</body>
</html>

3.4 其他类型转布尔

 console.log('1' == 1);    //true
  
//  数字转布尔 ,除了0 都是true     NaN 转布尔也是false     NaN != NaN是true
//  字符串转布尔: 出了空的字符串, 都是true   ''  ""
//    null 转布尔一定是false
//    undefined 转布尔一定是false
//    对象 转布尔一定是true

4. 事件流

事件对象需要被分派到事件目标。但是在分派开始之前,必须首先确定事件对象的传播路径,传播路径是事件通过的当前事件目标的有序列表,该传播路径反映文档的分层树结构.

列表中的最后一项是事件目标,并且列表中的前面的项目被称为目标的祖先,其中前面的项目作为目标的父项目.一旦确定了传播路径,事件对象就会经过一个或多个事件阶段。

共有三个事件阶段:捕获阶段,目标阶段和冒泡阶段

捕获阶段:

事件对象通过目标的祖先从窗口传播到目标的父项。这个阶段也被称为捕获阶段

目标阶段:

事件对象到达事件目标。这个阶段也被称为目标阶段。

冒泡阶段:

事件对象以相反的顺序通过目标的祖先传播,从目标的父项开始,以窗口结束。这个阶段也被称为冒泡阶段

5. 事件委托

5.1 什么是事件委托 :

本来是要注册给自己的事件,注册给了父元素.事件触发后的事情,委托给父元素执行

5.2 事件委托的好处 :

  • 代码简洁

  • 节省内存​

5.3 事件委托的原理 :

事件冒泡

事件委托案例:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    
    div {
      border: 1px solid blue;
    }
    
    ul {
      padding: 20px;
    }
    
    li {
      list-style: none;
      margin-top: 10px;
      background-color: skyblue;
      height: 30px;
      line-height: 30px;
    }
    
    button {
      width: 100%;
      height: 50px;
      background-color: white;
    }
  </style>
</head>

<body>

<div>
  <ul id="ul">
    <li>你见,或者不见我 </li>
    <li>我就在那里 </li>
    <li>不悲不喜  </li>
    <li>你念,或者不念我 </li>
    <li>情就在那里  </li>
    <li>不来不去  </li>
    <li>你爱,或者不爱我 </li>
    <li>爱就在那里 </li>
    <li>不增不减 </li>
  </ul>
  <button id="btn">点击加载更多..</button>
</div>

<script>
  var arr = [
    '你跟,或者不跟我',
    '我的手就在你手里',
    '不舍不弃',
    '来我的怀里',
    '或者',
    '让我住进你的心里',
    '默然 相爱',
    '寂静 喜欢',
  ]
  
  //        需求:
  // 1.无序列表中每一个li都有点击事件,点击之后,把对应的文本打印到控制台上
//  //        1. 获取元素 li
//  var lis = document.querySelectorAll('li');
  var ul= document.querySelector('#ul');
//
//  //        2. 给每一个li注册点击事件
//  for (var i = 0; i < lis.length; i++) {
//    lis[i].onclick = fn;
//  }
//  function fn() {
//    //        3. 在事件处理函数中,打印对应的文本
//    console.log(this.innerText);
//  }
  
  //事件委托: 本来是自己做的事件,委托给父级元素
//  事件委托的优点:
//  1. 代码简洁
//  2. 节省内存
//  事件委托的原理:
//    事件流(事件冒泡)
  ul.onclick = function(e){
    //找到点的是那个li
    console.log(e.target.innerText);
  }
  
//  2. 点击按钮,加载更多
//  2.1 获取元素
  var btn = document.querySelector('#btn');
//  2.2 给按钮注册点击事件
  btn.addEventListener('click', function(){
    //  2.3 在事件处理函数中,动态的创建li,添加到ul中
    for(var i = 0; i < arr.length; i++) {
      var li = document.createElement('li');
      li.innerText = arr[i];
//      li.onclick = fn; //由于后创建的没有注册事件,所以必须给他们在注册一遍
      ul.appendChild(li);
    }
  }, false);

</script>
</body>
</html>

优化:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
    <style>
    * {
      margin: 0;
      padding: 0;
    }

    div {
      border: 1px solid blue;
    }

    ul {
      padding: 20px;
    }

    li {
      list-style: none;
      margin-top: 10px;
      background-color: skyblue;
      height: 30px;
      line-height: 30px;
    }

    button {
      width: 100%;
      height: 50px;
      background-color: white;
    }
  </style>

</head>

<body>
  <div>
    <ul id="ul">
      <li>你见,或者不见我 </li>
      <li>我就在那里 </li>
      <li>不悲不喜  </li>
      <li>你念,或者不念我 </li>
      <li>情就在那里  </li>
      <li>不来不去  </li>
      <li>你爱,或者不爱我 </li>
      <li>爱就在那里 </li>
      <li>不增不减 </li>
    </ul>
    <button id="btn">点击加载更多..</button>
  </div>
  <script>
    var arr = [
      '你跟,或者不跟我',
      '我的手就在你手里',
      '不舍不弃',
      '来我的怀里',
      '或者',
      '让我住进你的心里',
      '默然 相爱',
      '寂静 喜欢',
    ]
    var ul = document.querySelector('ul');
    btn.addEventListener('click',fn , false)
    function fn() {
      for (var i = 0; i < arr.length; i++) {
        var li = document.createElement('li');
        ul.appendChild(li);
        li.innerText = arr[i];
      }
    }
    ul.addEventListener('click' , fx , false)
    function fx(e) {
      e = e || window.event
      //判断事件目标是谁. 如果事件目标是ul,则不执行下面的代码
      if(e.target == ul){
        return ;
      }
      var txt = e.target.innerText;
      console.log(txt);
    }

  </script>
</body>

</html>

6. 扩展内容@

6.1 addEventListener 在早期的ie8浏览器中不支持

如果要在ie8中注册多个相同的事件,使用attachEvent这个方法

语法: element.attachEvent('on + 事件名', 事件处理函数);

  //html
   <a id="link" href="">点击在控制台打印1</a>

  //js
  var link = document.getElementById('link');
  link.attachEvent('onclick',function(){
          console.log(1);
          return false; //阻止a标签的默认行为
   });

注意:

  1. 在attachEvent中的第一个参数要加on

  2. 在attachEvent中阻止a标签的默认行为用return false

  3. attachEvent 对应移除事件的方法是detachEvent

  4. 同样,如果要移除事件,在注册事件时,事件处理函数也不要写成匿名函数

6.2 事件对象的兼容性问题

如果在ie8中使用onclick注册事件,那么浏览器不会把事件对象,传递到事件处理函数中,

而是把事件对象绑定到了window的event属性上面.

兼容性的写法:

//html
 <a id="link" href="">点击在控制台打印1</a>

//js
var link = document.getElementById('link');
link.onclick= function(e){
    e = e || window.event; //兼容性的写法
  
}

6.3 pageX,pageY兼容问题

IE8及以前不支持

6.4 target兼容问题

ie8及以前不支持target,使用srcElement代替

//html
 <a id="link" href="">点击在控制台打印1</a>

//js
var link = document.getElementById('link');
link.onclick= function(e){
    e = e || window.event; //兼容性的写法
    console.log(e.target); // 在ie8中是undefined
    console.log(e.srcElement); //兼容性写法, 返回目标元素
    return false;
}
<script>
  var box = document.getElementById('box');
  box.onclick = function(e){
//    console.log(e);
    
    //因为chrome浏览器 比较强大,一方面把事件对象传递到了事件处理函数中,另一方面他也把事件对象赋值给了window.event
//    console.log(window.event);
//    console.log(event);
//    console.log(event === window.event);
//    console.log(event === e);
  }
</script>

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值