javascript基础:事件流以及常用的事件属性

事件其实前面也说过就是,其实就是在页面上的操作行为,比如滑动鼠标,点击等。给元素添加这些事件称之为注册事件或者绑定事件,前面注册事件的时候很多是直接对元素进行绑定,这些是传统的模式,还有一种方法监听注册方法

注册方式

  • 传统模式注册

    前面使用的事件注册方式都是传统的注册方式,比如注册onclick等。

    这种注册其实具有唯一性,那就是只能注册一种事件一次,如果这样说可能有点懵,所以演示一下。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>测试文档</title>
        <style>
    
        </style>
    </head>
    <body>
    
    <button  id="bt">发布</button>
    
    
    <script>
      var bn1=document.getElementById("bt");
      bn1.onclick=function () {
            
            
       alert("test1");
      }
       bn1.onclick=function () {
            
            
       alert("test2");
      }
    </script>
    </body>
    </html>
    

    在这里插入图片描述

    可以看出传统注册事件,只会执行其最一次注册的方式。

    **补充:注册事件的唯一性是同一个元素注册同一个事件。**如果同一个事件注册多次,在传统模式中只有最后事件才能生效。

  • 方法监听注册

    这个是w3c标准推荐的方式,其通过addEventListener()这个方法进行实现,也有兼容问题,那就是IE9之前的版本不支持,可以用attachEvent代替,不过目前IE9的浏览器很少了,以及官方也不推荐使用,知道即可。

    EventTarget.addEventListener() 方法将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行。

    格式如下:

    target.addEventListener(type, listener, options);
    target.addEventListener(type, listener, useCapture);
    target.addEventListener(type, listener, useCapture, wantsUntrusted);  // Gecko/Mozilla only
    
    • type:
      表示监听事件类型的字符串。其实一些前面使用的比如onclick,而这个参数是click,说一个不负责的话有些常用的在传统事件注册的时候,将前面的on去掉就是这事件监听中使用,其事件具体有的可以看一下官网:https://developer.mozilla.org/zh-CN/docs/Web/Events

    • options 可选参数
      一个指定有关 listener 属性的可选参数对象。可用的选项如下:
      1:capture: Boolean,表示 listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发。
      2:once: Boolean,表示 listener 在添加之后最多只调用一次。如果是 true, listener 会在其被调用之后自动移除。
      3:passive: Boolean,设置为true时,表示 listener 永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。查看 使用 passive 改善的滚屏性能 了解更多.
      4:signal:AbortSignal,该 AbortSignal 的 abort() 方法被调用时,监听器会被移除。
      5: mozSystemGroup: 只能在 XBL 或者是 Firefox’ chrome 使用,这是个 Boolean,表示 listener 被添加到 system group。

    • useCapture 可选
      Boolean,在DOM树中,注册了listener的元素, 是否要先于它下面的EventTarget,调用该listener。 当useCapture(设为true) 时,沿着DOM树向上冒泡的事件,不会触发listener。当一个元素嵌套了另一个元素,并且两个元素都对同一事件注册了一个处理函数时,所发生的事件冒泡和事件捕获是两种不同的事件传播方式。事件传播模式决定了元素以哪个顺序接收事件。进一步的解释可以查看 事件流 及 JavaScript Event order 文档。 如果没有指定, useCapture 默认为 false 。

    • wantsUntrusted Non-Standard

      如果为 true , 则事件处理程序会接收网页自定义的事件

    来一个例子,证明可以注册多次

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>测试文档</title>
        <style>
    
        </style>
    </head>
    <body>
    
    <button  id="bt">发布</button>
    
    
    <script>
      var bn1=document.getElementById("bt");
      bn1.addEventListener("click",function(){
            
            
          alert("test1");
      })
      function test2(){
            
            
           alert("test2");
      }
      // 也可以这样注册
      bn1.addEventListener("click",test2)
    
    </script>
    </body>
    </html>
    

在这里插入图片描述

可以看出同一个元素同样的数据可以通过监听注册多次。

为了解决兼容问题一般会这样写:

   function addEventListener(element,eventName,fn ) {
//  首先满足大多数的浏览器
        if(element.addEventListener){
            element.addEventListener(eventName,fn);
        }else if(element.attachEvent ){
            element.attachEvent('on'+eventName,fn);
        }else{
            //相当于类似element.οnclick=fn
            element['on'+eventName]=fn;
        }
    }

删除事件

  • 传统模式下删除事件,相对很简单。演示如下:

      var bn1=document.getElementById("bt");
        bn1.onclick=function () {
          
          
            alert("test1");
           // 重新对事件进行定义赋值即可
            bn1.onclick=null;
        }
    
  • 方法监听注册

    这个在删除事件的时候,就很难用重新赋值的方法了,毕竟可以注册多次。所以就需要用一个方法,格式如下:

    target.removeEventListener(type, listener[, options]);
    target.removeEventListener(type, listener[, useCapture]);
    

    演示:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>测试文档</title>
        <style>
    
        </style>
    </head>
    <body>
    
    <button  id="bt1">发布</button>
    <button  id="bt2">发布</button>
    
    <script>
        var bn1=document.getElementById("bt1");
        var bn2=document.getElementById("bt2");
    
        bn1.addEventListener("click",function(){
            
            
            alert("bt1_test1");
            bn1.removeEventListener("click",function(){
            
            
            alert("bt1_test1");
        },false)
        },false)
        // 这样不会删除监听的,因为注册的时候是匿名函数,而这样删除也是匿名,两个匿名无法证明是同一个事件
    
    
        function test2(){
            
            
             alert("bt1_test2");
                // 删除事件的通过函数名  其实后面的参数 false 和非false 有区别的 移出事件的时候需要注意,这个涉及事件 捕获和冒泡下来聊
               bn2.removeEventListener("click",test2,false);
        }
        bn2.addEventListener("click",test2,false);
    
    
    
    </script>
    </body>
    </html>
    
    

在这里插入图片描述

为了兼容性如下写

    function removeEventListener(element,eventName,fn ) {
//  首先满足大多数的浏览器
        if(element.removeEventListener){
            element.removeEventListener(eventName,fn);
        }else if(element.detachEvent ){
            element.detachEvent('on'+eventName,fn);
        }else{
            //相当于类似element.οnclick=null;
            element['on'+eventName]=null;
        }
    }

DOM事件流

什么是事件流?

事件流是描述是从页面中接收事件的顺序。

在DOM中事件发生时会在元素节点之间按照待定的顺序传播,这个传播过程称之为DOM事件流。

可以先看图:

在这里插入图片描述

简单的DOM事件流分为3个阶段:

  • 捕获阶段 (capture phrase):网景最早提出,由DOM最顶层节点开始,然后逐级向下传播到最具体(也就是触发的元素)元素接受的过程。
  • 当前目标阶段(target phrase):触发事件的元素。
  • 冒泡阶段(bubbling phrase):IE最早提出的,事件开始时由最具体的元素接收,然后逐级向上传播到DOM最顶层节点的过程。

注意:

  • JS代码中只能执行捕获或者冒泡其中的一个阶段。
  • onclike和attachEvent只能得到冒泡阶段。而且有些事件时没有冒泡的比如:onblur,onfocus,onmouseenter,onmouseeleave等。
  • target.addEventListener(type, listener[, useCapture]) 第三个参数如果true,表示在事件捕获阶段调用事件处理程序。如果false(不写默认false)表示在事件冒泡阶段调用事件处理程序。

说实话如果单独看理论都会懵,所以具体通过演示理解。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试文档</title>
    <style>
        [id^="father"]{
      
      
            width: 400px;
            height: 400px;
            background-color: #4a90e2;
            border: 1px solid red;
        }
        [id^="son"]{
      
      
            margin: 100px;
            width: 200px;
            height: 200px;
            background-color: green;
        }
    </style>
</head>
<body>
<div id="father1">
    <div id="son1"></div>
</div>
<hr>
<div id="father2">
    <div id="son2"></div>
</div>

<script>
    var f1=document.getElementById("father1");
    var s1=document.getElementById("son1");
    s1.addEventListener('click',function () {
      
      
        alert("son1")
    },false)
    // 如果不再父标签上添加一个同样的事件,那么所谓的事件流也就很难体现了
    f1.addEventListener('click',function () {
      
      
        alert("father1")
    },false)

    var f2=document.getElementById("father2");
    var s2=document.getElementById("son2");
    s2.addEventListener('click',function () {
      
      
        alert("son2")
    },true)
    // 如果不再父标签上添加一个同样的事件,那么所谓的事件流也就很难体现了
    f2.addEventListener('click',function () {
      
      
        alert("father2")
    },true)


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

在这里插入图片描述

如果这样看,事件流就是在父类和子类中绑定相同的事件,在触发的时候时先在父类响应还是子类中响应罢了。

其实这个会涉及到一个类似委派机制的东西,就是将有些事情自己不做,让其其它层级中处理,后面会演示。

其实在很多时候开发的时候更加关注的时冒泡阶段而不是事件捕获。

事件对象

只要有了事件就会创建事件对象,而且他是系统自动给我创建的,不需要参数传递。其包含了某个事件的相关数据。

这句话如果简单的说可能有点懵,先看代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试文档</title>
    <style>
        [id^="father"]{
      
      
            width: 400px;
            height: 400px;
            background-color: #4a90e2;
            border: 1px solid red;
        }
        [id^="son"]{
      
      
            margin: 100px;
            width: 200px;
            height: 200px;
            background-color: green;
        }
    </style>
</head>
<body>
<div id="father1">
    <div id="son1"></div>
</div>
<hr>
<div id="father2">
    <div id="son2"></div>
</div>

<script>

    var s1=document.getElementById("son1");
    var s2=document.getElementById("son2");
    s1.addEventListener('click',function () {
      
      
        console.log("----son1----");
        console.log(arguments);

    },false)
    s2.onclick=function(){
      
      
        console.log("----son2----");
        console.log(arguments);
    }


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

在这里插入图片描述

可以看出无论是传统注册方式还是监听注册方式,调用的方法默认都会传递一个参数,这个参数其实就是事件对象。

所以一般使用事件对象的时候,就会在触发事件的方法中写一个参数。

//event 只是一个参数名 可以随便写
s1.addEventListener('click',function (event) {
        console.log("----son1----");
        console.log(event);

    },false)
s2.οnclick=function(event){
        console.log("----son2----");
        console.log(event);
    }

这个事件对象,包含的事件的所有信息,可以在控制台看一下:

在这里插入图片描述

比如上面是鼠标点击行为,以及鼠标点击的行为,以及鼠标点击的元素等信息。

但是也有兼容性,在ie低版本中无法实现,所以兼容写法如下

function(event){
       event=event||widow.event;
        console.log(event);
    }

所以可以看出event对象代表事件的状态,比如键盘按键的状态,鼠标的位置,鼠标的状态等。

事件对象常用常见的属性或方法

事件属性方法 描述
event.target 返回触发事件的对象 标准 (有兼容性)
event.currentTarget 它总是指向事件绑定的元素,而 Event.target 则是事件触发的元素
event.srcElement 返回触发事件的对象 非标准IE6-8 (有兼容性)
event.type 返回事件的类型比如click ,mouseover等
event.cancelBubble 该属性阻止冒泡件 非标准IE6-8 (有兼容性)
event.stopPropagation() 该属性阻止冒泡件 标准 (有兼容性)
event.returnValue 该属性阻止默认事件(默认行为) 非标准 (有兼容性) 比如不让链接跳转等
event.preventDefault() 该属性阻止默认事件(默认行为) 标准 (有兼容性) 比如不让链接跳转等

event.target 和event.currentTarget

看一下两者的区别,顺便也看一下与this是否有关。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试文档</title>
    <style>
        [id^="father"]{
      
      
            width: 400px;
            height: 400px;
            background-color: #4a90e2;
            border: 1px solid red;
        }
        [id^="son"]{
      
      
            margin: 100px;
            width: 200px;
            height: 200px;
            background-color: green;
        }
    </style>
</head>
<body>
<div id="father1">
    <div id="son1"></div>
</div>
<hr>
<div id="father2">
    <div id="son2"></div>
</div>

<script>

    var s1=document.getElementById("son1");
    var f2=document.getElementById("father2");
    s1.addEventListener('click',function (event) {
      
      
        console.log("绑定在子盒子上点击子元素");
        console.log("this==   ",this);
        console.log("event.target==   ",event.target);
        console.log("event.currentTarget==   ",event.currentTarget);
        console.log("event.currentTarget==this   ",event.currentTarget==this);

    },false)
    f2.onclick=function(event){
      
      
        console.log("绑定在父盒子上点击子元素");
        console.log("this==   ",this);
        console.log("event.target==   ",event.target);
        console.log("event.currentTarget==   ",event.currentTarget);
        console.log("event.currentTarget==this   ",event.currentTarget==this);

    }

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

在这里插入图片描述

可以看出target 是触发事件的元素,而currentTarget是事件绑定的元素其与this是相同的。

event.type

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试文档</title>
    <style>
        [id^="father"]{
      
      
            width: 400px;
            height: 400px;
            background-color: #4a90e2;
            border: 1px solid red;
        }
        [id^="son"]{
      
      
            margin: 100px;
            width: 200px;
            height: 200px;
            background-color: green;
        }
    </style>
</head>
<body>
<div id="father1">
    <div id="son1"></div>
  
</div>

<script>

    var s1=document.getElementById("son1");
    var f2=document.getElementById("father2");
    s1.addEventListener('click',function (event) {
      
      
        console.log(event.type);

    },false)



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

在这里插入图片描述

可以看出得到的是事件的操作类型。

阻止默认事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试文档</title>
    <style>
        [id^="father"]{
      
      
            width: 400px;
            height: 400px;
            background-color: #4a90e2;
            border: 1px solid red;
        }
        [id^="son"]{
      
      
            margin: 100px;
            width: 200px;
            height: 200px;
            background-color: green;
        }
    </style>
</head>
<body>
<p>Please click on the checkbox control.</p>

<form>
    <label for="id-checkbox">Checkbox:</label>
    <input type="checkbox" id="id-checkbox"/>
</form>

<div id="output-box"></div>

<script>

       document.querySelector("#id-checkbox").addEventListener("click", function(event) {
      
      
         document.getElementById("output-box").innerHTML += "Sorry! <code>preventDefault()</code> won't let you check this!<br>";
         event.preventDefault();
}, false);

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

在这里插入图片描述

因为这个兼容性,所以如下写:

// 例子:
ele.οnclick=function(event){
     //标准模式
    event.preventDefault();
     // 非标模式 低版本
    event.returnValue;
    
    // 还有一种只能在传统模式下使用  return false;  但是后面语句就无法在运行了。 
    
}

阻止冒泡

冒泡行为有好,自然也有不好的地方,所以有些时候需要将其禁止。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试文档</title>
    <style>
        [id^="father"]{
      
      
            width: 400px;
            height: 400px;
            background-color: #4a90e2;
            border: 1px solid red;
        }
        [id^="son"]{
      
      
            margin: 100px;
            width: 200px;
            height: 200px;
            background-color: green;
        }
    </style>
</head>
<body>
<div id="father1">
    <div id="son1"></div>
</div>

<script>
    var f1=document.getElementById("father1");
    var s1=document.getElementById("son1");
    s1.addEventListener('click',function (event) {
      
      
        alert("son1");

        if(event=event || event.stopPropagation){
      
      
            // 标准下 取消冒泡
            event.stopPropagation();
        }else {
      
      
            //非标准下取消冒泡
            event.cancelBubble=true;
        }


    },false)
    // 如果不再父标签上添加一个同样的事件,那么所谓的事件流也就很难体现了
    f1.addEventListener('click',function () {
      
      
        alert("father1");
    },false)


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

在这里插入图片描述

委托机制

其实冒泡方式会有个类似委托的机制,简单的说就是父类下的子类有一些操作,可以交个父类操作。还是那句话用例子演示。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试文档</title>
    <style>
        li{
            list-style: none;
        }
    </style>
</head>
<body>
<ul id="test_father">
    <li>艾尔登法环玩家:宫崎老贼你个老yb</li>
    <li>战神玩家:卧槽,你惹奎爷干嘛。</li>
    <li>塞尔达玩家:再说一遍,主人公叫林克,不叫塞尔达</li>
    <li>神秘海域玩家:卧槽,真是臂力惊人,不亏为神秘臂力。</li>

</ul>

<script>
    var ele_ul=document.getElementById("test_father");


    ele_ul.addEventListener("click",function (event) {
        event.target.style.backgroundColor="red"
        alert(event.target.textContent);

    },false)
</script>
</body>
</html>

在这里插入图片描述

鼠标事件对象

在这里插入图片描述

很多时候,无论是划过还是点击等都会有一个作品,前面聊过鼠标的一些触发行为,而现在搞一个x和y周坐标的例子。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试文档</title>
    <style>
      img{
      
      
          position: absolute;
          display: none;
          width: 100px;
          transform:translate(-50%,-50%);
          cursor: pointer;
      }
    </style>
</head>
<body>
<img id="fly" src="jpg/hudie.gif">

<script>

    var ele_fly=document.getElementById("fly")

    document.addEventListener("mousemove", function (event) {
      
      
        console.log(event.clientY);
      ele_fly.style.display="block";
      ele_fly.style.top=event.clientY+"px";
      ele_fly.style.left=event.clientX+"px";

    },false)
</script>
</body>
</html>

在这里插入图片描述

键盘事件

前面聊了键盘事件,现在再聊一下,如下三个:

在这里插入图片描述

现在来弄一下其事件触发的时间前后,来一个例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试文档</title>

</head>
<body>
<input  id="test" type="text">

<script>

    var ele_test=document.getElementById("test")
    ele_test.addEventListener("keyup", function () {
      
      
        console.log("keyup事件");
    });
    ele_test.addEventListener("keypress", function () {
      
      
        console.log("keypress事件");
    });
    ele_test.addEventListener("keydown", function () {
      
      
        console.log("keydown事件");
    });

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

演示按非功能键:

在这里插入图片描述

如果点击功能键:

在这里插入图片描述

可以看出执行顺序是:keydown--》keypress--》keyup.

当然既然监控键盘,自然就可以监控你按了那个键,这个就需要事件的key。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试文档</title>

</head>
<body>
<input  id="test" type="text">

<script>

    var ele_test=document.getElementById("test")
    ele_test.addEventListener("keyup", function (event) {
      
      
        console.log("keyup事件:" ,event.key);
    });
    ele_test.addEventListener("keypress", function (event) {
      
      
        console.log("keypress事件",event.key);
    });
    ele_test.addEventListener("keydown", function (event) {
      
      
        console.log("keydown事件",event.key);
    });

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

在这里插入图片描述

以前是keycode返回的ASCII对应的数字,而且keyup和keydown还不识别大小写 ,不过目前和key一样都返回内容了,而且也都识别大小写了,这样方便了很多。

这个监控其实京东有一个隐藏的监控那就是按键s会自动聚焦到搜索栏, 不再演示了。

猜你喜欢

转载自blog.csdn.net/u011863822/article/details/124330769