JS DOM Event

关于这一篇章有太多关于我来讲杂且乱的学问点,单单是离别DOM层级分别我看过的文章就有(0,2,3)的,(0,2)的,由于本身学问控制还很柔弱所以只能参考他人文章连系本身明白来写,这个中也触及到一点W3C规范制订史的生长,不相识的像我如许的小白肯定会一头雾水啦。

主要的有,DOM层级事宜处置惩罚、事宜流、闇练一些事宜处置惩罚要领等

简介

JavaScript中,最主要的就是对事宜举行处置惩罚。Web运用也是经由历程事宜驱动递次设计其功用的。在事宜驱动递次设计中,须要注册差别事宜的处置惩罚体式格局。
在注册了事宜的处置惩罚体式格局后,阅读器就会在该事宜发作时实行所注册的处置惩罚体式格局。所注册的处置惩罚体式格局被称作事宜处置惩罚递次、事宜句柄或事宜监听器。
JavaScript递次设计的基本内容之一就是猎取须要对事宜举行捕捉的元素,并针对该元素注入响应的事宜处置惩罚递次。

事宜处置惩罚递次/事宜侦听器的设定(DOM 0级/DOM 2级

首先讲DOM 0级事宜处置惩罚递次

对事宜的体式格局被称为事宜处置惩罚递次或事宜侦听器,但这两者之间是有辨别的。

  • 设定要领差别

  • 支撑处置惩罚元素数目差别

    详细点,说一些对事宜处置惩罚举行设定的体式格局

  • 指定为HTML元素的属性(DOM 0级)

  • 指定为DOM 元素的属性(DOM 0级)

  • 经由历程EventTarget.addEventListener举行设定(DOM 2级)

1. 首先是第一个,指定为HTML元素的属性

        <input type="button" id="input" value="Click Me!" onclick="alert('bar');alert('baz')"/>

这个例子中,经由历程字符串对onclick事宜处置惩罚递次将要实行的JavaScript代码举行了设定。假如包括代码,可以分号分开,固然,事宜别的定义一个函数以后再实行该函数的体式格局也不会有题目。

长处:

设定步骤简朴,确保事宜处置惩罚递次会在载入时被设定。假如运用第二种体式格局(DOM元素属性),元素被载入时,其事宜处置惩罚递次可以还没有注册,这时刻用户实行任何本应触发事宜的操纵,也没有效果。

注重:这里的onclick全都是以小写字母誊写。HTML不会辨别大小写字母,所以改写为onClick也没有题目。然则XHTML会辨别大小写字母,所以最好照样运用悉数小写的onclick,进步兼容性。

小运用:假如事宜处置惩罚递次返回一个false值,则会作废该事宜的默许行动,(啥是默许行动,比方点击a标签会跳转链接,表单的提交等这些就属于默许行动),比方:当onsubmit事宜处置惩罚递次返回一个false,表单内容不会被发送,,这可以发明内容有误时返回false作废表单数据发送。或许像下面代码,作废页面跳转。

        <a href="http://www.baidu.com" onclick="return stop();">BAIDU.com</a>
        <script type="text/javascript">
            function stop(e) {
                alert("Stop page transfer");
                return false;
            };

这里放一个W3C链接,有关HTML的事宜属性:HTML 事宜属性

2. 第二种,指定为DOM元素的属性

假如一个页面离别运用了HTML文件和JavaScript文件(常说的关注点星散),则应当尽量少地在HTML文件中运用JavaScript代码,以进步可保护性。因而,最好将事宜处置惩罚递次的设定全都写在JavaScript内。

        <input type="button" id="btn" value="Click Me!" />
        <script type="text/javascript">
            var btn = document.getElementById("btn");
            function btnFn() {
                alert("btnFn");
            }
            btn.onclick = btnFn;

须要注重的是,这里被指定为事宜处置惩罚递次的恰是一个函数。

            btn.onclick = btnFn();          // 这类体式格局指定的是函数的返回值,毛病
            btn.onclick = "btnFn()";        // 以字符串情势指定该函数也是无效的
            btn.onclick = btnFn;            // 一般实行

第一种指定返回值,相称因而在HTML节点属性上设置alert("btnFn"),所以run就会直接弹出窗口。

注重:与经由历程HTML属性设定差别,这里必需悉数运用小写字母(毕竟是JavaScript代码),设定属性以后,HTML标签属性中的内容将被覆写。因而,假如愿望经由历程JavaScript代码在HTML标签属性所指定的内容以后追加新的处置惩罚操纵,仅采纳这类指定DOM元素的要领时很难完成的。然则在DOM2级中可以处理这一题目,后续再说。

第一,第二种体式格局可以对事宜注册林林总总的处置惩罚,但有一个配合瑕玷,关于某一元素的某一事宜,只可以实行一种处置惩罚操纵。

        <input type="button" id="btn" value="Click Me!" />
        <script type="text/javascript">
            document.getElementById("btn").onclick = function () {
                alert("0");
            };
            document.getElementById("btn").onclick = function () {
                alert("1");
            }
            // "1"

总结瑕玷:

  • 标记数目大、可读性、保护性差、不符合关注点星散头脑,不能处置惩罚庞杂的行动、事宜等

3. 第三种,经由历程EventTarget.addEventListener()举行指定(DOM2级

打个脸:此要领不能在IE8之前的阅读器版本中运用,为此可以运用attachEvent()要领。

        <input type="button" id="btn" value="Click Me!" />
        <script type="text/javascript">
            var btn = document.getElementById("btn");
            btn.addEventListener("click",function(){
                alert("click me");
            },false);

语法:element.addEventListener(event, function, useCapture)

  • event必需。字符串,指定事宜名

  • function必需。指定要事宜触发时实行的函数。

  • useCapture可选。布尔值,指定事宜是不是在捕捉或冒泡阶段实行

在DOM2中,useCapture这一参数是必需的。而在DOM3中,假如省略了该参数,则会默许从事宜冒泡阶段最先实行。而在DOM3中省略该参数,则会默许从事宜冒泡阶段最先实行。之前说的HTML属性、DOM元素属性设定事宜的体式格局都会在冒泡阶段最先实行处置惩罚递次。假如愿望在捕捉阶段实行事宜处置惩罚递次的话,只能运用EventTarget.addEventListener()要领了。在IE运用的attachEvent()要领中,是没有与之对应的参数,所以IE中时刻侦听器总是在冒泡阶段被实行。

var btn = document.getElementById("btn");
function handler() {
    alert("btnFn");
};
btn.addEventListener("click",handler,false);
btn.addEventListener("click",handler,false);    // 对雷同的事宜侦听器举行注册
btn.addEventListener("click",handler,true);     // 由于实行阶段差别,则将会被作为另一个事宜侦听器注册

一般来讲实行递次与注册递次雷同,绝大部分阅读器也都是以注册的递次对事宜侦听器实行的。然则关于和实行递次有关的处置惩罚,照样应当放在同一个事宜侦听器中实行,而不应当将它们置于多个差别的事宜侦听器当中。
另外,不能对事宜目的、事宜范例、实行阶段都雷同的对象注册多个雷同的事宜侦听器。以后的注册将被疏忽掉,这类状况下,事宜侦听器注册递次不会变化,实行递次也不会转变。

            var btn = document.getElementById("btn");
            function btnfn1() {
                alert("btnFn1");
            };
            function btnfn2() {
                alert("btnFn2");
            };
            function btnfn3() {
                alert("btnFn3");
            };
            btn.addEventListener("click",btnfn1,false);
            btn.addEventListener("click",btnfn2,false);
            btn.addEventListener("click",btnfn3,false);
            btn.addEventListener("click",btnfn1,false);        // 此次注册将被疏忽
            // btnfn1——btnfn2——btnfn3

发明:假如运用匿名函数情势,则可以对事宜目的、事宜范例、实行阶段都雷同的目的注册雷同的事宜侦听器

将对象注册为时刻侦听器

            var btn = document.getElementById("btn");
            var eventListener = {
                message:"This is an event listener object",
                handleEvent:function (e) {
                    alert(this.message);
                }
            };
            btn.addEventListener("click",eventListener,false);
            // "This is an event listener object"

事宜处置惩罚递次/事宜侦听器内的this援用

在事宜处置惩罚递次内部的this援用的对象是设定了该事宜处置惩罚递次的元素

document.getElementById("btn").onclick = function () {console.log(this);};    // "[object HTMLInputElement]"===this

然则下面状况差别了

            var Listener = function () {};
            var lib = new Object;
            lib.handleClick = function (event) {console.log(this)};
            document.getElementById("btn").onclick = lib.handleClick;

这里 this援用的是#btn,即设定了事宜处置惩罚递次的元素。
假如愿望在lib.handlerClick内经由历程this援用lib,可以加一个匿名函数。

            document.getElementById("btn").onclick = function(event) {
                lib.handleClick(event);
                // lib.handlerClick中的this援用将会援用lib
            };

事宜流

PS:事宜的发作是由用户操纵激发的,在用户阅读网页的历程当中,发作最为频仍的事宜时mousemove事宜。在鼠标指针挪动的历程当中,这一事宜延续发作,假如设定了mousemove事宜的处置惩罚,则有可以致使鼠标挪动速率变慢,要注重这一点。

一个DOM元素被触发的时刻,不只是在它的劈头对象上触发,而是阅历三个差别的阶段,事宜一最先从文档根节点流向目的对象(捕捉阶段),然后到目的对象上被触发(目的阶段),末了又流出到文档根节点(冒泡)。

  • 捕捉阶段:在捕捉阶段中,事宜将会从window对象最先向下遍历DOM树来流传。假如在流向目的对象历程当中碰到设置为捕捉阶段的事宜侦听器,则会直接实行

  • 目的阶段:在这一阶段中,被事宜目的注册的事宜侦听器将会被实行。假如一个事宜处置惩罚递次被指定为了HTML的标签属性,或许指定为对象属性,则会在这一阶段实行

  • 冒泡阶段:这一阶段中,事宜的流传体式格局为从事宜目的最先向上遍历DOM树,直至Window对象完毕。在冒泡阶段碰到设置的事宜侦听器将会被实行。

注重:有些事宜不会经由冒泡阶段,比方,click事宜为了肯定须要触及那些元素而有必要在流传事宜历程当中遍历DOM树,focus事宜则是一种只需处置惩罚当前元素的事宜。这类状况下对focus事宜举行流传是没有意义的,所以focus事宜不会经由冒泡阶段。
《JS DOM Event》

例:

        <div id="div">
            <button id="btn">click me!</button>
        </div>
        <script type="text/javascript">
            var div = document.getElementById("div");
            var btn = document.getElementById("btn");
            // 由内以外
            btn.addEventListener("click",function(e){
                alert("Now to the element BUTTON");
            },false);
            div.addEventListener("click",function(e){
                alert("Now to the element DIV");
            },false);
            document.body.addEventListener("click",function(e){
                alert("Now to the element BODY");
            },false);
            document.documentElement.addEventListener("click",function(e){
                alert("Now to the element DOCUMENT");
            },false);
            // click me!        BUTTON——DIV——BODY——DOCUMENT

由于第三个参数全都设置为冒泡阶段实行,所以实行起来,事宜先从window流入,然则没有发明捕捉阶段的时刻侦听器,继承流入直到碰到目的节点。我们操纵的元素是button,它就是目的节点,所以此时是实行处于目的阶段的目的节点,弹出BUTTON,以后就是事宜流流出历程,也就是冒泡历程,一切设置了冒泡阶段的时刻侦听器将被实行,末了流出window对象。

那假如转变事宜目的节点呢?《JS DOM Event》

我们在DIV上设置CSS款式轻易我们检察,此时目的元素就是DIV。跟着事宜流入…..到目的元素,发明我们的button虽然设置了事宜侦听器,却没有实行。由于此时button已不是目的元素,事宜流经由时,它是处于流入阶段也就是捕捉阶段的,但button是冒泡阶段挪用,所以不会实行button,“疏忽了它”。以后就是老套路:DIV-BODY-DOCUMENT.

接下来我们看看捕捉阶段:

            var div = document.getElementById("div");
            var btn = document.getElementById("btn");
            // 由内以外
            btn.addEventListener("click",function(e){
                alert("Now to the element BUTTON");
            },true);
            div.addEventListener("click",function(e){
                alert("Now to the element DIV");
            },true);
            document.body.addEventListener("click",function(e){
                alert("Now to the element BODY");
            },true);
            document.documentElement.addEventListener("click",function(e){
                alert("Now to the element DOCUMENT");
            },true);
            // click me!        DOCUMENT-BODY-DIV-BUTTON

点击click,事宜流流入,事宜目的是button,则到button之前是捕捉阶段,正好这些元素刚好是在捕捉阶段设定事宜侦听器,则根据DOM数划定规矩,由外至内一层一层实行。然则注重:事宜目的button是在目的阶段实行的,不是捕捉也不是冒泡,不信?我们用eventPhase来测试:《JS DOM Event》

            btn.addEventListener("click",function(e){
                alert("Now to the element BUTTON");
                alert(e.eventPhase);
            },false);
            // 不论是true照样false,只需点击的是button元素,eventPhase都是返回2

找到规律了吗?也没有什么规律,脑子里能模拟出DOM树就都清晰了。

            btn.addEventListener("click",function(e){
                alert("Now to the element BUTTON");
            },false);
            div.addEventListener("click",function(e){
                alert("Now to the element DIV");
                alert(e.eventPhase);
            },true);
            document.body.addEventListener("click",function(e){
                alert("Now to the element BODY");
            },false);
            document.documentElement.addEventListener("click",function(e){
                alert("Now to the element DOCUMENT");
            },true);
            // click me!        DOCUMENT-DIV(2)-BODY

目的节点是DIV,那事宜流流入最深处就是DIV,DIV就是事宜目的,则button基础不会实行,不论它是捕捉照样冒泡,由于它处于DOM树最深处。

怎样作废事宜流流传?

1.Event.stopPropagation()

  • 住手事宜在流传历程的捕捉、目的处置惩罚或起泡阶段进一步流传。挪用该要领后,该节点上处置惩罚该事宜的处置惩罚递次将被挪用,事宜不再被分派到其他节点

  • 该要领将住手事宜的流传,阻挠它被分派到其他 Document 节点。在事宜流传的任何阶段都可以挪用它。注重,虽然该要领不能阻挠同一个 Document 节点上的其他事宜句柄被挪用,然则它可以阻挠把事宜分派到其他节点

                btn.addEventListener("click",function(e){
                    alert("Now to the element BUTTON");
                    e.stopPropagation();
                },false);
                div.addEventListener("click",function(e){
                    alert("Now to the element DIV");
                },false);
                document.body.addEventListener("click",function(e){
                    alert("Now to the element BODY");
                },false);
                document.documentElement.addEventListener("click",function(e){
                    alert("Now to the element DOCUMENT");
                },false);
                // click me!        BUTTON
                

    此时只会在button实行一次事宜侦听器,以后的流传被阻挠了。

然则假如你点击的是DIV,那依旧会往上一层一层实行事宜侦听器,设置stopPropagation()的是button
以此类推…

2. event.stopImmediatePropagation()

假如你愿望阻挠当前节点上的其他回调函数被挪用的话,你可以运用更激进的event.stopImmediatePropagation()要领。

MDN:假如某个元素有多个雷同范例事宜的事宜监听函数,则当该范例的事宜触发时,多个事宜监听函数将根据递次顺次实行.假如某个监听函数实行了 event.stopImmediatePropagation()要领,则除了该事宜的冒泡行动被阻挠以外(event.stopPropagation要领的作用),该元素绑定的后序雷同范例事宜的监听函数的实行也将被阻挠

看MDN这段话我再试着明白明白,一向再说这事宜流传,流传,究竟是只要跨节点的事宜处置惩罚递次被触发才叫流传,同节点上,多个时刻侦听器这个不能算是流传,如许明白就好了。

            btn.addEventListener("click",function(e){
                alert("1");
            },false);
            btn.addEventListener("click",function(e){
                alert("2");
                e.stopPropagation();
                // e.stopImmediatePropagation() 
                alert("2-1");
            },false);
            btn.addEventListener("click",function(e){
                alert("3");
            },false);
            div.addEventListener("click",function(e){
                alert("div");
            },false)

关于e.stopPropagation()而言,它能阻挠的是一切阶段的事宜侦听器流传,效果就是挪用该要领的节点上一切的时刻侦听器可以触发,然则流传被住手,也就是DIV没有了。弹出1-2-(2-3)-3,这些都是btn节点上的。

换到e.stopImmediatePropagation() ,那它就是准确到节点内的事宜侦听器了

            document.documentElement.addEventListener("click",function(e){
                alert("document")
            },true)
            btn.addEventListener("click",function(e){
                alert("1");
            },false);
            btn.addEventListener("click",function(e){
                alert("2");
                e.stopImmediatePropagation(); // 这里今后的节点内事宜侦听器不能实行
                //    e.stopPropagation();
                alert("2-1");
            },false);
            btn.addEventListener("click",function(e){
                alert("3");
            },false);
            div.addEventListener("click",function(e){
                alert("div");
            },false)
            // document/1/2/2-1
            // 阻挠事宜流中当前节点的和一切后续节点的事宜监听器的实行。即影响当前结点的事宜监听器

这就是MDN:该元素绑定的后序雷同范例事宜的监听函数的实行也将被阻挠,这句话的意义了。

默许操纵Event.preventDefault()

在默许状况下,点击a描点元素后,将会跳转至链接页面。而假如在这时刻实行了Event.preventDefault()要领,则不会发作这一行动。这个要领作用等同于让一个指定为了HTML标签属性或DOM属性的事宜处置惩罚递次返回一个false

        <a href="https://segmentfault.com" id="a">segmentfault</a>
        <script type="text/javascript">
            var link = document.getElementById("a");
            function fn(e) {
                alert("不会发作页面跳转");
                event.preventDefault();
            }
            link.addEventListener("click",fn,false);
        </script>

不过也有一些事宜没法经由历程运用preventDefault()要领来中断。blur事宜就是个中之一,它是一个核心挪动至其他元素时被触发的事宜。
stopPropagation()preventDefault()要领不仅可以用于事宜冒泡阶段,在其他阶段中也可以运用这些要领。

IE

IE阅读器下,绑定/删除(attachEvent/detachEvent)、阻挠事宜冒泡(cancelBubble)、阻挠事宜的默许行动(returnValue)、用于猎取事宜的目的(srcElement)等等须要别的讨论,大致雷同。到时刻我另开一篇来继承进修。

Event接口对象

《JS DOM Event》

关于target和currentTarget

这两个属性在差别状况下指向也不尽雷同,先来看看观点

  • target:事宜属性可返回事宜的目的节点(触发该事宜的节点),如天生事宜的元素、文档或窗口。

  • currentTarget 事宜属性返回其监听器触发事宜的节点,即当前处置惩罚该事宜的元素、文档或窗口。
    在捕捉和起泡阶段,该属性黑白常有效的,由于在这两个节点,它差别于 target 属性。

event.currentTarget指向事宜所绑定的元素,而event.target一直指向事宜发作的元素。

        <div id="div">
            <p id="p">
                <button id="btn">click me!</button>
            </p>
        </div>        
          <script type="text/javascript">
            document.getElementById("btn").onclick = function (e) {
                console.log(e.target+" | "+e.currentTarget+" | "+this);
            };
            document.getElementById("p").onclick = function (e) {
                console.log(e.target+" | "+e.currentTarget+" | "+this);
            };
            document.getElementById("div").onclick = function (e) {
                console.log(e.target+" | "+e.currentTarget+" | "+this);
            };
            document.body.onclick = function (e) {
                console.log(e.target+" | "+e.currentTarget+" | "+this);
            };

《JS DOM Event》
不论你在哪一个节点注册事宜侦听器,激发这一系列递次的”罪魁祸首”是button,所以target指向它。假如button节点换成a描点元素,你再点击atarget就指向a(明白为事宜目的也行)
currentTarget指向的是这个事宜流三个阶段中,事宜侦听器绑定的元素

由于我只是开端相识了一下,重点在运用,没有几个实实在在的前端演习是不可的。

现在我只是简朴相识罢了,灵活运用这些要领须要不停的敲敲敲,练项目造轮子。这里引几个文章吧

    原文作者:Queen
    原文地址: https://segmentfault.com/a/1190000009516484
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞