概述

JavaScript 与 HTML 之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中产生的一些特定的交互霎时。能够应用侦听器(或处理程序)来预约事件,以便事件产生时执行相应的代码。这种在传统软件工程中被称为观察员模式的模型,反对页面的行为(JavaScript 代码)与页面的外观(HTML 和 CSS 代码)之间的松耦合。

事件流

事件流 形容的是从页面中接管事件的程序。IE 和 Netscape 开发团队提出了两种实现事件流的计划,事件冒泡流和事件捕捉流。

IE 提出的计划为事件冒泡流,从最具体的元素开始触发,而后向上流传至文档节点。

Netscape 团队提出的计划为事件捕捉流,事件捕捉由文档节点开始流传至最具体的元素。事件捕捉实际上是为在事件达到最终目标前拦挡事件。

DOM 事件流

DOM2 Events 标准规定事件流分为 3 个阶段:事件捕捉、达到指标和事件冒泡。

  1. 事件捕捉阶段:事件从上往下查找对应元素,直到捕捉到事件。
  2. 达到指标阶段:达到指标元素后执行事件对应的处理函数。
  3. 事件冒泡阶段:事件从指标元素开始冒泡。

在 DOM 事件流中,理论的指标(<div>元素)在捕捉阶段不会收到事件。这是因为捕捉阶段从document<html>再到<body>就完结了。下一阶段,即会在<div>元素上触发事件的"达到指标"阶段,通常在事件处理时被认为是冒泡阶段的一部分。而后,冒泡阶段开始,事件反向流传至文档。

事件处理

事件就是在浏览器上执行的某种动作。如clickload等。事件处理程序(或事件监听器)就是响应事件而调用的函数。

HTML事件处理程序

能够在反对事件的HTML元素上通过属性来指定可能执行的JavaScript代码的值。如下所示:

<input type="button" value="HTML Event Handler" onclick="alert('Click HTML Event')" />

在 HTML 中定义的事件处理程序能够蕴含准确的动作指令,也能够调用在页面其余中央定义的脚本:

<input type="button" value="HTML Event Handler" onclick="handleHTMLEvent()" /><script>    function handleHTMLEvent() {        alert("Click HTML Event");    }</script>

在调用handleHTMLEvent()函数之前点击了按钮,会产生谬误。能够将HTML事件处理程序封装在try/catch块中:

<input type="button" value="HTML Event Handler 2" onclick="try{handleHTMLEvent()} catch (e) {}" />

这种事件处理程序会使HTML与JavaScript强耦合,不利于保护。

DOM0事件处理程序

通过JavaScript获取事件处理程序并将一个函数赋值给该事件处理程序属性。

<input id="btn" type="button" value="DOM Event Handler" /><script>    var btn = document.getElementById('btn');    btn.onclick = function () {        alert("Click DOM Event");    }</script>

应用DOM0形式为事件处理程序赋值的函数被认为是元素的办法。此时获取的this等于事件处理程序所在的元素。以这种形式增加事件处理程序注册在事件流的冒泡阶段。

DOM2事件处理程序

DOM2 Events 定义addEventListener()removeEventListener() 两个办法,用于事件处理程序的赋值和移除。所有DOM节点都含这两个办法,它们接管 3 个参数:事件名、事件处理函数和一个布尔值,布尔值示意调用事件处理程序的事件流阶段,true在捕捉阶段,false(默认)在冒泡阶段。

<input id="btn" type="button" value="DOM Event Handler" /><script>    var btn = document.getElementById('btn');    btn.addEventListener("click", () => {        alert("DOM2 Event Handler");    }, false);</script>

应用addEventListener()能够为同一个事件增加多个事件处理程序。

var btn = document.getElementById('btn');btn.addEventListener("click", () => {    alert("DOM2 Event Handler");}, false);btn.addEventListener("click", () => {    alert("Rep DOM2 Event Handler");}, false);

应用removeEventListener()并传入与addEventListener()同样的参数来移除。

var btn = document.getElementById('btn');btn.addEventListener("click", () => {    alert("DOM2 Event Handler");}, false);btn.removeEventListener("click", function () {    // 无成果    alert("Remove DOM2 Event Handler");}, false);

但这种传入匿名函数的事件处理程序无奈移除。removeEventListener()必须和addEventListener()传入的事件处理函数必须是同一个。

var btn = document.getElementById('btn');var handler = function() {    alert("DOM2 Event Handler");}btn.addEventListener("click", handler, false);btn.removeEventListener("click", handler, false);    // 有成果

大多数状况下,事件处理程序会被增加到事件流的冒泡阶段,次要起因是跨浏览器兼容性好。把事件处理程序注册到捕捉阶段通常用于在事件达到其指定指标之前拦挡事件。如果不须要拦挡,则不要应用事件捕捉。

IE事件处理程序

IE 实现了 attachEvent()detachEvent() 办法用于事件处理程序的赋值和移除。接管两个同样的参数:事件处理程序的名字和事件处理函数。IE8以前只反对事件冒泡,应用attachEvent()会增加到冒泡阶段。

var btn = document.getElementById("btn");btn.attachEvent("onclick", function() {    alert("IE Event Handler");});

此时的事件处理程序是在全局作用域中运行。this等于window。也能够给一个元素增加多个事件处理程序,但事件处理程序会以增加的程序的反向触发。

事件对象

DOM产生事件时会将相干信息收集并存储在event对象中。该对象会蕴含一些事件的元素、类型等根本信息。

在浏览器中,event对象是传给事件处理程序的惟一参数。在增加事件处理程序时能够应用事件对象:

<input id="btn" type="button" value="DOM Event Object" onclick="alert(event.type)"><script>    var btn = document.getElementById("btn");    btn.onclick = function(event) {        alert(event.type);    };    btn.addEventListener("click", (event) => {        alert(event.type);    }, false);</script>

下表列出全副属性和办法:

属性/办法类型读/写阐明
bubbles布尔值只读示意事件是否冒泡
cancelable布尔值只读示意事件是否能够被勾销
currentTarget元素只读以后事件处理程序所在的元素
defaultPrevented布尔值只读true示意曾经调用preventDefault()办法(DOM3 Events中新增)
detail整数只读只有UIEvent(用户界面)事件才具备。返回一个数值,示意事件的某种信息。如click事件,detail返回的是鼠标按下的次数。
eventPhase整数只读示意事件流正被解决到了哪个阶段:1代表捕捉阶段,2代表达到指标,3代表冒泡阶段
target元素只读事件指标
isTrusted布尔值只读true示意事件是由浏览器发动的。false示意事件是由脚本创立、批改、通过EventTarget.dispatchEvent()派发
type字符串只读被触发的事件类型
preventDefault()函数只读用于勾销事件的默认行为。只有cancelable为true才能够调用这个办法
stopImmediatePropagation()函数只读用于勾销后续事件捕捉或事件冒泡,并阻止调用任何后续事件处理程序(DOM3 Events中新增)
stopPropagation()函数只读阻止捕捉和冒泡阶段中以后事件的进一步流传

事件处理程序外部,对于currentTargettarget两个属性,将事件处理程序间接增加在指标上时,this和它们是相等的。

<body>    <input id="btn" type="button" value="DOM Event Object" /></body><script>    let btn = document.getElementById("btn");    btn.onclick = function(event) {        alert(event.currentTarget === this);        alert(event.target === this);    };</script>

如果将事件处理程序增加到按钮的父结点上,后果就是currentTargetdocument.bodythis相等,因为它是注册事件处理程序的元素。而target属性等于按钮自身,因为click事件才是真正的指标。但按钮自身没注册事件处理程序,click事件冒泡到document.body,触发注册的处理程序。

document.body.onclick = function (event) {    alert(event.currentTarget === document.body);    // true    alert(this === document.body);    // true    alert(event.target === this);    // false    alert(event.target === document.getElementById("btn"));    // true    }

preventDefault()办法用于阻止特定事件的默认动作。比方,链接的默认行为就是在被单机时导航到href属性指定的URL。如果想阻止这个导航行为,能够在onclick事件处理程序中勾销,如上面的例子所示:

let link = document.getElementById('link');link.onclick = function (event) {    event.preventDefault();}

任何能够通过preventDefault()勾销默认行为的事件,其事件对象的cancelable属性都会设置为true

stopPropagation()办法用于立刻阻止事件流在DOM构造中流传,勾销后续的事件捕捉或冒泡。例如,间接增加到按钮的事件处理程序中调用stopPropagation(),能够阻止document.body上注册事件处理程序执行。比方:

let btn = document.getElementById("btn");btn.onclick = function(event) {    console.log("Clicked");    event.stopPropagation();};document.body.onclick = function(event) {    console.log("Body clicked");};

IE事件对象也蕴含与导致其创立的特定事件相干的属性和办法,很多都与DOM属性和办法对应。如srcElement对应DOM中的targetreturnValue属性对应DOM中的preventDefault()办法等等。

事件类型

DOM3 EventsDOM2 Events 根底上从新定义了并减少了新的事件类型:

  • UI Event:用户界面事件,波及与 BOM 交互的通用浏览器事件。
  • Focus Event:焦点事件,在元素取得和失去焦点时触发。
  • Mouse Event:鼠标事件,应用鼠标在页面上执行某些操作时触发。
  • Wheel Event:滚轮事件,应用鼠标滚轮或相似设施时触发。
  • Input Event:输出事件,向文档中输出文本时触发。
  • Keyboard Event:键盘事件,应用键盘在页面上执行某些操作时触发。
  • Composition Event:合成事件,在应用某种 IME(Input Method Editor,输入法编辑器)输出字符时触发。

用户界面事件

用户界面事件或 UI 事件不肯定跟用户操作无关。这类事件在 DOM 标准呈现之前就曾经以某种模式存在了,保留它们是为了向后兼容。UI 事件次要有以下几种。

  • load:在 window 受骗页面加载实现后触发,在<frameset>受骗所有<frame>都加载实现后触发,在<img>元素受骗图片加载实现后触发,在<object>元素受骗相应对象加载实现后触发。
  • unload:在 window 受骗页面齐全卸载后触发,在<frameset>受骗所有<frame>都卸载实现后触发,在<object>元素受骗相应对象卸载实现后触发。
  • abort:在<object>元素受骗相应对象加载实现前被用户提前终止下载时触发。
  • error:在 window 受骗 JavaScript 报错时触发,在 <img> 元素受骗无奈加载指定图片时触发,在 <object> 元素受骗无奈加载相应对象时触发,在<frameset>受骗一个或多个<frame>无奈实现加载时触发。
  • select:在文本框(<input><textarea>)受骗用户抉择了一个或多个字符时触发。
  • resize:在 window<frame>受骗窗口或窗口被缩放时触发。
  • scroll:当用户滚动蕴含滚动条的元素时在元素上触发。<body>元素蕴含已加载页面的滚动条。

大多数HTML事件与window对象和表单控件无关。

除了DOMActivate,这些事件在DOM2 Events中都被归为HTML Events。

焦点事件

当页面中的某元素取得(用户选中)或失去焦点时触发焦点事件。焦点事件有以下几种:

  • blur:当元素失去焦点时触发。事件不冒泡。
  • focus:当元素取得焦点时触发。事件不冒泡。
  • focusin:当元素取得焦点时触发。是focus的冒泡版事件。
  • focusout:当元素失去焦点时触发。是blur的通用版。

DOMFocusInDOMFocusOutfocusblur 的冒泡版,这两个事件是Opera新增的,但已被弃用,可能还有一些浏览器在反对。focusinfocusout,这两个事件是IE浏览器新增的,已被DOM3 Events标准化。

焦点从页面中一个元素转移到另一个元素时,会顺次产生如下事件:①focusout在失去焦点的元素上触发;②focusin在取得焦点的元素上触发;③blur在失去焦点的元素上触发;④DOMFocusOut在失去焦点的元素上触发;⑤focus在取得焦点的元素上触发;⑥DOMFocusIn在取得焦点的元素上触发。

鼠标事件

鼠标事件是较罕用的事件类型,继承 MouseEvent 接口。DOM Events 定义了 9 中鼠标事件。

  • click:用户单击鼠标主键时触发。
  • dblclick:用户双击鼠标主键时触发。DOM3 Event将其标准化。
  • mousedown:用户按下任意鼠标键时触发。
  • mousemove: 用户将鼠标在元素上挪动时重复触发。不能通过键盘触发事件。
  • mouseout:用户将鼠标从元素上移出时触发。挪动的元素能够是原始元素的内部元素,也能够是原始元素的子元素。不能通过键盘触发事件。
  • mouseover:用户将鼠标光标从元素内部移到元素外部时触发。不能通过键盘触发。
  • mouseup:用户开释鼠标时触发。不能通过键盘触发。
  • mouseenter:用户把鼠标悬停在某些元素之上时触发。不是冒泡事件,也不会悬停在后辈元素之上时触发。DOM3 Events新增的事件。
  • mouseleave:用户把鼠标从悬停的元素上移出时触发。不是冒泡事件,也不会在通过后辈元素时触发。DOM3 Events新增的事件。
  • mousewheel:滚轮事件,鼠标事件的子类别。反映鼠标滚轮或相似设施时交互。

事件之间存在关系,勾销鼠标事件的默认行为会影响其它事件。

键盘事件

键盘事件由用户操作键盘时触发。DOM2 Events最终没有定义键盘事件,都是基于DOM0实现的。

DOM3 Events 为键盘事件提供了标准,蕴含 3 个事件:

  • keydown:用户按下键盘时触发。
  • keypress:用户按下键盘上某个键并产生字符时触发。DOM3 Events 废除了 keypress 事件,举荐 textInput 事件。
  • keyup:用户开释键盘上按键时触发。

尽管所有元素都反对这些事件,但当用户在文本框中输出内容时最容易看到。

用户按下键盘上的某个字符键触发事件的正确程序如下:①keydown;②keypress;③keyupkeydownkeypress事件会在文本框呈现变动之前触发,keyup事件在文本框呈现变动之后触发。非字符键会省略keypress事件。

但当用户在按下键足够长的工夫时,它会开始“主动反复事件”:keydownkeypress一直被反复,直到按键开释时,才会触发keyup事件。非字符键会省略keypress事件步骤,一直触发keydown事件,直到开释才会触发keyup事件。

输出事件

DOM3 Events 引入 textInput 输出事件,是对 keypress 事件的扩大,用于在文本显示给用户之前更不便地截获文本输出。textInput会在文本被插入到文本框之前触发。

只有可编辑区域才反对 textInput 事件,如文本域,并且只有当插入字符时才触发。该事件为 Event 对象增加了 event.data 属性,该属性蕴含新插入的字符。

合成事件

DOM3 Events新增合成事件,用于解决应用IME输出时的简单输出序列。IME能够让用户输出物理键盘上没有的字符。如应用拉丁字母键盘的用户能够应用IME输出日文。IME通常须要按下多个键能力输出一个字符。合成事件用于检测和管制这种输出。合成事件有以下 3 中:

  • compositionstart:在IME文本合成零碎关上时触发,示意输出行将开始。
  • compositionupdate:在新字符插入输出字段时触发。
  • compositionend:在IME文本合成零碎敞开时触发,示意恢复正常键盘输入。

合成事件在很多方面与输出事件很相似。在合成事件触发时,事件指标是接管文本的输出字段。惟一减少的事件属性是 data,其中蕴含的值视状况而异:

  • compositionstart 事件中,蕴含正在编辑的文本(例如,曾经抉择了文本但还没替换);
  • compositionupdate 事件中,蕴含要插入的新字符;
  • compositionend 事件中,蕴含本次合成过程中输出的全部内容。

与文本事件相似,合成事件能够用来在必要时过滤输出内容。能够像上面这样应用合成事件:

let textbox = document.getElementById("text");textbox.addEventListener("compositionstart", (event) => {    console.log(event.data);});textbox.addEventListener("compositionupdate", (event) => {    console.log(event.data);});textbox.addEventListener("compositionend", (event) => {    console.log(event.data);})

除了上述事件类型外,DOM2的变动事件(Mutation Events)被Mutation Observers取代。还有用于定义设施及设施相干的设施事件、用于触摸设施的触摸事件,还有资源事件等等,就不一一列举了。

自定义事件

DOM3减少了自定义事件的类型。自定义事件不会触发原生DOM事件,但能够创立全新的事件类型,应用document.createEvent(type)创立事件类型,type是一个字符串,示意创立的事件类型。如"UIEvents""MouseEvents"等。创立自定义事件,就须要传入"CustomEvent"

创立事件后,须要调用initCustomEvent()对事件进行初始化,该办法接管 4 个参数。

  • type:示意要触发的事件类型,返回字符串,如 "customEvent"
  • bubbles:示意事件是否冒泡,返回布尔值。
  • cancelable:示意事件是否能够勾销,返回布尔值。
  • detail:示意任意值,作为event对象的detail属性。返回对象。

自定义事件能够像其它事件一样在DOM中派发。如:

<div id="div" value="Dom Custom Event Handler">DOM Custom Event Handler</div><script>    let div = document.getElementById("div");    div.addEventListener("cusEvent", (event) => {        console.log("DIV: " + event.detail);    });    document.addEventListener("cusEvent", (event) => {        console.log("DOCUMENT: " + event.detail);    });    let event = document.createEvent("CustomEvent");    event.initCustomEvent("cusEvent", true, false, "Custom Event");    div.dispatchEvent(event);</script>

initCustomEvent第二个参数设置了能够冒泡,因而浏览器会负责把事件冒泡到document

DOM4增加了对CustomEvent构造函数的反对,能够应用new CustomEvent(type, eventInitDict)来创立自定义事件。它有以下参数。

  • type:事件的类型,是一个字符串。
  • eventInitDict:一个对象,提供了事件的配置信息。

eventInitDict参数的类型是CustomEventInit,它有几个参数。

  • bubbles:一个布尔值,示意该事件是否会冒泡。
  • cancelable:一个布尔值,示意该事件是否能够被勾销。
  • detail:当事件初始化时传递的数据。

举一个例子:

<div id="div" value="Dom Custom Event Handler">DOM Custom Event Handler</div><script>    let div = document.getElementById("div");    div.addEventListener("cusEvent", (event) => {        console.log("DIV: " + event.detail);    });    document.addEventListener("cusEvent", (event) => {        console.log("DOCUMENT: " + event.detail);    });    let event = new CustomEvent("cusEvent", {detail: {name: "大卫"}, bubbles: true});    div.dispatchEvent(event);</script>

事件委托

事件委托利用事件冒泡,应用一个事件处理程序来治理同一类型的事件。

<ul id="links">    <li id="red">Red</li>    <li id="green">Green</li>    <li id="blue">Blue</li></ul>

通常给<li>元素增加点击事件,通常给三个<li>元素调配三个onclick处理程序。与其如此,咱们能够在<ul>元素上增加一个事件处理程序捕捉所有的<li>元素的点击事件:

let list = document.getElementById('links');list.addEventListener("click", (event) => {    let target = event.target;    switch (target.id) {        case "red":            document.title = "大红大紫";            break;        case "green":            location.href = "http://www.sample.com";            break;        case "blue":            alert("Hi, Blue");            break;    }});

这样,点击每个<li>元素,都会将事件向上冒泡,交给父节点<ul>的事件处理程序来解决,在<ul>的事件中,通过event.targetid属性能够确定哪个元素被点击,而后执行相应操作。

事件委托具备如下长处:

  • document对象随时可用,任何时候都能够给它增加事件处理程序。这意味着只有页面渲染出可点击的元素,就能够无提早地起作用。
  • 节俭花在设置页面事件处理程序上的工夫。只指定一个事件处理程序既能够节俭DOM援用,也能够节省时间。
  • 缩小整个页面所需的内存,晋升整体性能。

能够先将事件处理程序删除,在通过innerHTML属性来对DOM进行删除操作,来删除不必的事件处理程序,来进步Web利用性能。

<div id="div">    <input id="btn" type="button" value="Del Event Handler"></div><script>    let btn = document.getElementById("btn");    btn.onclick = function () {        // 执行操作        ...        btn.onclick = null;  // 删除事件处理程序        document.getElementById("div").innerHTML = "Processing...";    }</script>

小结

事件是JavaScript与网页交互的次要形式。、本文次要介绍了事件的接管程序,事件对象和常见的事件类型,还有一些事件类型如HTML5事件、设施事件等没有介绍,还有一些浏览器也会实现本人专有的事件,不便扩大满足用户的需要的性能。事件在JavaScript中十分重要,了解事件的原理对其性能也十分重要。


更多内容请关注公众号「海人的博客」,回复「资源」即可取得收费学习资源!