概述
JavaScript 与 HTML 之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中产生的一些特定的交互霎时。能够应用侦听器(或处理程序)来预约事件,以便事件产生时执行相应的代码。这种在传统软件工程中被称为观察员模式的模型,反对页面的行为(JavaScript 代码)与页面的外观(HTML 和 CSS 代码)之间的松耦合。
事件流
事件流 形容的是从页面中接管事件的程序。IE 和 Netscape 开发团队提出了两种实现事件流的计划,事件冒泡流和事件捕捉流。
IE 提出的计划为事件冒泡流,从最具体的元素开始触发,而后向上流传至文档节点。
Netscape 团队提出的计划为事件捕捉流,事件捕捉由文档节点开始流传至最具体的元素。事件捕捉实际上是为在事件达到最终目标前拦挡事件。
DOM 事件流
DOM2 Events
标准规定事件流分为 3 个阶段:事件捕捉、达到指标和事件冒泡。
- 事件捕捉阶段:事件从上往下查找对应元素,直到捕捉到事件。
- 达到指标阶段:达到指标元素后执行事件对应的处理函数。
- 事件冒泡阶段:事件从指标元素开始冒泡。
在 DOM 事件流中,理论的指标(<div>
元素)在捕捉阶段不会收到事件。这是因为捕捉阶段从 document
到<html>
再到 <body>
就完结了。下一阶段,即会在 <div>
元素上触发事件的 ” 达到指标 ” 阶段,通常在事件处理时被认为是冒泡阶段的一部分。而后,冒泡阶段开始,事件反向流传至文档。
事件处理
事件就是在浏览器上执行的某种动作。如 click
、load
等。事件处理程序(或事件监听器)就是响应事件而调用的函数。
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() | 函数 | 只读 | 阻止捕捉和冒泡阶段中以后事件的进一步流传 |
事件处理程序外部,对于 currentTarget
和target
两个属性,将事件处理程序间接增加在指标上时,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>
如果将事件处理程序增加到按钮的父结点上,后果就是 currentTarget
和document.body
和 this
相等,因为它是注册事件处理程序的元素。而 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
中的 target
,returnValue
属性对应 DOM
中的 preventDefault()
办法等等。
事件类型
DOM3 Events
在 DOM2 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
的通用版。
DOMFocusIn
和 DOMFocusOut
是 focus
与 blur
的冒泡版,这两个事件是 Opera 新增的,但已被弃用,可能还有一些浏览器在反对。focusin
和 focusout
,这两个事件是 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
;③keyup
。keydown
和 keypress
事件会在文本框呈现变动之前触发,keyup
事件在文本框呈现变动之后触发。非字符键会省略 keypress
事件。
但当用户在按下键足够长的工夫时,它会开始“主动反复事件”:keydown
和 keypress
一直被反复,直到按键开释时,才会触发 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.target
的id
属性能够确定哪个元素被点击,而后执行相应操作。
事件委托具备如下长处:
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 中十分重要,了解事件的原理对其性能也十分重要。
更多内容请关注公众号「海人的博客」,回复「资源」即可取得收费学习资源!