事件流

事件流是指从页面接受事件的顺序
图片描述

一般考虑兼容性问题,会使用冒泡而不适用捕获

事件冒泡event bubbling

事件开始时由具体的元素(嵌套层次最深的元素)接受,然后逐级向上传播到文档document

图片描述

事件捕获

基本跟事件冒泡相反,事件捕获用于在于事件到达预定目标之前捕获他,document首先接收到事件,然后事件依次向里传递,一直到传播事件的实际目标,例如这个div是我们点击的那个div

图片描述
事件捕获用的不多

dom事件流

DOM2级事件,规定的事件流包括三个阶段

  • 事件捕获阶段

  • 处于目标阶段

  • 事件冒泡阶段

图片描述

其实每一个事件都是有这么一个阶段转换过程的

事件处理程序

html事件处理 (不建议使用)

即在html中写的事件
两种使用方式

<input typeof="button" value="click me" onclick="alert('click')"/>   
<script>
function showMessage() {
    alert("Hello");
}
</script>
<input typeof="button" value="click me" onclick="showMessage()"/>

缺点:不方便mvc分离,代码混乱

DOM0级事件处理程序

  • 将一个函数赋值给一个事件处理程序属性

  • 主动式的事件处理

    var btn = document.getElementById("myBtn");
    //主动赋值给onclick属性
    btn.onclick = function () {
        console.log("Clicked");
    }
    btn.onclick = null; // 删除事件处理程序

DOM2级事件处理程序

  • addEventListener() 添加事件处理程序监听

  • removeEventListener() 移除事件处理程序监听

  • 所有DOM节点中都包含这两个方法

  • 被动式的事件处理

  • 可以添加多个多个事件处理程序,他们会按照添加的顺序触发

  • 大多数情况下,都是将时间处理程序添加到事件流的冒泡阶段,这样可以最大限度的兼容各种浏览器.

  • 最好只在需要在事件到达目标之前截获他的时候将时间处理程序添加到捕获阶段

        var btn = document.getElementById("myBtn");
        //三个参数,要处理的事件名,作为事件处理程序的函数和一个布尔值
        btn.addEventListener("click",function () {
            alert(this.id);
            //true表示捕获阶段调用事件处理程序,false表示在冒泡阶段调用事件处理程序
        },false)

removeEventListener只能移除添加时传入的函数,所以匿名函数是没办法移除的(匿名函数没有办法确定是同一个)

    var btn = document.getElementById("myBtn");
    //将匿名函数改成有名函数
    var handler = function () {
        alert(this.id);
    };
    btn.addEventListener("click",handler,false);
    //可以移除
    btn.removeEventListener("click",handler,false);

事件对象

在触发DOM上的某个事件时,会产生一个事件对象event,这个对象包含着所有与事件相关的信息. 事件属性查看

    var btn = document.getElementById("myBtn");
    //这个event就是事件对象,里面的属性可以直接调用查看
    btn.onclick = function (event) {
        alert(event.type);//返回click
    };
    btn.addEventListener("click",function (event) {
        alert(event.type);//返回click
    },false)

如果点击一个在body里面的myBtn元素:

    document.body.onclick = function (event) {
        console.log(event.currentTarget === document.body); //返回true
        console.log(this === document.body); //返回true
        console.log(event.target === document.getElementById("myBtn")); //返回true
    }

1.currentTarget代表事件处理程序当前正在处理的事件的那个元素,那么本例子的话就是body,另外this也是body,因为事件处理程序是注册到body 上的
2.target是事件的目标,即点击的那个元素,就是myBtn
3.需要注意的是即使点击了一个元素,但是因为最终都会冒泡到body那里,所以事件处理处理会在冒泡到最外层进行处理

通过判断事件对象的type来触发不同的事件处理程序,用于需要通过一个函数处理多个事件时.

    var btn = document.getElementById("myBtn");
    var handler = function (event) {
        switch (event.type) {
            case "click":
                alert("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;

阻止特定事件的默认行为preventDefault()

默认行为可以使点击a标签的时候跳转到另外一个页面,或者内容超出一定大小的滚动事件

//假设myLink是一个a标签
    var link = document.getElementById("myLink");
    link.onclick = function (event) {
        //阻止了url 默认行为跳转
        event.preventDefault();
    };

需要注意的是只有cancelable属性设置为true的事件,才可以使用preventDefault(),这个属性可以在浏览器调试模式里面看,也可以打印出来

停止事件在DOM层次中传播,例如事件冒泡

    var btn = document.getElementById("myBtn");
    btn.onclick = function (event) {
        alert("mgBtn click");
        //如果没有屏蔽传播的话,会出现两次alert,因为事件会传播到body,也会被触发然后执行
        event.stopPropagation();
    };
    document.body.onclick = function (event) {
        alert("body click");
    }

事件对象的eventPhase属性

用来确定事件当前正位于事件流的哪个阶段

  • 捕获阶段调用的事件处理程序 值为1

  • 事件处理程序处于目标对象上 值为2

  • 在冒泡阶段调用的事件处理程序 值为3

    var btn = document.getElementById("myBtn");
    btn.onclick = function (event) {
        alert("btn:" + event.eventPhase); //返回2
    };
    document.body.addEventListener("click", function (event) {
        alert("body-1:" + event.eventPhase);//返回1
    }, true); // 这里是true代表在捕获阶段

    document.body.onclick = function (event) {
        alert("body-2:" + event.eventPhase);//返回3
    }
  1. 首先是返回1,因为是在捕获阶段触发,按照流程上是先捕获然后再到目标再到冒泡的,而捕获阶段是从最外层往里传播的,所以是body

  2. 然后是到达了目标myBtn,所以返回2
    3.再到返回3,冒泡阶段,是从最里往外传播,最里的是myBtn,最外是body,所以触发了最后的body事件处理程序

ie事件(暂时不看)

事件类型

ui事件

  • load
    当页面完全加载后在window上面触发(包括所有img,js,css等)

    window.addEventListener("load",function (event) {
        alert("loaded");
    })

在DOM2级事件规范,应该在document而非window上面触发load事件,但是所有浏览器都在window上面实现了该事件,所以这样用确保兼容性

也可以单独对某个元素的加载进行触发

    var img = document.getElementById("myImg");
    img.addEventListener("load",function (event) {
        //这个加载是指图片完全加载完成,包括下载成功
        alert(event.src);
    });
    window.addEventListener("load", function (event) {
        //创建了img元素
        var img = document.createElement("img");
        //添加load事件监听
        img.addEventListener("load", function (event) {
            alert("aa");
        });
        //输出到dom树
        document.body.appendChild(img);
        //设置src后,img才会开始加载
        img.src = "https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png";
    })

img只有设置了src属性才会开始下载
script文件的话,只有设置了src并且放到dom树里面才会开始下载

确定js文件是否加载成功

    window.addEventListener("load", function (event) {
        //创建了script元素
        var script = document.createElement("script");
        //添加load事件监听
        script.addEventListener("load", function (event) {
            alert("Loaded");
        });
        //script设置src后,并且添加到dom树后才会开始加载
        script.src = "https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.js";
        //输出到dom树
        document.body.appendChild(script);
    })
  • unload
    与load事件对应,这个事件在文档被完全卸载后触发,例如用户从一个页面切换到另外一个页面,一般用于清楚引用,避免内存泄露

    window.addEventListener("unload", function (event) {
        alert("aaa");
    })
  • abort
    在用户停止下载过程时,如果嵌入的内容没有加载完,则在对象元素上触发

  • error

    • 当发生js错误时在window上面触发

    • 当无法加载图像时,在img触发

    • 如果嵌入的内容没有加载完,则在对象元素上触发

  • select
    当用户选择文本框 input或texterea中的一个或多个字符时触发

  • resize
    调整浏览器窗口的高度或者宽度的时候触发

  • scroll
    当用户滚动带滚动条的元素中的内容时,在该元素上触发

焦点事件

  • blur 在元素失去焦点时触发

  • DOMFocusIn 用下面的

  • DOMFocusOut 用下面的

  • focus 在元素获得焦点时触发,这个事件不会冒泡

  • focusin 在元素获得焦点时触发,会冒泡

  • focusout 在元素失去焦点时触发

即使focus和blur不冒泡,但是也可以在捕获阶段侦听到

鼠标事件

  • click 单击

  • dblclick 双击

  • mousedown 用户按下了鼠标按钮

  • mouseenter 鼠标从元素外部首次移动到元素范围之内

  • mouseleave 鼠标从元素范围内移动到元素范围外

  • mousemove 鼠标在元素内部移动时重复触发

  • mouseout 鼠标在元素上方

  • mouseover 当鼠标指针位于一个元素外部,然后用户将其首次移入另外一个元素边界之内触发

  • mouseup 用户释放鼠标按钮时触发

除了mouseenter 和mouseleave,所有鼠标事件都会冒泡

触摸设备

  • 轻击可单击元素会触发mousemove事件,如果此操作会导致内容变化,将不再有其他事件发生,如果屏幕没有因此变化,那么会依次发生mousedown,mouseup和click事件

  • 轻击不可单击元素不会触发任何事件

  • 两个手指放在屏幕上且页面随手指移动而滚动时会触发mousewheel和scroll事件

滚轮事件

mousewheel,值是event.wheelDelta,方向是用正负判断

文本事件和键盘事件

  • keydown 当用户按下减胖上的任意键时触发,而且如果按住不放的话会重复触发

  • keypress 当用户按下键盘的字符键时触发,而且如果按住不放的话会重复触发

  • keyup 当用户释放键盘上的按键时触发
    首先会触发keydown,然后keypress,然后keyup

  • 需要知道键码,对应不同的按键

  • 字符编码(ASCII编码),用户按键后输入的屏幕文本的显示内容,用charCode属性,用String.fromCharCode()转换为实际的字符

  • 只有一个文本事件,textInput,用户按下能够输入实际字符的键时才会触发

  • textInput的事件的对象里面有一个属性inputMethod,用来分辨文本输入到文本框的方式

0   不确定是怎么输入
1   键盘输入
2   文本粘贴
3   拖放进来的
4   IME输入
5   通过表单中选择某一项输入
6   手写输入
7   语音输入
8   通过几种方式组合输入
9   通过脚本输入

变动事件

dom变动的时候触发

  • 删除节点
    replaceChild的时候

  • 插入节点
    appendChild(),replaceChild()或insertBefor()的时候

html5事件

  • DOMContentLoaded事件,相对于load事件会在页面中的一切都加载完成后触发,这个的话,就只会在dom树加载完成后触发,不理会其他img,js文件,css文件之类的
    jquery的document.ready()的加载方式就是使用跟这个

  • pageshow和pagehide事件
    在页面显示触发(在load之后)和在页面隐藏时触发(在unload之前)

  • hashchange事件
    url参数列表的锚点!!!变化的时候通知开发人员,锚点是#这样的

    window.addEventListener("hashchange", function (event) {
        console.log(event); //返回一个对象
//        console.log("old url:" + event.oldURL + "new url:" + event.newURL);
    },false);

返回的对象里面有很多有用的信息,例如

newURL
:
"http://localhost:63342/test-webstorm/test5.html?_ijt=dhn30q02o026mo2upn01tav7jn#dsad"
oldURL
:
"http://localhost:63342/test-webstorm/test5.html?_ijt=dhn30q02o026mo2upn01tav7jn"

设备事件

跳过

触摸手势事件

  • touchstart 当手指触摸屏幕时触发

  • touchmove 当手指滑动时连续触发

  • touchend 当手指离开屏幕时触发

  • touchcancel 当系统停止跟中触摸时触发
    每个触摸事件都有一些属性

  • clientX,clientY(视口),screenX,screenY(屏幕),pageX,pageY(页面) 一些坐标属性

  • target 触摸的dom节点目标

  • touches 表示当前跟踪的触摸操作的touch对象数组,每次触摸都会生成一个对象数组,这个对象数组只有一个对象,所以用[0]调用

  • targetTouches 特定于事件目标的touch对象的数组

  • changeTouches 一个 TouchList 对象,包含了代表所有从上一次触摸事件到此次事件过程中,状态发生了改变的触点的 Touch 对象,每次触摸都会生成一个对象数组,这个对象数组只有一个对象,所以用[0]调用

    function handleTouchEvent(event) {
        //只跟踪一次触摸
        if (event.touches.length == 1) {
            switch (event.type) {
                case "touchstart":
                    console.log("touch stared(" + event.touches[0].clientX + "," + event.touches[0].clientY + ")");
                    break;
                case "touchend":
                    console.log("touch end(" + event.changedTouches[0].clientX + "," + event.changedTouches[0].clientY + ")");
                    break;
                case "touchmove":
                    //阻止默认滚动行为
                    event.preventDefault();
                    console.log("touch moved(" + event.changedTouches[0].clientX + "," + event.changedTouches[0].clientY + ")");
                    break;
            }
        }
    }
    window.addEventListener("touchstart", handleTouchEvent);
    window.addEventListener("touchend", handleTouchEvent);
    window.addEventListener("touchmove", handleTouchEvent);

内存与性能

事件委托

解决事件处理程序过多的问题

<ul id="myLinks">
    <li id="test1">test1</li>
    <li id="test2">test2</li>
</ul>

通过添加一次事件可以绑定所有的li,利用冒泡的特性

    var list = document.getElementById("myLinks");
    list.addEventListener("click",function (event) {
        //event事件对象有target属性,里面还有id
        //根据里面的不同的id来触发不同的事件处理逻辑
        //每一个li都能够冒泡到上层的ul,所以可以得到被处理
        switch (event.target.id){
            case "test1":
                console.log("test111111");
                break;
            case "test2":
                console.log("test22222");
                break;
        }
    });

移除事件处理程序

空事件处理程序过多也会导致性能不好
例如手动设置null

btn.onclick = null;

但是没说有对于addEventListener那种处理.

模拟事件

模拟事件在测试web应用程序时候是一种极其有用的技术


线上猛如虎
2.2k 声望178 粉丝

你们都有梦想的,是吧.怀抱着梦想并且正朝着梦想努力的人,寻找着梦想的人,我想为这些人加油呐喊!