js事件(参考加整理......)

300 阅读10分钟

DOM 事件流

事件流包括三个阶段。:事件一开始从文档的根节点流向目标对象(捕获阶段),然后在目标对象上被触发(目标阶段),之后再回溯到文档的根节点(冒泡阶段)。

DOM 事件流

事件捕获阶段(Capture Phase)

事件的第一个阶段是捕获阶段。事件从文档的根节点出发,随着 DOM 树的结构向事件的目标节点流去。途中经过各个层次的 DOM 节点,并在各节点上触发捕获事件,直到到达事件的目标节点。捕获阶段的主要任务是建立传播路径,在冒泡阶段,事件会通过这个路径回溯到文档跟节点。

目标阶段(Target Phase)

当事件到达目标节点的,事件就进入了目标阶段。事件在目标节点上被触发,然后会逆向回流,直到传播至最外层的文档节点。

冒泡阶段(Bubble Phase)

事件在目标元素上触发后,并不在这个元素上终止。它会随着 DOM 树一层层向上冒泡,直到到达最外层的根节点。也就是说,同一个事件会依次在目标节点的父节点,父节点的父节点...直到最外层的节点上被触发。

冒泡过程非常有用。它将我们从对特定元素的事件监听中释放出来,相反,我们可以监听 DOM 树上更上层的元素,等待事件冒泡的到达。如果没有事件冒泡,在某些情况下,我们需要监听很多不同的元素来确保捕获到想要的事件。

所有的事件都要经过捕捉阶段和目标阶段,但是有些事件会跳过冒泡阶段。例如,让元素获得输入焦点的 focus 事件以及失去输入焦点的 blur 事件就都不会冒泡。

事件处理程序

HTML 事件处理程序

<!-- 输出 click -->
<input type="button" value="Click Me" onclick="console.log(event.type)">

<!-- 输出 Click Me this 值等于事件的目标元素 -->
<input type="button" value="Click Me" onclick="console.log(this.value)">
通过 HTML 指定的事件处理程序都需要HTML的参与,即结构和行为相耦合,不易维护。

DOM0 级事件处理程序

<input type="button" value="Click Me" id="btn">
<script>
    var btn=document.getElementById("btn");
    btn.onclick=function(){
        console.log(this.id); // 输出 btn
    }
</script>

这是将一个函数赋值给一个事件处理程序的属性,以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。要删除事件将 btn.onclick 设置为 null 即可。

DOM2 级事件处理程序

DOM2 级事件定义了addEventListener()removeEventListener()两个方法,用于处理和删除事件处理程序的操作。

DOM 节点都包含这两个方法,它们接受3个参数:要处理的事件名作为事件处理程序的函数一个布尔值。最后的布尔值参数是 true 表示在捕获阶段调用事件处理程序,如果是 false(默认) 表示在冒泡阶段调用事件处理程序。

<input type="button" value="Click Me" id="btn">
<script>
   var btn=document.getElementById("btn");
     btn.addEventListener("click",function(){
     console.log(this.id);
     },false);
     btn.addEventListener("click",function(){
     console.log('Hello word!');
     },false);
     </script>

上面代码两个事件处理程序会按照它们的添加顺序触发,先输出 btn 再输出 Hello word!

通过 addEventListener()添加的事件处理程序只能使用 removeEventListener()来移除,移除时传入的参数与添加时使用的参数相同,即匿名函数无法被移除。

<input type="button" value="Click Me" id="btn">
<script>
    var btn=document.getElementById("btn");
    var handler = function(){
        console.log(this.id);
    }
    btn.addEventListener("click", handler, false);
    btn.removeEventListener("click",handler, false);
</script>

IE 事件处理程序

IE通常都是特立独行的,它添加和删除事件处理程序的方法分别是:attachEvent()detachEvent()

同样接受事件处理程序名称与事件处理程序函数两个参数,但跟addEventListener()的区别是:

  • 事件名称需要加“on”,比如“onclick”;
  • 没了第三个布尔值,IE8及更早版本只支持事件冒泡;
  • 仍可添加多个处理程序,但触发顺序是反着来的。

还需要注意,DOM0 和 DOM2 级的方法,其作用域都是在其所依附的元素当中,attachEvent()则是全局,即如果像之前一样使用this.id,访问到的就不是button元素,而是 window,就得不到正确的结果。

跨浏览器事件处理程序

var EventUtil={
    addHandler:function(element,type,handler){
         if(element.addEventListener){
            element.addEventListener(type,handler,false);
    } else if(element.attachEvent){
            element.attachEvent(“on”+ type,handler);
      } else {
            element[“on” + type]=handler;
      }
    },
    removeHandler:function(element,type,handler){
          if(element.removeEventListener){
             element.removeEventListener(type,handler,false);
        } else if(element.detachEvent){
             element.detachEvent(“on”+ type,handler);
        } else {
    element[“on” + type]=null;
        }
      }
    }

事件对象

在触发 DOM 上的某个事件时,会产生一个事件对象 event,这个对象中包含着所有与事件有关的信息。所有的浏览器都支持 event 对象,但支持方式不同。

DOM 中的事件对象

兼容 DOM 的浏览器会将一个 event 对象传入到事件处理程序中。event 对象包含与创建它的特定事件有关的属性和方法。触发的事件类型不一样,可用的属性和方法也不一样。

不过,所有事件都会有下面列出的成员。

  • bubbles (boolean) — 表明事件是否冒泡
  • cancelable (boolean) — 这个变量指明这个事件的默认行为是否可以通过调用 event.preventDefault 来阻止。也就是说,只有 cancelable 为 true 的时候,调用 event.preventDefault 才能生效。
  • currentTarget(element) — 当事件遍历DOM时,标识事件的当前目标。它总是引用事件处理程序附加到的元素,而不是event.target,event.target标识事件发生的元素。
  • defaultPrevented (boolean) — 这个状态变量表明当前事件对象的 preventDefault 方法是否被调用过
  • eventPhase (number) — 这个数字变量表示当前这个事件所处的阶段(phase):none(0),capture(1),target(2),bubbling(3)。
  • preventDefault(function) — 这个方法将阻止浏览器中用户代理对当前事件的相关默认行为被触发。比如阻止<a>元素的 click 事件加载一个新的页面
  • stopImmediatePropagation (function) — 这个方法将阻止当前事件链上所有的回调函数被触发,也包括当前节点上针对此事件已绑定的其他回调函数。
  • stopPropagation (function) — 阻止捕获和冒泡阶段中当前事件的进一步传播。
  • target (element) — 事件起源的 DOM 节点(获取标签名:ev.target.nodeName)
  • type (String) — 事件的名称
  • isTrusted (boolean) — 如果一个事件是由设备本身(如浏览器)触发的,而不是通过 JavaScript 模拟合成的,那个这个事件被称为可信任的(trusted)
  • timestamp (number) — 事件发生的时间

在事件处理程序内部,对象 this 始终等于 currentTarget 的值,而 target 则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素, 则 this、currentTarget、target 包含相同的值。

<input type="button" value="Click Me" id="btn">
<script>
    var btn=document.getElementById("btn");
    btn.onclick = function (event) {
        console.log(event.currentTarget === this); // true
        console.log(event.target === this); // true
    }
</script>

如果事件处理程序存在于按钮的父节点,那么这些值是不同的。

<input type="button" value="Click Me" id="btn">
<script>
    var btn=document.getElementById("btn");
    document.body.onclick = function (event) {
        console.log(event.currentTarget === document.body); // true
        console.log(this === document.body); // true
        console.log(event.target === btn); // true
    }
</script>

在需要通过一个函数处理多个事件时,可以使用 type 属性。

<input type="button" value="Click Me" id="btn">
<script>
    var btn=document.getElementById("btn");
    var handler = function(event) {
        switch (event.type) {
            case "click":
                console.log("clicked");
                break;
            case "mouseover":
                event.target.style.backgroundColor = "red";
                break;
            case "mouseout":
                event.target.style.backgroundColor = "";
                break;
        }
    }
    btn.onclick = handler;
    btn.onmouseover = handler;
    btn.onmouseout = handler;
</script>

跨浏览器的事件对象

EventUtil = {
    addHandler: function(element,type,handler){
         
    },
    removeHandler: function(element,type,handler){
        
    },
    getEvent: function(event){
        return event?event:window.event;
    },
    getTarget: function(event){
        return event.target || event.srcElement;
    },
    preventDefault: function(event){
        if(event.preventDefault){
            event.preventDefault();
        }else{
            event.returnValue = false;
        }
    },
    stopProgagation: function(event){
        if(event.stopProgagation){
            event.stopProgagation();
        }else{
            event.cancelBubble = true;
        }
    }
};

阻止事件冒泡/停止传播(Stopping Propagation)

可以通过调用事件对象的 stopPropagation 方法,在任何阶段(捕获阶段或者冒泡阶段)中断事件的传播。此后,事件不会在后面传播过程中的经过的节点上调用任何的监听函数。

<input type="button" value="Click Me" id="btn">
<script>
    var btn=document.getElementById("btn");
    btn.onclick = function (event) {
        console.log("Clicked"); // 触发
        event.stopPropagation();
    }
    document.body.onclick = function (event) {
        console.log("Body clicked"); // 传播阻断 不触发
    }
</script>

调用 event.stopPropagation()不会阻止当前节点上此事件其他的监听函数被调用。如果你希望阻止当前节点上的其他回调函数被调用的话,你可以使用更激进的 event.stopImmediatePropagation()方法。

阻止浏览器默认行为

  1. 当特定事件发生的时候,浏览器会有一些默认的行为作为反应。最常见的事件不过于 link 被点击。当一个 click 事件在一个<a>元素上被触发时,它会向上冒泡直到 DOM 结构的最外层 document,浏览器会解释 href 属性,并且在窗口中加载新地址的内容。

  2. 在 web 应用中,开发人员经常希望能够自行管理导航(navigation)信息,而不是通过刷新页面。为了实现这个目的,我们需要阻止浏览器针对点击事件的默认行为,而使用我们自己的处理方式。这时,我们就需要调用 event.preventDefault().

  3. 可以阻止浏览器的很多其他默认行为。比如,可以在 HTML5 游戏中阻止敲击空格时的页面滚动行为,或者阻止文本选择框的点击行为。

事件类型

事件的种类可谓相当繁多,不同的事件类型具有不同的信息,常用的大致可分为如下几类:

  • UI:load、 error(错误触发)、select、resize、scroll等

  • 焦点:blur、focus、change(当用户提交对元素值的更改时触发)等

  • 鼠标:click、dblclick、mousedown/up、mouseenter/leave、mousemove、mouseover等

  • 键盘:keydown/up、keypress

  • 触摸:touchstart(即使已经有一个手指放在屏幕上也会触发)、touchend、touchmove等

在触摸屏幕上的元素时事件的发生顺序为:touchstart → mouseover → mousemove(一次) → mousedown → mouseup → click → touchend;

  • 手势:gesturestart、gestureend、gesturechange等

1、gesturestart:当一个手指已经按在屏幕上,而另一个手指又触摸在屏幕时触发。 2、gesturechange:当触摸屏幕的任何一个手指的位置发生变化时触发。3、gestureend:当任何一个手指从屏幕上面移开时触发。注意:只有两个手指都触摸到事件的接收容器时才触发这些手势事件。

事件委托

事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。例如,click 事件会一直冒泡到 document 层次,也就是说,我们可以为整个页面指定一个 onclick 事件处理程序,而不必为每个可点击的元素分别添加事件处理程序。

<ul id="myLinks">
    <li id="goSomewhere">Go somewhere</li>
    <li id="doSomething">Do something</li>
    <li id="sayHi">Say hi</li>
</ul>
<script>
    var list = document.getElementById("myLinks");
    EVentUtil.addHandler (list, "click", function (event) {
        event = EVentUtil.getEvent(event);
        var target = EVentUtil.gitTarget(event);

        switch(target.id) {
            case "doSomething":
                document.title = "I changed the document's title";
                break;
            case "goSomewhere":
                location.href = "https://heycoder.cn/";
                break;
            case "sayHi":
                console.log("hi");
                break;
        }
    })
</script>

常用的操作

load 事件:

load 事件可以在任何资源(包括被依赖的资源)被加载完成时被触发,这些资源可以是图片,css,脚本,视频,音频等文件,也可以是 document 或者 window。

Image 元素 load:

// window onload
EVentUtil.addHandler (window, "load", function () {
    var image = new Image();
    // 要在指定 src 属性之前先指定事件
    EVentUtil.addHandler (image, "load", function () {
        console.log("Image loaded!");
    });
    image.src = "smile.gif";
})
新图像元素不一定要添加到文档后才开始下载,只要设置了 src 属性就会开始下载。

script 元素 load:

// window onload
EVentUtil.addHandler (window, "load", function () {
    var script = document.createElement("script");
    EVentUtil.addHandler (script, "load", function (event) {
        console.log("loaded!");
    });
    script.src = "example.js";
    document.body.appendChild(script);
})
与图像不同,只有设置了 script 元素的 src 属性并将元素添加到文档后,才会开始下载 js 文件,
对于 script 元素而言指定 src 属性和指定事件处理程序的先后顺序就不重要了。

onbeforeunload 事件(HTML事件):

window.onbeforeunload 让开发人员可以在想用户离开一个页面的时候进行确认。这个在有些应用中非常有用,比如用户不小心关闭浏览器的 tab,我们可以要求用户保存他的修改和数据,否则将会丢失他这次的操作。

EVentUtil.addHandler (window, "onbeforeunload", function (event) {
    if (textarea.value != textarea.defaultValue) {
        return 'Do you want to leave the page and discard changes?';
    }
});

需要注意的是,对页面添加 onbeforeunload 处理会导致浏览器不对页面进行缓存,这样会影响页面的访问响应时间。 同时,onbeforeunload 的处理函数必须是同步的(synchronous)。

resize 事件:

在一些复杂的响应式布局中,对 window 对象监听 resize 事件是非常常用的一个技巧。仅仅通过 css 来达到想要的布局效果比较困难。很多时候,我们需要使用 JavaScript 来计算并设置一个元素的大小。

EVentUtil.addHandler (window, "resize", function (event) {
    // update the layout
});

error 事件:

当我们的应用在加载资源的时候发生了错误,我们很多时候需要去做点什么,尤其当用户处于一个不稳定的网络情况下。Financial Times 中,我们使用 error 事件来监测文章中的某些图片加载失败,从而立刻隐藏它。由于“DOM Leven 3 Event”规定重新定义了 error 事件不再冒泡,我们可以使用如下的两种方式来处理这个事件。

imageNode.addEventListener('error', function(event) {
    image.style.display = 'none';
});

获取鼠标在网页中的坐标:

..........................................................................................................................................................................................................................................。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。这个真的太难了。。。