• 一步步学习javascript基础篇(8):细说事件


    终于学到事件了,不知道为何听到“事件”就有一种莫名的兴奋。可能是之前的那些知识点过于枯燥无味吧,说起事件感觉顿时高大上了。今天我们就来好好分析下这个高大上的东西。

    可以说,如果没有事件我们的页面就只能阅读了。有了事件,我们可以通过键盘或是鼠标和页面交互了,通过我们不同的操作页面给出不同的响应。好了,开始我们今天的分析吧。

    DOM0级事件处理方式

    什么是DOM0级?

    其实世上本来没有DOM0级,叫的人多了就有了DOM0级。

    在1998 年 10 月 DOM1级规范成为 W3C 的推荐标准,在此之前的实现我们就习惯称为DOM0级,其实本是没有这个标准的。

    <input type="button" value="but" id="but" />
    <script type="text/javascript">
        document.getElementById("but").onclick = function () {
            alert("点击了按钮1");
        }
        document.getElementById("but").onclick = function () {
            alert("点击了按钮2");
        } 
    </script>

    上面代码,我们发现点击按钮时,仅仅只弹出了“点击了按钮2”。上一个定义的方法被覆盖了。这种会覆盖上一次事件定义的方式我们称为DOM0级事件。

    如果我们使用Jquery来添加事件的话:

    <script src="../Scripts/jquery-1.8.2.js"></script>
    <input type="button" value="but" id="but" />
    <script type="text/javascript">
        $("#but").click(function () {
            alert("点击了按钮1");
        });
        $("#but").click(function () {
            alert("点击了按钮2");
        });

    我们会发现依次弹出了“点击了按钮1”,“点击了按钮2”,这是怎么做到的?为什么没有覆盖上一次的定义,我猜应该是使用到了DOM2级事件机制。(我没有看过Jquery的源码,暂时还看不懂)。

    DOM2级事件处理方式

    <input type="button" value="but" id="but" />
    <script type="text/javascript">      
        document.getElementById("but").addEventListener("click", function () {
            alert("点击了按钮1");
        });
        document.getElementById("but").addEventListener("click", function () {
            alert("点击了按钮2");
        });        
    </script>

    如此通过元素对象的addEventListener添加的方法就是2级事件。这里需要注意,与0级事件不同的是事件名前面不能带“on”了。点击按钮弹出的结果和前面的Jquery添加方式一样。

    有人可能要问了,怎么没有DOM1级事件。我能说的是没有就是没有,没有为什么。在确定DOM1级标准的时候不需要扩展事件定义机制,DOM0级的事件就够用了。

    我们刚才通过2级事件添加了方法,那么如果我们想要删除其中的一个怎么办。如果是上面的那种匿名方法,我可以很明确的告诉你没办法移除。下面我们来说说可以移除的添加方式吧:

    <input type="button" value="but" id="but" />
    <script type="text/javascript">      
        document.getElementById("but").addEventListener("click", fun1);//给click事件添加方法fun1
        document.getElementById("but").addEventListener("click", fun2);//给click事件添加方法fun2
    
        function fun1() {
            alert("点击了按钮1");
        }
        function fun2() {
            alert("点击了按钮2");
        }
    
        document.getElementById("but").removeEventListener("click", fun1);//给click事件移除方法fun1
    </script>

    如此就通过removeEventListener方法进行移除操作了。

    以上只是DOM2级的标准实现,当然除了IE这个怪胎非得当搅屎棍。在IE下,有同效的实现函数:

    <input type="button" value="but" id="but" />
    <script type="text/javascript">      
        document.getElementById("but").attachEvent("onclick", fun1, false);//给click事件添加方法fun1
        document.getElementById("but").attachEvent("onclick", fun2, false);//给click事件添加方法fun2
    
        function fun1() {
            alert("点击了按钮1");
        }
        function fun2() {
            alert("点击了按钮2");
        }
    
        document.getElementById("but").detachEvent("onclick", fun1);//给click事件移除方法fun1

    注意:attachEvent() 和 detachEvent() 替换了addEventListener()和removeEventListener(),且第一个参数是事件名前面加了"on"。

    DOM3级事件

    可能有人觉得很奇怪,哪来的DOM3级事件啊。其实上面我们说的DOM0级和DOM2级事件说的是事件的处理方式而已,而这里说的DOM3级事件是说的在DOM3级中新增的一些事件。

    至于DOM1级事件,那我就真的没听过。

    DOM3级事件是在DOM2级事件的基础上重新定义了或是新增了某些事件。如鼠标事件:

    DOM2有,click、mousedown、mousemove、mouseout、mouseover、mouseup
    而DOM3级中却有,click、dblclick、mousedown、mouseenter、mouseleave、mousemove、mouseout、mouseover、mouseup

    (其中dblclick、mouseenter、mouseleave是DOM3中新增的)
    DOM3级事件实现方式可以用DOM0和DOM2级中的方式,只是新增了写事件类型。这里就不一一列举了。

    事件冒泡

    什么是事件冒泡?我们先来看个例子。

    <div onclick="divfun();" style="border:1px dashed red;padding:50px">
        <span onclick="spanfun();" style="border: 1px dashed #00ff21; padding: 30px; ">
            <input type="button" value="but" onclick="butfun();" style="border: 1px dashed #0094ff" />
        </span>
    </div>
    <script type="text/javascript">
        function butfun() {
            alert("按钮被点击了");
        }
        function spanfun() {
            alert("span被点击了");
        }
        function divfun() {
            alert("div被点击了");
        }
    </script>

    一个div包了一个span,然后span里面包了一个button。我们在点击按钮的时候会先触发按钮事件,然后触发span的点击事件,然后触发div的点击事件。这就是事件冒泡。(使用DOM0级事件默认是事件冒泡方式

    效果图:

    示意图:

    事件捕获

    什么是事件捕获?其实就是事件冒泡的逆向。

    那我们怎么实现这个效果呢?我们可以通过DOM2级事件。上面我们已经简单的讲解过了DOM2级事件的实现方式

    通过addEventListener和removeEventListener给事件添加和删除函数。上面我们讲addEventListener的时候如果你再回头看看,我们只给了两个参数,第一个是事件名,第二个是要添加的方法,其实还有第三个参数一个布尔值用来表示事件流方向(true为事件捕获方向,false为事件冒泡方向)。那么我们完全可以通过DOM2级事件来实现事件捕获的效果,如:

    <div id="mydiv" style="border:1px dashed red;padding:50px">
        <span id="mysapn" style="border: 1px dashed #00ff21; padding: 30px; ">
            <input id="mybut" type="button" value="but" style="border: 1px dashed #0094ff" />
        </span>
    </div>
    <script type="text/javascript">
        document.getElementById("mydiv").addEventListener("click", divfun, true);
        document.getElementById("mysapn").addEventListener("click", spanfun, true);
        document.getElementById("mybut").addEventListener("click", butfun, true);
    
        function butfun() {
            alert("按钮被点击了");
        }
        function spanfun() {
            alert("span被点击了");
        }
        function divfun() {
            alert("div被点击了");
        }
    </script>

    效果图:

    同样,我们也可以通过DOM2级事件实现事件冒泡,就上面的代码仅仅只需要把addEventListener的第三那个参数改成false就可以了,有兴趣的同学可以自己试试。

    然后IE这个搅屎棍又开始不服了,我就不实现事件捕获你能把我怎么着。IE中等效实现的attachEvent的根本就没给第三个参数,所以为了兼容,我们一般只用事件冒泡(IE只支持事件冒泡)。

    事件冒泡的使用

    • 取消事件冒泡

    通过上面,我们知道只要点击了按钮,那么按钮的上层元素中的点击事件都会触发。那我们会想如果我TM就只想点击某个元素的时候才 触发,不想让它往上冒怎么办,请看下面:

    <div id="mydiv" style="border:1px dashed red;padding:50px">
        <span id="mysapn" style="border: 1px dashed #00ff21; padding: 30px; ">
            <input id="mybut" type="button" value="but" style="border: 1px dashed #0094ff" />
        </span>
    </div>
    <script type="text/javascript">
        //第三个参数是false,那么就是事件冒泡了
        document.getElementById("mydiv").addEventListener("click", divfun, false);
        document.getElementById("mysapn").addEventListener("click", spanfun, false);
        document.getElementById("mybut").addEventListener("click", butfun, false);
    
        function butfun(event) {
            event.stopPropagation();//在点击按钮时,取消事件冒泡
            alert("按钮被点击了");
        }
        function spanfun() {
            //这里没有取消事件冒泡,所以点击span的时候还是会继续冒泡到div
            alert("span被点击了");
        }
        function divfun() {
            alert("div被点击了");
        }
    </script>

    效果图:

    您如果仔细看了效果图,那么你会发现。点击按钮的时候并没有冒泡,而点击span的时候还是冒泡了。那是因为我们代码里面在按钮事件的方法里面加了 event.stopPropagation()//取消冒泡 。

    • 事件委托

    既然可以如此,我们可以在每个事件方法里面都加上取消冒泡,那么所有的元素都只实现自己的事件对应的方法了。我们发现这个事件冒泡反而把事情搞麻烦了,好好的一个元素对应一个事件干嘛要冒泡啊,还要手动去取消冒泡。既然有这个东西,它总是有它的作用的。我们看到上面我们定义事件方法时,取到了每个元素,然后给每个元素定义方法。那我们可以通过事件冒泡定义一个方法来实现吗,先看看下面的代码:

    <div id="mydiv" style="border:1px dashed red;padding:50px">
        <span id="mysapn" style="border: 1px dashed #00ff21; padding: 30px; ">
            <input id="mybut" type="button" value="but" style="border: 1px dashed #0094ff" />
        </span>
    </div>
    <script type="text/javascript">
        //第三个参数是false,那么就是事件冒泡了
        document.getElementById("mydiv").addEventListener("click", divfun, false);   function divfun(event) {
            var targetID = event.target.id 
            if (targetID == "mybut") {
                alert("按钮被点击了");
            } else if (targetID == "mysapn") {
                alert("span被点击了"); 
            } else if (targetID == "mydiv") {
                alert("div被点击了"); 
            } 
        }
    </script>

    效果图:

    仔细观察的你,会发现我们点击每个元素会触发对应消息。这就是我们上面为每个元素添加方法然后取消冒泡同样的效果。那么我们再来分析下实现代码,会发现这里反而是使用了冒泡。

     event.target//返回事件的目标节点(触发该事件的节点)  var targetID = event.target.id //返回事件的目标节点的id(触发该事件的节点的Id) 

    因为我们为最外面的div添加了事件的方法,所以我们在点击按钮的时候会依次触发按钮、span、div的点击事件,然按钮和span都没有定义事件方法,所以不管是点击按钮、span还是div都会冒泡到div的点击事件,然后我们就可以根据上面的target属性来得知到底是由那个节点触发的。请看示意图:

    有人会这,这么麻烦有什么好处呢?我们仔细看看这个通过冒泡实现的个节点点击事件,你有没有发现我们仅仅只是通过getElementById取了一个最外层的div元素,且我们也仅仅只用了一个方法(虽然方法里面的逻辑会更加复杂点)。这只是3个元素,如果有30个呢?甚至上百个呢?我们只定义最外层的元素,这样就减少了大量的DOM引用了(这样就直接减少了检索元素需要花的时间),同时也可以减少内存的占用。直接提升了性能。(虽然很多时候我们不会这样来定义事件,我自己就很少这样来定义事件,可能是习惯问题吧。>_<

    这是学习记录,不是教程。文中错误难免,您可以指出错误,但请不要言辞刻薄。

    原文首链:http://www.haojima.net/zhaopei/531.html

    本文已同步至目录索引:一步步学习javascript

  • 相关阅读:
    jQuery 源码解析(二十四) DOM操作模块 包裹元素 详解
    jQuery 源码解析(二十三) DOM操作模块 替换元素 详解
    jQuery 源码解析(二十二) DOM操作模块 复制元素 详解
    jQuery 源码分析(二十一) DOM操作模块 删除元素 详解
    jQuery 源码分析(二十) DOM操作模块 插入元素 详解
    jQuery 源码分析(十九) DOM遍历模块详解
    python 简单工厂模式
    python 爬虫-协程 采集博客园
    vue 自定义image组件
    微信小程序 image组件坑
  • 原文地址:https://www.cnblogs.com/zhaopei/p/javascript_event.html
Copyright © 2020-2023  润新知