JavaScript事件

Posted 季诗筱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript事件相关的知识,希望对你有一定的参考价值。

所谓事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间

一、事件流

在页面上,单击某个元素的同时,也单击了它的包含容器。事件流就是描述的从页面中接收事件的顺序。IE是事件冒泡流,Netscape是事件捕获流。
事件冒泡:事件开始时,由最具体的元素接收,然后逐级向上传播到较为不具体的节点;(所有现代浏览器都支持事件冒泡)
事件捕获:不太具体的节点最早接收到事件,最具体的节点最后接收到事件。(老版本浏览器不支持)

DOM事件流

DOM2级事件规定的事件流包括三个阶段:1、事件不惑阶段;2、处于目标阶段;3、事件冒泡阶段

这里写图片描述

DOM事件流中,实际的目标在捕获阶段不会接收到时间

二、事件处理程序

事件就是用户或浏览器自身执行的某种动作,如click/load/mousedown,这些都是事件的名字。
事件处理程序:是响应某个事件的函数

为事件指定事件处理函数的方法有3种:

1、html事件处理程序

某个元素支持的每种事件,都可以在元素内部,使用与事件处理程序同名的HTML属性来指定。这个属性的值是能够执行的javascript代码,既可以包含要执行的具体动作,也可以调用在页面其他地方定义的脚本。

<input type="button" value="点我有惊喜" onclick="alert('hello,vicky!')" />

缺点:
1、存在时差,用户可能会在HTML元素加载完就触发相应事件,而这时相应的事件处理程序还没有加载完,就会报错
2、使得HTML代码和JavaScript代码耦合,所以不推荐使用!

2、DOM0级事件处理程序

每个元素都有自己的事件处理程序属性,这些属性通常全部小写,将这种属性的值设置为一个函数,就可以指定事件处理程序。

var btn = document.getElementById("btn");//取得一个要操作对象的引用
btn.onclick = function () {
    alert("hello vicky!");
}

3、DOM2级事件处理程序

DOM2级事件定义了两个方法:addEventListener( ) removeEventListener( ),都接受三个参数,要处理的事件名,事件处理函数,一个布尔值(true表示在捕获阶段调用事件处理程序,false表示在冒泡阶段)

var btn = document.getElementById("btn");
btn.addEventListener("click", function (){
    alert("hello vicky!");
},false);
btn.addEventListener("click",function () {
    alert("hello jay!")
}, false);//可以添加多个事件处理程序,且按照其添加的顺序触发

通过addEventListener()添加的事件处理程序只能通过 removeEventListener()来移出。事件处理程序是在其依附的元素的作用域中运行

4、IE事件处理程序

IE实现了与DOM中类似的两个方法:attachEvent()和detachEvent()。事件处理程序在全局作用域中运行,添加的事件处理程序都会被添加到冒泡阶段

var btn = document.getElementById("btn");
btn.attachEvent("onclick",function(){
    alert("hello vicky!");
});
btn.attachEvent("onclick",function () {
    alert("hello jay!");
});//添加多个事件处理程序,以添加顺序的相反顺序执行

5、跨浏览器的事件处理程序

var EventUtil = {
    //添加事件处理程序
    addHandler: function (element,type,handler) {
        if (element.addEventListener) {
            element.addEventListener(type,handler,false);//DOM2级
        } else if (element.attachEvent) {
            element.attachEvent("on" + tye,handler);//IE的方法
        } else {
            element["on" + tyle] = handler;//DOM0级
        }
    },
    //取消事件处理程序
    removeHandler: function (element,type,handler) {
        if (element.removeEventListener) {
            element.removeEventListener(type,handler,false);
        } else if (element.detachEvent) {
            element.detachEvent("on" + tye,handler);
        } else {
            element["on" + tyle] = null;
        }
    }
}

三、事件对象

在出发DOM上的某个事件时,会产生要给事件对象event

1、DOM中的事件对象

兼容DOM的浏览器会将一个event对象传入到事件处理程序中。event对象包含于创建它的特定事件有关的属性和方法。

//属性
bubbles:Boolean //表明事件是否冒泡
cancelable:Boolean //表明是否可以取消事件的默认行为
currentTarget:Element //事件处理程序当前正在处理事件的那个元素
defaultPrevented:Boolean //是否调用了preventDefault()
detail:Integer //细节信息
eventPhase: Integer //1,捕获阶段;2,处于目标阶段;3,冒泡阶段
target:Element //事件的目标
trusted:Boolean //是否为浏览器生成
type:Sting //被触发事件的类型

//方法
preventDefault()  //取消事件的默认行为
stopImmediatePropagation() //取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用
stopPropagation() //取消事件的进一步捕获或冒泡。

在事件处理程序内部,对象this始终等于currentTarget的值,target则只包含事件的实际目标

2、IE中的事件对象

不同之处:
1、event对象作为window对象的一个属性存在
2、事件的目标 window.event.srcElement
3、returnValue=false相当于preventDefault()

3、跨浏览器的事件对象

var EventUtil = {
    //获取事件对象
    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;//取消默认行为
        }
    },
    //停止事件冒泡
    stopPropagation: function (event) {
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancleBubble = true;
        }
    }
}

四、事件类型

1、UI事件

UI事件:指的是那些不一定与用户操作有关的事件。主要有:
load事件:页面完全加载后再window上面触发
unload事件:页面完全卸载后在window上触发
abort事件:用户停止下载过程时,如果嵌入的内容还没加载完在object元素上面触发
error事件:发生错误时触发
select事件:用户选择文本框时触发
resize事件:窗口或框架的大小变化时触发
scroll事件:用户滚动带滚动条的元素中的内容时触发

(1)load事件

当页面完全加载后,就会触发window上面的load事件

1)可以为图像设置load事件。

注意:在创建新的img元素时,要在指定其src属性之前先指定事件。新图像元素不一定要从添加到文档后才开始下载,只要设置了src属性就会开始下载。

2)为script元素指定事件处理程序

注意:与图像不同,只有在设置了script元素的src属性并将该元素添加到文档后,才会开始下载JavaScript文件,即指定src属性和指定事件处理程序的先后顺序不重要。

(2)unload事件

在文档被完全卸载后触发,只要用户从一个页面切换到另一个页面,就会发生unload事件。

(3)resize事件

当浏览器窗口被调整到一个新的高度或宽度时,就会触发resize事件
注意:不要在这个事件的处理程序中加入计算量大的代码,因为这些代码很有可能会被频繁执行,从而导致浏览器反应明显变慢。当然,这个缺点也是有办法来解决的,那就是函数节流 ,详情请见之前博客【JavaScript】函数节流

2、焦点事件

blur:在元素失去焦点时触发
focus:在元素获得焦点时触发
这两个事件都不冒泡,所有浏览器都支持它们

3、鼠标与滚轮事件

(1)鼠标事件

click:单击主鼠标或者按下回车键触发
dbclick:双击主鼠标时触发
mousedown:用户按下了任意鼠标按钮时触发
mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。此事件不冒泡,且光标移动到后代元素上不触发
mouseleave:位于元素上的光标移动到元素范围之外时触发
mouseover:鼠标指针在元素内部移动时触发
mouseout:鼠标指针由一个元素移到另一个元素上时触发
mouseup:释放鼠标按钮时触发

//鼠标事件触发顺序
(1)mousedown
(2)mouseup
(3)click
(4)mousedown
(5)mouseup
(6)dbclick

1)位置属性:
这里写图片描述
clientX,clientY:到浏览器视口区的左边和上边的值
screenX,screenY:到显示屏幕左边和上边的值
offsetX,offsetY:到元素左边和上边的值
2)修改键
shiftKey,ctrlKey,altKey,metaKey.

//打印出用户按下的修改键
        var text = document.getElementById("text");
        EventUtil.addHandler(text,"keydown",function (event) {
            var event = EventUtil.getEvent(event);
            var keys = new Array();
            if (event.shiftKey) {
                keys.push("shift")
            }

            if (event.ctrlKey) {
                keys.push("ctrl");
            }
            console.log(keys);
        }) ;

3)相关元素
发生mouseover和mouseout事件时,还会涉及更多的元素。这两个事件都会涉及把鼠标指针从一个元素的编辑之内移动到另一个元素的边界之内

//获取相关元素
    getRelatedTarget: function (event) {
        if (event.relatedTarget) {//DOM
            return event.relatedTarget;
        } else if (event.toElement) {//mouseout时IE的toElement保存相关元素
            return event.toElement;
        } else if (event.fromElement) {//mouseover时IE的fromElement保存相关元素
            return event.fromElement;
        } else {
            return null;
        }
    }

4)鼠标按钮
event对象存在一个button属性,其值有3种:0表示主鼠标按钮,1表示中间的鼠标按钮,2表示次鼠标按钮

(2)鼠标滚轮事件

将mousewheel事件处理程序指定给页面中的任何元素或document对象,即可处理鼠标滚轮交互操作
使用鼠标事件应注意的问题:
(1)使用click事件执行代码
(2)不要使用onmouseover向用户显示新的选项。原因是 屏幕阅读器中,无法触发mousedown和mouseover事件
(3)不要使用dbclick执行重要的操作。因为键盘无法触发这个事件

4、键盘事件

keydown:用户按下键盘上的任意键时触发
keypress:用户按下键盘上的字符键时触发
keyup:用户释放键盘上的键时触发
textInput:在文本插入文本框之前触发

(1)键码

在发生keydown和keyup事件时,event对象的keyCode属性中会包含一个代码,与键盘上一个特定的键对应。
这里写图片描述
这里写图片描述

(2)字符编码

发生keypress事件意味着按下的键会影响到屏幕中文本的显示。
charCode属性:只有在发生keypress事件时才包含值,而且值是ASCII编码。要想跨浏览器取得字符编码,必须首先检测charCode属性是否可用,如果不可用则使用keyCode

//获取charCode
    getCharCode: function (event) {
        if (typeof event.charCode == "number") {
            return event.charCode;
        } else {
            return event.keyCode;
        }
    }

(3)textInput事件

用户在可编辑区域中输入字符时,就会触发这个事件
textInput事件的event对象中包含一个data属性,这个属性的值就是用户输入的字符(不是编码)。

var textbox = document.getElementById("textbox");
EventUtil.addHandler(textbox,"textInput",function (event) {
    event = EventUtil.getEvent(event);
    alert(event.data);
})

5、HTML5事件

(1)contexmenu事件

contextmenu事件表示何时应该显示上下文菜单,以便开发人员取消默认的上下文菜单而提供自定义的菜单

//contextmenu事件
    EventUtil.addHandler(window,"load",function (event) {
        var div = document.getElementById("myDiv");
        EventUtil.addHandler(div, "contextmenu", function (event) {
            event = EventUtil.getEvent(event);
            EventUtil.preventDefault(event);
            var menu = div.nextElementSibling;
            menu.style.left = event.clientX + "px";
            menu.style.right = event.clientY + "px";
            menu.style.visibility = "visible";
        });
        EventUtil.addHandler(document, "click", function (event) {
            document.getElementById("myMenu").style.visibility = "hidden";
        })
    });

这里写图片描述
右键出现自定义的上下文信息

(2)readystatechange事件

readystatechange事件的目的是提供与文档或元素的加载状态有关的信息
可能的状态有5种:

uninitialized(未初始化):对象存在但尚未初始化
loading(正在加载):对象正在加载数据
loaded(加载完毕):对象加载数据完成
interactive(交互):可以操作对象了,但还没有完全加载
complete(完成):对象已经加载完毕

并非所有对象都会经历readyState的这几个阶段
栗子:

//readystatechange事件,确定外部js是否加载完毕
    EventUtil.addHandler(window, "load", function () {
        var script = document.createElement("script");
        EventUtil.addHandler(script, "readystatechange", function (event) {
            var event = EventUtil.getEvent(event);
            var target = event.getTarget(event);
            if (target.readyState == "loaded" || target.readyState == "complete") {
                EventUtil.removeHandler(target, "readystatechange", arguments.callee);
                alert("event.js is loaded");
            }
        });
        script.src = "test.js";
        document.body.appendChild(script);
    });
    //readystatechange 事件,确定外部css是否加载完毕
    EventUtil.addHandler(window, "load", function () {
        var link = document.createElement("link");
        link.type = "text/css";
        link.rel = "stylesheet";

        EventUtil.addHandler(link,"readystatechange", function (event) {
            var event = EventUtil.getEvent(event);
            var target = EventUtil.getTarget(event);

            if (target.readyState == "loaded" || target.readyState == "complete") {
                EventUtil.removeHandler(target, "readystatechange", arguments.callee);
            }
        });
        link.href = "test.css";
        document.getElementsByTagName("head")[0].appendChild(link);
    });
    //以上两个函数,最重要的是一并检测readyState的两个状态,并在调用了一次事件处理程序后就将其移除

五、事件委托

1、事件委托

对”事件处理程序过多“这个问题的解决方案就是事件委托,事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。比如,click事件会冒泡到document对象上,所以可以为整个页面指定一个onclick事件处理程序,而不必给每个可单击的元素分别添加。

事件委托的优点:
(1)在页面中设置时间处理程序所需的时间更少。只添加一个事件处理程序所需的DOM引用更少,花费时间少
(2)整个页面占用的内存空间更少,能够提升整体性能。

适合使用时间委托技术的事件有:click/mousedown/mouseup/keydown/keyup/keypress

2、移出事件处理程序

内存中留有那些过时不用的“空事件处理程序”,也是造成Web应用程序内存与性能问题的主要原因,解决问题的一种方法就是移除这些事件处理程序。

btn.onclick = null;

在页面卸载之前,使用onunload事件,移除所有事件处理程序

六、巩固学习的新知识

String.fromCharCode( ): 可接受一个指定的Unicode值,返回对应的字符串

/\\d/.test(String.fromCharCode(EventUtil.getCharCode(event)))//判断按键按下的是否为数字

hasArrtibute( ):如果存在指定属性,则hasAttribute方法返回true,否则返回false。
specified属性:HTML DOM属性,查明是否已规定某属性

PS.《JavaScript高级程序设计》书又刷了一遍,还是又学习了很多新的知识,我觉着写博客是一种巩固学习的好方法,看书看一遍,把书上的代码敲一遍,再在博客中梳理一遍,整个知识框架就会很清晰。最近心里也有点着急,看书速度有些慢了,又买了好几本新书,不知道内推之前还来不来的及看完了。英雄不打无准备之仗,所以,加油!

以上是关于JavaScript事件的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段12——JavaScript的Promise对象

30秒就能看懂的JavaScript 代码片段

是否可以使用 Javascript 在音频文件中找到一段无声的片段?

常用Javascript代码片段集锦

Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段

48个值得掌握的JavaScript代码片段(上)