本文主要总结了在开发中经常使用的的一些浏览器事件的使用。
系列目录
本文涉及内容加多,截止现在,“拖拽事件”、“触摸事件”、“手势事件” 三节尚未完成。
键盘事件
键盘事件最常用的是 keyup
和 keydown
两个事件,这两个事件本身很简单,不再多说,本节说一点键盘事件其他细节吧。
这两个事件常配合表单使用,比如用户输入内容后校验,虽然也可以用,但是处理起来较为麻烦,需要判断组合键,非文本输入按键比如 Shift、F1 之类的等,更合适的方法是使用表单的 input 事件,将在后面表单事件说明
组合键
和鼠标事件类似,按键事件也具有这几个属性:altKey
、ctrlKey/metaKey
、metaKey
ctrlKey、
shiftKey`,具体可以参考上一节,不再详细说明。
e.code 和 e.key
这两个属性表示了按键的内容,是两个比较接近的内容,但是也有一点点不同。
code
表示按的是哪个键,而 key
则表示按键的键值,这么说比较模糊,先看个例子:
document.onkeydown = function (e) { |
由上述例子可以看出,code
是表示的具体的哪个按键被按下,所以会区分左 Shift
和右 Shift
,而 key
则反映了按键代表的含义,再看另一个例子:
// 按下键盘 A |
忽略组合键多出来的一组按键,可以看出,此时,多了 Shift, code 值不变,是小写 a
,而 key
值变成了大写的 A
。
字母键的 key 值为 "Key" + <letter>
,数字的 key 值为 "Digit" + <number>
,小键盘和手机按键的 key 值通常为 "Numpad" + <number>
,其他控制键则为其名字,更具体查看Specs - Key Codes for Standard Keyboards,这里不在一一说明。
e.keyCode
使用 keyCode
判断按键是很常用的代码,比如判断用户输入了回车键,则直接执行操作。但是须知,这个属性已经被废弃,虽然在很多浏览器还有支持,但是不建议再使用,而是使用 key
或者 code
替代:
// 不建议使用 keyCode |
默认行为
一些功能键有一些默认的行为,比如:Delete
会删除表单域的一个字符,PageDown
会向下滚动,Ctrl + S
可以打开浏览器保存网页的对话框,这些行为都是可以取消的:
document.onkeydown = function (e) { |
比如上面例子取消了保存网页和翻页的默认操作,但是有些操作是无法取消的,比如操作系统的 Alt + F4
。
页面生命周期事件
和资源生命周期有关的事件主要有:load
、DOMContentLoaded
、 abort
、beforeunload
、unload
:
load
load:目标对象为 Window
, Document
和 Element
,当一个资源和其依赖的所有资源加载完成的时候,会触发 load
事件。这个资源可以是图片、CSS 文件、JS 文件、视频、document
和 window
等等。
// 页面内所有的资源加载完成后执行 |
DOMContentLoaded
DOMContentLoaded:目标对象为 Document
,当初始 HTML 文档被完全加载和解析完成之后,DOMContentLoaded
事件被触发,而无需等待样式表、图像和子框架完成加载。所以如果想要你的网页加载完成立即执行某些脚本,使用这个事件要优于 load
事件的,因为 load
事件要等待所有内容加载完成,这样无疑是很慢的。
window.onload = function () { |
注意:本函数只能绑定在 Document
元素上,而 load
可以绑定到任何元素上,监听该元素的资源加载完成事件。更多关于初始化资源加载情况的监听探讨可以查看本人的另一篇文章(关于文档加载状态相关的事件探讨)
abort
abort:目标对象为 Window
和 Element
,当资源的加载被中止时,此事件被触发。
beforeunload
beforeunload:目标对象为 Window
, 当窗口,文档及其资源即将被卸载时,beforeunload
事件触发。如果此事件的监听器处理函数为 Event
对象的 returnValue
属性赋值非空字符串,或者函数返回非空字符串,浏览器会弹出一个对话框,来询问用户是否确定要离开当前页面。否则,该事件不做响应。
window.addEventListener("beforeunload", function (e) { |
注意:在 chrome 51 后,不再支持用户自定义网页提示的消息文字,chrome 认为这个经常被用来欺骗用户(详情查看Remove custom messages in onbeforeunload dialogs)。而且 Safari 9.1+,Firefox 4+ 都不支持这个功能。
注意:在实践中调用 window.alert()
, window.confirm()
, window.prompt()
会被忽略,同时该对话框只能起到提示、阻止关闭作用,如果用户选择关闭,并无法在js中获得回调值,所以,如果页面有未保存动作,可以在这个时候进行一些保存动作。
unload
unload:目标对象为 Window
, Document
和 Element
,当DOM实现从环境中删除资源(如文档)或任何依赖资源(如图像,样式表,脚本)时,会触发此事件。
如果事件被绑定在 document
或者 window
对象上的时候,当此事件触发,此时文档会处于以下状态:
- 所有资源仍存在 (图片, iframe 等.)
- 对于终端用户所有资源均不可见
- 界面交互无效 (
window.open
,window.alert
,window.confirm
等.) - 错误不会停止卸载文档的过程
请注意 unload
事件也遵循文档树:父 iframe
会在子 iframe
卸载前卸载:
<!-- Parent.html--> |
更具体的关于删除文档的细节查看:Unloading Documents — Prompt to unload a document
拖拽事件
拖拽事件也是鼠标事件的变形,这里单独列出来解释。
// todo
触摸事件
// todo
手势事件
// todo
错误事件
当错误发生时,触发错误事件。错误事件有两种:
- 当一项资源(如
<img>
或<script>
)加载失败,加载资源的元素会触发一个Event
接口的error
事件,并执行该元素上的onerror()
处理函数。这些 error 事件不会向上冒泡到window
,不过能被单一的window.addEventListener
捕获。 - 当 JavaScript 运行时错误(包括语法错误)发生时,window 会触发一个
ErrorEvent
接口的error
事件,并执行window.onerror()
。
第一种应用场合很少,这里主要介绍一下利用第二种的事件来监控和报告发生在网页的错误。
window.onerror
window.onerror = function(message, source, lineNumber, columnNumber, errorObj) { … }
函数参数:
- message:字符串,错误信息。
- source:字符串,发生错误的脚本URL
- lineNumber:数字, 发生错误的行号
- columnNumber:数字,发生错误的列号
- errorObj:对象,Error 对象
若该函数返回true,则阻止执行默认事件处理函数。
前四个参数告诉您哪个脚本,行和列出现错误,实际上,最后一个参数 Error 对象也许是最有价值的。
Error 对象和 error.stack
new Error([message[, fileName[, lineNumber]]])
Error 对象的构造函数包含3个属性:message
,fileName
和 lineNumber
。实际上这三个属性已经包含在了 window.onerror
提供给你的参数中。
Error 对象最有价值的属性是一个非标准属性:Error.prototype.stack
。此堆栈属性会告诉你发生错误时程序的每个帧的源位置。错误堆栈跟踪是调试的关键部分,尽管这个属性是非标准的,但是每个现代浏览器都提供此属性。
这是一个堆栈追踪的示例:
Error: foobar |
使用 try…catch 的 Polyfill
并不是所有浏览器都支持 errorObj
这个参数,如果为了获得 errorObj
对象,可以使用 try...catch
手动捕获。
function invoke(obj, method, args) { |
当然,调用的每个函数都使用 invoke
手动包装无疑十分愚蠢。js 是单线程的,所以我们可以将 try...catch
代码包裹程序的入口,这样就不需要到处手动包装。
这意味着你需要包装以下函数声明:
- 在你的应用程序开始(例如 jquery 的
$(document).ready
,document 的DOMContentLoaded
事件) - 在事件处理程序中,例如
addEventListener
或$.fn.click
- 基于定时器的回调,例如
setTimeout
或requestAnimationFrame
function wrapErrors(fn) { |
Script error 安全限制
当加载自不同域的脚本中发生错误时,为避免信息泄露,语法错误的细节将不会报告,而代之简单的Script error.
。这种情况通常发生在使用了 CDN,或者来自其他机构提供的服务。
这个问题有两种方法解决:
- 让非本域网络资源设置
Access-Control-Allow-Origin
标头,这将允许跨域资源共享(CORS
)。 - 在
source
标签上使用新的crossorigin
属性:<script crossorigin="anonymous" src="script.js"></script>
crossorigin
标签有两个可能的值:
- anonymous:匿名,意味着无需用户凭据来访问该文件。
- use-credentials:如果从引用的外部 JavaScript 文件使用了
Access-Control-Allow-Credentials
头,则使用凭据。
其他事件
cut、 copy、 paste
这些事件在剪切、复制、粘贴的时候触发,可以被取消。
比如下面这个例子,我们想要用户认真填写自己的Id,禁止用户粘贴:
<input type="text" id="id" /> |
还有一个常见的例子是当用户复制的时候追加额外的文本,比如追加文章的版权说明:
document.addEventListener('copy', function(e) { |
此时选中,复制任何文本,都会在后面追加著作权的信息。
这里的方案仅仅修改文本,如果想要将标签也复制进去比较麻烦:
// 选中选择的文本所在的 html |
剪切板是系统级别的,浏览器可能会出于安全的策略限制了对系统剪切板的访问权限。
剪切板事件并不常用,本文只做了简单的介绍,想要了解更多细节,查看
resize 和 scroll
这两个事件很简单,分别是监听文档视图尺寸和页面滚动的事件,本身没有什么要说的,这里主要说的是这两个事件的触发频率比较高,建议使用 requestAnimationFrame
,setTimeout
或customEvent
来调节事件的触发频率:
使用 requestAnimationFrame
和 customEvent
优化高频率触发的事件:
var throttle = function(type, name, obj) { |