JavaScript与HTML的交互是通过事件实现的,事件代表文档或浏览器窗口中某个有意义的时刻。

能够应用仅在事件产生时执行的监听器(即处理程序)订阅事件。=>观察者模式 => 页面行为(JavaScript中定义)与页面展现(HTML和CSS定义)的拆散。

最早的事件是为了把某些表单解决工作从服务器转移到浏览器上来。DOM2开始尝试以合乎逻辑的形式来标准化DOM事件。IE8是最初一个应用专有事件零碎的支流浏览器。

事件流

页面哪个局部领有特定的事件呢?当你点击一个按钮时,实际上不光点击了这个按钮,还点击了它的容器以及整个页面。

事件流形容了页面接管事件的程序。

IE反对事件冒泡流,Netscape Communicator反对事件捕捉流

事件冒泡

从最具体的元素开始触发,而后向上流传至没有那么具体的元素(document)。

点击事件:被点击的元素最先触发click事件,而后click事件沿DOM树一路向上,在通过的每个节点上顺次触发,直至达到document对象。

古代浏览器中的事件会始终冒泡到window对象。

事件捕捉

最不具体的节点(document)最先收到事件,而最具体的节点最初收到事件。=>为了在事件达到最终目标前拦挡事件

点击事件:最先由document元素捕捉,而后沿DOM树顺次向下流传,直至达到理论的指标元素。

古代浏览器都是从window对象开始捕捉事件。DOM2 Events标准规定的是从document开始。

因为旧版本浏览器不反对,通常倡议应用事件冒泡,非凡状况下能够应用事件捕捉。

DOM事件流

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

  1. 事件捕捉:最先产生,为提前拦挡事件提供了可能;
  2. 理论的指标元素接管到事件;
  3. 冒泡:最迟要在这个阶段响应事件。

在DOM事件流中,理论的指标在捕捉阶段不会接管到事件。下一阶段会在理论指标元素上触发事件的“达到指标”阶段,通常在事件处理时被认为是冒泡阶段的一部分;而后冒泡阶段开始,事件反向流传至文档。

尽管DOM2 Events标准明确捕捉阶段不命中事件指标,但古代浏览器都会在捕捉阶段在事件指标上触发事件。=> 在事件指标上有两个机会来处理事件。

所有古代浏览器都反对DOM事件流,只有IE8及更早版本不反对。

  capture phase  | |  / \ bubbling up-----------------| |--| |-----------------| element1       | |  | |                ||   -------------| |--| |-----------     ||   |element2    \ /  | |          |     ||   --------------------------------     ||        W3C event model                 |------------------------------------------

事件处理程序

事件是用户或浏览器执行的某种动作。如click、load等。

为响应事件而调用的函数被称为事件处理程序(或事件监听器)。事件处理程序的名字以“on”结尾。

有多种形式能够指定事件处理程序。

HTML事件处理程序

特定元素反对的每个事件都能够应用事件处理程序的名字(onxxx)以HTML属性的模式来指定。此时属性的值必须是可能执行的JavaScript代码。

因为属性的值是JavaScript代码,所以不能在未经本义的状况下应用HTML语法字符,如&、"、<和>。为防止应用HTML实体,能够应用单引号代替双引号,或者应用\&quot;。

在HTML中定义的事件处理程序能够蕴含准确的动作指令,也能够调用在页面其余中央定义的脚本。作为事件处理程序执行的代码能够拜访全局作用域中的所有。

以这种形式指定的事件处理程序有一些非凡的中央

  1. 会创立一个函数来封装属性的值。这个函数有一个非凡的局部变量event,即event对象;
  2. 在这个函数中,this值相当于事件的指标元素;
  3. 这个动态创建的包装函数,其作用域链被扩大了。=> document和元素本身的成员都能够被当成局部变量来拜访。这是通过应用with实现的。

    // 实际上的包装函数是onclick属性的值function () {  with(document) {    with(this) {      // ... HTML事件处理程序属性值    }  }}

=> 事件处理程序能够更不便地拜访本人的属性(不必带this.)

如果元素是一个表单输入框,则作用域链中还会蕴含表单元素 => 事件处理程序的代码能够不用援用表单元素,而间接拜访同一表单中的其余成员了(通过name属性)。

在HTML中指定事件处理程序存在的问题

  1. 机会问题。有可能HTML元素曾经显示在页面上,但事件处理程序的代码还无奈执行。=> 大多数HTML事件处理程序会封装在try/catch块中,以便在这种状况下静默失败。

    <input type="button" value="Click Me" onclick="try{doSomething();}catch(ex){}">
  2. 对事件处理程序作用域链的扩大在不同浏览器中可能导致不同的后果。不同JavaScript引擎中标识符解析的规定存在差别 => 拜访有限定的对象成员可能导致谬误。
  3. HTML与JavaScript的强耦合。(如果要批改,必须在HTML和JavaScript中都批改代码)

DOM0事件处理程序

把一个函数赋值给(DOM元素的)一个事件处理程序属性。=> 简略

要应用JavaScript指定事件处理程序,必须先获得要操作对象的援用

每个元素(包含window和document)都有通常小写的事件处理程序属性。

赋值代码运行之后才会给事件处理程序赋值。

所赋函数被视为元素的办法。=> 事件处理程序会在元素的作用域中运行,即this等于元素。

let btn = document.querySelector('#myBtn');btn.onclick = function() {  console.log(this.id); // "myBtn"}

以这种形式增加事件处理程序是注册在事件流的冒泡阶段的。

通过将事件处理程序属性的值设置为null,能够移除通过DOM0形式增加的事件处理程序。(在HTML中指定的事件处理程序,也能够通过JavaScript将相应属性设置为null来移除)

btn.onclick = null;

DOM2事件处理程序

DOM2 Events为事件处理程序的赋值和移除定义了两个办法:addEventListener()和removeEventListener()。裸露在所有DOM节点上。

接管3个参数:事件名、事件处理函数和一个示意是否在捕捉阶段调用处理函数的布尔值(默认值为false,在冒泡阶段调用)。

这个事件处理程序同样在被附加到的元素的作用域中运行:this等于元素。

次要劣势:能够为同一个事件增加多个事件处理程序。多个事件处理程序以增加程序来触发。

通过addEventListener()增加的事件处理程序只能应用removeEventListener()并传入与增加时同样的参数来移除。(事件处理函数必须是同一个)

大多数状况下,事件处理程序会被增加到事件流的冒泡阶段,次要起因是跨浏览器兼容性好。(除非须要在事件达到其指定指标之前拦挡事件)

IE事件处理程序

IE实现了与DOM相似的办法,attachEvent()和detachEvent()。

接管2个参数:事件处理程序的名字和事件处理函数。因为IE8及更早版本只反对事件冒泡,所以应用attachEvent()增加的事件处理程序会增加到冒泡阶段。

在IE中应用attachEvent()与应用DOM0形式的次要区别在于事件处理程序的作用域。应用attachEvent()时,事件处理程序是在全局作用域中运行的,因而this等于window。

attachEvent()办法也能够给一个元素增加多个事件处理程序。以增加它们的程序反向触发

应用attachEvent()增加的事件处理程序将应用detachEvent()来移除,只有提供雷同的参数(处理函数是雷同的函数援用)。

let btn = document.querySelector('#myBtn');var handler = function() {  console.log("Clicked");};btn.attachEvent("onclick", handler);btn.detachEvent("onclick", handler);

跨浏览器事件处理程序

以跨浏览器兼容的形式处理事件。

本人编写跨浏览器事件处理代码次要依赖能力检测。要确保最大兼容性,只有让代码在冒泡阶段运行即可。

var EventUtil = {  addHandler: function(element, type, handler) {    if(element.addEventListener) {      element.addEventListener(type, handler, false);    } else if(element.attachEvent) {      element.attachEvent("on"+type, handler);    } else { // 默认DOM0形式      element["on"+type] = handler;    }  },  removeHandler: function(element, type, handler) {    if(element.removeEventListener) {      element.removeEventListener(type, handler, false);    } else if(element.detachEvent) {      element.detachEvent("on"+type, handler);    } else { // 默认DOM0形式      element["on"+type] = null;    }  }};// 应用let btn = document.querySelector('#myBtn');let handler = function() {  console.log("Clicked");};EventUtil.addHandler(btn, "click", handler);EventUtil.removeHandler(btn, "click", handler);

没有解决的存在的问题:

  1. IE的作用域问题。(attachEvent()中this等于window)
  2. 多个事件处理程序执行程序问题。DOM2以增加程序,IE为增加程序的反向
  3. DOM0只反对给一个事件增加一个处理程序。(DOM0浏览器曾经很少人应用,问题应该不大)

事件对象event

在DOM中产生事件时,所有相干信息(如事件指标元素、事件类型)都会被收集并存储在一个名为event的对象中。

DOM事件对象

在DOM合规的浏览器中,event对象是传给事件处理程序的惟一参数。不论以哪种形式(DOM0或DOM2)指定事件处理程序,都会传入这个event对象。在通过HTML属性指定的事件处理程序中,同样能够应用变量event援用事件对象。

所有事件对象蕴含的公共属性和办法:

  • bubbles。布尔值。是否冒泡
  • cancelable。布尔值。是否能够勾销事件的默认行为
  • currentTarget。元素。以后事件处理程序所在的元素
  • defaultPrevented。布尔值。true示意曾经调用preventDefault()办法。(DOM3 Events新增)
  • detail。整数(??)。事件相干的其余信息
  • eventPhase。整数。示意调用事件处理程序的阶段:1-捕捉阶段;2-达到指标;3-冒泡阶段
  • target。元素。事件指标元素
  • trusted。布尔值。true示意事件由浏览器生成;false示意事件由开发者通过JavaScript创立。(DOM3 Events新增)
  • type。字符串。被触发的事件类型
  • View。AbstractView。与事件相干的形象视图;等于事件所产生的window对象
  • preventDefault()。函数。用于勾销事件的默认行为 => cancelable为true时才可调用
  • stopImmediatePropagation()。函数。用于勾销所有后续事件捕捉或事件冒泡,并阻止调用任何后续事件处理程序。(DOM3 Events新增)
  • stopPropagation()。函数。用于勾销所有后续事件捕捉或事件冒泡 => bubbles为true时才可调用

在事件处理程序外部,this始终等于currentTarget。this === event.currentTarget

如果事件处理程序间接增加在用意的指标,则this、currentTarget和target三者相等。

type属性在一个处理程序解决多个事件时很有用:依据事件类型,做出不同的响应。

preventDefault()可阻止特定事件的默认动作,如链接的默认行为是被单击时导航到href属性指定的URL。任何可调用preventDefault()勾销默认行为的事件,其event对象的cancelable属性都会设置为true。

stopPropagation()用于立刻阻止事件流在DOM构造中流传,勾销后续的事件捕捉或冒泡。

eventPhase属性可用于确定事件流以后所处的阶段。如果事件处理程序在指标上被调用,则eventPhase等于2 => 尽管”达到指标“是在冒泡阶段产生的,但eventPhase等于2。=> 当eventPhase等于2,this、currentTarget和target三者相等。

event对象只在事件处理程序执行期间存在,一旦执行结束,就会被销毁。

IE事件对象

IE事件对象能够基于事件处理程序被指定的形式以不同的形式来拜访。

  • 如果应用DOM0形式指定,则event对象是window的一个属性
  • 如果应用attachEvent()指定,则event对象会作为惟一的参数传给处理函数。此时event对象依然是window的属性,出于不便也将其作为参数传入
  • 在通过HTML属性形式指定的事件处理程序中,同样能够应用变量event援用事件对象。

所有IE事件对象都会蕴含的公共属性和办法:

  • cancelBubble。布尔值。读/写。true示意勾销冒泡(默认false),与stopPropagation()办法成果类似
  • returnValue。布尔值。读/写。false示意勾销事件默认行为(默认true),与preventDefault()成果雷同
  • srcElement。元素。事件指标,即target
  • type。字符串。触发的事件类型

事件处理程序的作用域取决于指定它的形式,所以更好的形式是应用事件对象的srcElement属性代替this。(DOM0形式下,this等于元素;attachEvent()形式下,this等于window)

与DOM不同,无奈通过JavaScript确定事件是否能够被勾销。

cancelBubble属性与stopPropagation()办法用处类似,但IE8及更早版本不反对捕捉阶段,所以只会勾销冒泡。

跨浏览器事件对象

DOM事件对象中蕴含IE事件对象的所有信息和能力,只是模式不同。这些共性可让两种事件模型之间的映射成为可能。

var EventUtil = {  addHandler: function(element, type, handler) {    // ...  },  removeHandler: function(element, type, handler) {    // ...  },  getEvent: function(event) { // IE事件中以DOM0形式指定事件处理程序时,event对象是window的一个属性    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.cancelBubble = true;    }  }};// 应用let btn = document.querySelector('#myBtn');btn.onclick = function(event) {  event = EventUtil.getEvent(event);  let target = EventUtil.getTarget(event);  EventUtil.preventDefault(event); // 阻止事件的默认行为  EventUtil.stopPropagation(event); // 阻止事件冒泡};

事件类型

所产生事件的类型决定了事件对象中会保留什么信息。DOM3 Events定义的事件类型:

  • 用户界面事件(UIEvent):与BOM交互的通用浏览器事件
  • 焦点事件(FocusEvent):元素取得和失去焦点时触发
  • 鼠标事件(MouseEvent):鼠标在页面上执行某些操作时触发
  • 滚轮事件(WheelEvent):应用鼠标滚轮(或相似设施)时触发
  • 输出事件(InputEvent):向文档中输出文本时触发
  • 键盘事件(KeyboardEvent):键盘在页面上执行某些操作时触发
  • 合成事件(CompositionEvent):应用某种IME(Input Method Editor,输入法编辑器)输出字符时触发
  • HTML5还定义了另一组事件
  • 浏览器通常在DOM和BOM上实现专有事件:依据开发者需要,不同浏览器的实现可能不同

DOM3 Events在DOM2 Events根底上从新定义了事件,并减少了新的事件类型。所有支流浏览器都反对DOM2 Events和DOM3Events。

用户界面事件 UIEvent

不肯定跟用户操作无关。保留它们是为了向后兼容。次要有以下几种:

  • DOMActivate(DOM3 Events中曾经废除)。元素被用户通过鼠标或键盘操作激活时触发,浏览器实现之间存在差别
  • load。window(页面加载实现后触发);窗套frameset(所有窗格frame都加载实现后触发);img(图片加载实现后触发);object(相应对象加载实现后触发)
  • unload。window(页面齐全卸载后触发);窗套(所有窗格都卸载实现后触发);object(相应对象卸载实现后触发)
  • abort。object(相应对象加载实现前被用户提前终止下载时触发)
  • error。window(JavaScript报错时触发);img(无奈加载指定图片时触发);object(无奈加载相应对象时触发);窗套(一个或多个窗格无奈实现加载时触发)
  • select。在文本框(input或textarea)上用户抉择了一个或多个字符时触发
  • resize。window或窗格(窗口或窗格被缩放时触发)
  • scroll。当用户滚动蕴含滚动条的元素时在元素上触发。body元素蕴含已加载页面的滚动条

大多数HTML事件与window对象和表单控件无关。除了DOMActivate,其余在DOM2 Events中都被归为HTML Events。(DOMActivate是UI事件)

焦点事件 FocusEvent

页面元素取得或失去焦点时触发。能够与document.hasFocus()和document.activeElement一起为开发者提供用户在页面中导航的信息。焦点事件有以下6种:

  • blur。失去焦点时触发。不冒泡,所有浏览器都反对
  • DOMFocusIn(DOM3 Events中曾经废除,举荐focusin)。取得焦点时触发。focus的冒泡版。Opera惟一反对
  • DOMFocusOut(DOM3 Events中曾经废除,举荐focusout)。失去焦点时触发,blur的通用版。Opera惟一反对
  • focus。取得焦点时触发。不冒泡,所有浏览器都反对
  • focusin。取得焦点时触发。focus的冒泡版
  • focusout。失去焦点时触发。blur的通用版

两个次要事件是focus和blur,它们最大的问题是不冒泡。

当焦点从页面中的一个元素A移到另一个元素B上,会顺次产生如下事件(测试,与书中不统一):

1)A:blur

2)A:focusout

3)B:focus

4)B:focusin

DOMFocusOut和DOMFocusIn未验证

鼠标和滚轮事件MouseEvent

鼠标是用户的次要定位设施。DOM3 Events定义了9种鼠标事件:

  • click。用户单击鼠标主键(通常是左键)或按键盘回车键时触发。
  • dblclick。用户双击鼠标主键(通常是左键)时触发(DOM3 Events中标准化)
  • mousedown。用户按下任意鼠标键时触发。不能通过键盘触发
  • mouseenter。用户把鼠标光标从元素内部移到元素外部时触发。不冒泡,也不会在光标通过后辈元素时触发。(DOM3 Events中新增)
  • mouseleave。用户把鼠标光标从元素外部移到元素内部时触发。不冒泡,也不会在光标通过后辈元素时触发。(DOM3 Events中新增)
  • mousemove。鼠标光标在元素上挪动时重复触发。不能通过键盘触发
  • mouseout。用户把鼠标光标从一个元素移到另一个元素上(内部元素或子元素)时触发。不能通过键盘触发
  • mouseover。用户把鼠标光标从元素内部移到元素外部时触发。不能通过键盘触发
  • mouseup。用户开释鼠标键时触发。不能通过键盘触发

页面中所有元素都反对鼠标事件。除了mouseenter和mouseleave,其余鼠标事件都会冒泡,都能够被勾销,这会影响浏览器的默认行为。因为事件之间存在关系,因而勾销鼠标事件的默认行为也会影响其余事件。

双击鼠标主键会按如下程序触发事件:

1)mousedown

2)mouseup

3)click

4)mousedown

5)mouseup

6)click

7)dblclick

click和dblclick在触发前都依赖其余事件触发,mousedown和mouseup则不会受其余事件影响。

IE8和更早的版本的实现中存在问题,会导致双击事件跳过第二次mousedown和click事件。

1)mousedown

2)mouseup

3)click

4)mouseup

5)dblclick

DOM3 Events中鼠标事件对应的类型是”MouseEvent“(复数模式)

鼠标事件还有一个名为滚轮事件的子类别。滚轮事件只有一个事件mousewheel,对应鼠标滚轮或带滚轮的相似设施上滚轮的交互。

鼠标事件event对象的一些属性:

  1. 客户端坐标

    注:客户端坐标不思考页面滚动

  2. 页面坐标

    在页面上的地位。示意事件产生时鼠标光标在页面上的坐标,通过event对象的pageX和pageY属性获取。

    反映的是光标到页面而非视口右边与上边的间隔。

  3. 屏幕坐标

    鼠标光标在屏幕上的坐标,通过event对象的screenX和screenY属性获取。

  4. 润饰键

    有时要确定用户想实现的操作,还要思考键盘按键的状态

    键盘上的润饰键Shift、Ctrl、Alt和Meta(win的window键,mac的command键)常常用于批改鼠标事件的行为。

    4个属性来示意这几个润饰键的状态:shiftKey、ctrlKey、altKey、metaKey。(被按下为true,否则为false)

    当初浏览器反对所有4个润饰键,IE8及更早版本不反对metaKey属性。

  5. 相干元素

    对mouseover和mouseout事件而言,还存在与事件相干的其余元素

  6. 鼠标按键

    对mousedown和mouseup事件来说,event对象上会有一个button属性,示意按下或开释的是哪个按键。DOM为button属性定义了3个值:0-主键;1-中键(通常是滚轮键);2-副键。

    IE8及更早版本也提供了button属性,思考了同时按多个键的状况。

  7. 额定事件信息

    DOM2 Events标准在event对象上提供了detail属性,以给出对于事件的更多信息。对鼠标事件来说,detail蕴含一个数值,示意在给定地位上产生了多少次单击(间断单击)。每次单击会加1。间断点击中断会重置为0。

    IE还为每个鼠标事件提供了以下额定信息:

    • altLeft,布尔值,是否按下了左Alt键(如果为true,则altKey也为true)
    • ctrlLeft,布尔值,是否按下左Ctrl键(如果为true,则ctrlKey也为true)
    • offsetX,光标绝对于指标元素边界的x坐标
    • offsetY,光标绝对于指标元素边界的y坐标
    • shiftLeft,布尔值,是否按下了左Shift键(如果为true,则shiftKey也为true)

滚轮mousewheel事件

在用户应用鼠标滚轮时触发,包含在垂直方向上任意滚动。会在任何元素上触发,并(在IE8中)冒泡到document和(所有古代浏览器中)window。

event对象蕴含鼠标事件的所有规范信息,此外还有一个名为wheelDelta的属性。

少数状况下只需晓得滚轮滚动的方向,而这通过wheelDelta值的符号就能够晓得。(向前滚动一次+120,向后滚动一次-120)

触摸屏设施

触摸屏通常不反对鼠标操作。

  • 不反对dblclick事件。(测试一加三能够)
  • 单指导触屏幕上的可点击元素会触发mousemove事件。(测试一加三不行)可点击元素是指导击时有默认动作的元素(如链接)或指定了onclick事件处理程序的元素
  • mousemove事件也会触发mouseover和mouseout事件。(还未测试)
  • 双指导触屏幕并滑动导致页面滚动时会触发mousewheel和scroll事件。(还未测试)

无障碍问题

如果Web利用或网站要思考残障人士,特地是应用屏幕阅读器的用户,那么必须小心应用鼠标事件(除了回车键能够触发click事件,其余鼠标事件不能通过键盘触发)。倡议不要应用click事件之外的其余鼠标事件向用户提醒性能或触发代码执行。=> 会严格障碍盲人或视障用户应用。

几条应用鼠标事件时应该遵循的无障碍倡议

  • 应用click事件执行代码。当应用onmousedown执行代码时,应用程序会运行得更快,但屏幕阅读器无奈触发mousedown事件
  • 不要应用mouseover向用户显示新选项。无奈触发。能够思考键盘快捷键
  • 不要应用dblclick执行重要的操作。无奈触发

更多网站无障碍的信息,能够参考WebAIM网站。

键盘与输出事件KeyboardEvent

用户操作键盘时触发。很大水平上是基于原始的DOM0实现的。

DOM3 Events为键盘事件提供了一个首先在IE9中齐全实现的标准,其余浏览器也开始实现该标准,但仍存在很多遗留的实现。

蕴含3个事件:

  • keydown,按下键盘上某个键时触发,继续按钮会反复触发
  • keypress(DOM3 Events曾经废除,举荐textInput事件),按下键盘上某个键并产生字符时触发,继续按住会反复触发。Esc键也会触发(Chrome里测试不会触发?)。
  • keyup,用户开释键盘上某个键时触发。

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

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

  1. 当用户按下某个字符键时,会触发keydown事件,而后触发keypress事件,最初触发keyup事件。(keydown和keypress会在文本框呈现变动之前触发,keyup会在文本框呈现变动之后触发);如果按住不放,keydown和keypress会反复触发,直到这个键被开释。
  2. 当用户按下非字符键时,会触发keydown事件,而后触发keyup事件。如果按住不放,keydown会反复触发,直到这个键被开释。

注:键盘事件反对与鼠标事件雷同的润饰键。

键盘事件event对象的一些属性:

  1. 键码keyCode

    keydown和keyup事件,event对象的keyCode属性会保留一个键码。

    对于字母和数字键,keyCode的值与大写字母和数字的ASCII编码统一。与是否按了Shift键无关。

    DOM和IE的event对象都反对keyCode属性

  2. 字符编码charCode

    keypress事件,意味着按键会影响屏幕上显示的文本。对插入或移除字符的键,所有浏览器都会触发keypress事件,其余键则取决于浏览器。

    event对象上的charCode属性,只有产生keypress事件时这个属性才会被设置值(此时与keyCode属性相等)。蕴含的是按键字符对应的ASCII编码。

    IE8及更早版本和Opera应用keyCode传播字符的ASCII编码。要以跨浏览器形式获取字符编码,首先要检测charCode属性是否有值,如果没有再应用keyCode。

    有了字母编码,就能够应用String.fromCharCode()办法将其转换为理论的字符了。

DOM3的变动

DOM3 Events标准并未规定charCode属性,而是定义了key和char两个新属性。

key属性用于代替keyCode,且蕴含字符串。按下字符键时,key等于文本字符;按下非字符键时,key的值是键名(如”Shift“或”ArrowDown“)

char属性在按下字符键时与key相似,在按下非字符键时为null。(测试Chrome中keypress和keydown的event对象此属性都无)

IE反对key属性但不反对char属性。

Safari和Chrome反对keyIdentifier属性(测试Chrome无此属性)。对于字符键,keyIdentifier返回以”U+0000“模式示意Unicode值的字符串模式的字符编码。

因为不足跨浏览器反对,不倡议应用key、keyIdentifier和char。

DOM3 Events也反对一个名为location的属性,是一个数值,示意是在哪里按的键。可能的值为:0-默认键;1-右边;2-左边;3-数字键盘;4-挪动设施(虚构键盘);5-游戏手柄。Safari和Chrome反对一个等价的keyLocation属性(实现有问题)

没有失去广泛支持,不倡议在跨浏览器开发时应用location属性。

给event对象减少了getModifierState()办法,接管一个参数,一个等于Shift、Control、Alt、AltGraph或Meta的字符串,示意要检测的润饰键。如果给定 的润饰键被按钮,则返回true。(也可间接应用event对象的shiftKey、ctrlKey、altKey或metaKey属性获取)

输出textInput事件

DOM3 Events标准新增,在字符被输出到可编辑区域时触发。

与keypress比对:1. keypress会在任何能够取得焦点的元素上触发,textInput只在可编辑区域上触发;2. textInput只在有新字符被插入时才会触发,而keypress对任何可能影响文本的键都会触发(包含退格键(Chrome里测试不会触发?))。3. 应用输入法(搜狗)时,在触发合成事件时不会触发keypress,在compositionend触发之前会先触发textInput事件。4. 应用键盘输入先触发keypress,再触发textInput。

该事件次要关注字符,event对象上有data属性,为被插入的字符(非字符编码);还有一个inputMethod的属性(Chrome中测试无此属性打印为undefined),示意向控件中输出文本的伎俩,能够辅助验证

设施上的键盘事件(非键盘)

任天堂Wii

合成事件

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

合成事件有以下3种:

  • compositionstart,示意输出行将开始
  • compositionupdate,在新字符插入输出字段时触发;
  • compositionend,示意恢复正常键盘输入

惟一减少的事件属性是data:

  • 在compositionstart中,为正在编辑的文本(默认是空串,或者是选中的文本)
  • 在compositionupdate中,为要插入的新字符
  • 在compositionend中,为本次合成过程中输出的全部内容

测试失去的触发程序:

keydown -> ...start -> ...update -> ( (keyup) -> keydown -> ...update -> (keyup))(循环触发) -> ...update(此时data与...end事件中的data统一) -> textInput -> ...end -> keyup

变动事件

DOM2的变动事件(Mutation Events),在DOM发生变化时提供告诉。

(已废除)

曾经被Mutation Observers所取代(第14章)

HTML5事件

HTML5中失去浏览器较好反对的一些事件(标准未涵盖)

  1. contextmenu事件

    单击鼠标右键(Ctrl+单击左键)。用于容许开发者勾销默认的上下文菜单并提供自定义菜单。冒泡。

    事件指标是触发操作的元素,这个事件在所有浏览器中都能够勾销(event.preventDefault()或event.returnValue设置为false)。

    通常通过onclick事件处理程序触发暗藏(自定义菜单)。

  2. beforeunload事件

    在window上触发。用于给开发者提供阻止页面被卸载的机会。在页面行将从浏览器中卸载时触发。

    不能取消,否则就意味着能够把用户永恒拦截在一个页面上。

    该事件会向用户显示一个确认框。用户能够点击勾销或者确认来到页面。须要将event.returnValue设置为要在确认框中显示的字符串(对于IE和FF来说)(测试FF显示的提醒文字与returnValue属性值无关),并将其作为函数值返回(对于Safari和Chrome来说)(测试Chrome无返回值也无影响)

  3. DOMContentLoaded事件

    会在DOM树构建实现后立刻触发,而不必期待图片、JavaScript文件、CSS文件或其余资源加载实现。(能够在内部资源下载的同时指定事件处理程序,从而让用户可能更快地与页面交互)

    比对load事件:要期待很多内部资源加载实现。

    须要给document或window增加事件处理程序(理论的事件指标是document,会冒泡到window)。

    通常用于增加事件处理程序或执行其余DOM操作。这个事件始终在load事件之前触发。

    对于不反对DOMContentLoaded事件的浏览器,能够应用超时为0的setTimeout()函数,通过其回调来设置事件处理程序。实质上是在以后JavaScript过程执行结束后立刻执行这个回调。(与DOMContentLoaded触发机会统一无绝对把握,最好是页面上的第一个超时代码)

  4. readystatechange事件

    IE首先定义。用于提供文档或元素加载状态的信息,但行为有时不稳固。

    event.target或其余反对readystatechange事件的对象都有一个readyState属性,该属性可能为以下5个值:

    • uninitialized:对象存在并尚未初始化
    • loading:对象正在加载数据
    • loaded:对象曾经加载完数据
    • interactive √:对象能够交互,但尚未加载实现
    • complete √:对象加载实现

    并非所有对象都会经验所有readyState阶段(Chrome测试document只经验了两个阶段:interactive和complete)

    值为”interactive“的readyState阶段,机会相似于DOMContentLoaded。进入交互阶段,意味着DOM树已加载实现。(此时图片和其余内部资源不肯定都加载实现了)。

    与load事件独特应用时,这个事件的触发程序不能保障。interactive和complete的程序也不是固定的,为了抢到较早的机会,须要同时检测交互阶段和实现阶段(能够保障尽可能靠近应用DOMContentLoaded事件的成果)。

  5. pageshow与pagehide事件

    FF和Opera开发的一个名为往返缓存(bfcache,back-forward cache)的性能,旨在应用浏览器”后退“和”后退“按钮时放慢页面之间的切换。不仅存储页面数据,也存储DOM和JavaScript状态,实际上是把整个页面都保留在内存里。

    如果页面在缓存中,导航到这个页面时就不会触发load事件。

    • pageshow:在页面显示时触发,无论是否来自往返缓存。新加载的页面,会在load事件之后触发;来自往返缓存的页面,会在页面状态完全恢复后触发。事件指标是document,但事件处理程序必须增加到window上。(点击了浏览器的”刷新“按钮,页面会从新加载)。event对象中的persisted属性为布尔值,示意页面内容是否来自往返缓存。
    • pagehide:在页面从浏览器中卸载后,在unload事件之前触发。事件指标是document,但事件处理程序必须增加到window上。event对象中的persisted属性为布尔值,示意页面在卸载后是否保留在往返缓存中。

      注册了onunload事件处理程序的页面会主动排除在往返缓存之外(测试beforeunload也会影响),因为onunload的典型场景就是撤销onload事件产生时所做的事件,如果应用往返缓存,下一次页面显示时就不会触发onload事件,这可能导致页面无奈应用。

  6. hashchange事件

    用于在URL散列值(#前面的局部)发生变化时告诉开发者。

    事件处理程序必须增加给window。event对象有两个新属性:oldURL和newURL,别离保留变动前后的URL,蕴含散列值的残缺URL。如果想确定以后的散列值,最好应用location对象。

设施事件

智能手机和平板计算机=>交互的新形式

用于确定用户应用设施的形式。

  1. orientationchange事件

    苹果,挪动Safari浏览器。判断用户的设施是处于垂直模式还是程度模式。window.orientation属性,有3种值:0-垂直模式,90-左转程度模式(Home键在右),-90-右转程度模式(Home键在左)。当属性值扭转就会触发该事件。

    所有iOS设施都反对该事件和该属性。(测试锁定竖屏=>不会扭转)

    被认为是window事件,也可给body元素增加onorientationchange属性来指定事件处理程序。

  2. deviceorientation事件

    DeviceOrientationEvent标准定义的事件。

    如果能够获取设施的减速计信息,且数据产生了变动,就会在window上触发。只反馈设施在空间中的朝向,与挪动无关。

    设施自身处于3D空间,x轴方向为从设施左侧到右侧,y轴方向为从设施底部到上部,z轴方向为从设施反面到侧面。

    event对象蕴含各个轴绝对于设施静置时坐标值的变动,次要有5个属性:

    • alpha:0~360内的浮点值,示意围绕z轴旋转时y轴的度数(左右转)
    • beta:-180~180内的浮点值,示意围绕x轴旋转时z轴的度数(前后转)
    • gamma:-90~90内的浮点值,示意围绕y轴旋转时z轴的度数(扭转)。
    • absolute:布尔值,示意设施是否返回绝对值。
    • compassCalibrated:布尔值,示意设施的指南针是否正确校准。

    测试iPhone8(iOS11.4.1)平放在桌面上也始终监听到变动(?),测试Android(一加三)平放在桌面上后不会变动

  3. devicemotion事件

    DeviceOrientationEvent标准定义的事件。

    用于提醒设施实际上在挪动,而不仅仅是扭转了朝向。event对象蕴含的额定属性:

    • acceleration:对象,蕴含x、y和z属性,反映不思考重力状况下各个维度的减速信息
    • accelerationIncludingGravity:对象,蕴含x、y和z属性,反映各个维度的减速信息,蕴含z轴天然重力加速度
    • interval:毫秒,间隔下次触发事件的工夫。此值在事件之间应为常量。
    • rotationRate:对象,蕴含alpha、beta和gamma属性,示意设施朝向。

    如果无奈提供acceleration、accelerationIncludingGravity、rotationRate信息,则属性值为null。=> 应用之前必须先检测

    测试iPhone8(iOS11.4.1)平放在桌面上也始终监听到变动,测试Android(一加三)平放在桌面上也始终监听到变动

触摸及手势事件

只实用于触屏设施。

Webkit为Android定制了很多专有事件,成为了事实标准,并被纳入W3C的Touch Events标准。

  1. 触摸事件

    如下几种:

    • touchstart:手指放到屏幕上时触发
    • touchmove:手指在屏幕上滑动时间断触发。在此事件中调用preventDefault()能够阻止滚动(测试并不能)
    • touchend:手指从屏幕上移开时触发
    • touchcancel:零碎进行跟踪触摸时触发。文档未明确什么状况下进行跟踪。

    都会冒泡,都能够被勾销。不属于DOM标准,浏览器以兼容DOM的形式实现它们。每个触摸事件的event对象都提供了鼠标事件的公共属性,另外提供以下3个属性用于跟踪触点:

    • touches:Touch对象的数组,示意以后屏幕上的每个触点。
    • targetTouches:Touch对象的数组,示意特定于事件指标的触点。
    • changedTouches:Touch对象的数组,示意自上次用户动作之后变动的触点

    每个Touch对象蕴含一些属性,可用于追踪屏幕上的触摸轨迹。(针对一个触点)touchend事件触发时touches汇合中什么也没有,这是因为没有滚动的触点了。

    当手指点触屏幕上的元素时,顺次触发的事件(测试与书本不统一):

    1)touchstart

    2)touchend

    3)mousemove

    4)mousedown

    5)mouseup

    6)click

  2. 手势事件

    iOS2.0中的Safari中减少。在两个手指触碰屏幕且绝对间隔或旋转角度变动时触发。有如下3种:

    • gesturestart:一个手指在屏幕上,再把另一手指放到屏幕上时触发
    • gesturechange:任何一个手指在屏幕上的地位发生变化时触发
    • gestureend:其中一个手指来到屏幕时触发

    都会冒泡。

    只有在两个手指同时接触事件接收者时(指标元素边界以内),这些事件才会触发。

    触摸事件和手势事件存在肯定的关系。

    每个手势事件的event对象都蕴含所有规范的鼠标事件属性,新增了两个属性是rotation和scale。

    rotation:示意手指变动旋转的度数,负值示意逆时针旋转,正值示意顺时针旋转(从0开始);

    scale:示意两指之间间隔变动(对捏)的水平,开始时为1,而后随着间隔增大或放大相应地增大或放大。

    触摸事件也会返回rotation和scale属性,但只在两个手指触碰屏幕时才会变动。

其余一些标准中定义的浏览器事件

参考书本

内存与性能

在JavaScript中,页面中事件处理程序的数量与页面整体性能间接相干。

首先,每个函数都是对象,都占用内存空间;其次,为指定事件处理程序所需拜访DOM的次数会先期造成整个页面交互的提早。

改善页面性能?

事件委托

”过多事件处理程序“的解决方案是应用事件委托。

利用事件冒泡,能够只应用一个事件处理程序来治理一种类型的事件。只有给所有元素(须要解决某种事件的元素)独特的先人节点增加一个事件处理程序,就能够解决问题(依据target判断进行不同的解决)。=> 只拜访了一个DOM元素和增加了一个事件处理程序。 => 占用内存更少,所有应用按钮的事件(大多数鼠标事件和键盘事件)都实用于这个解决方案。

只有可行,就应该思考只给document增加一个事件处理程序,通过它解决页面中所有某种类型的事件。长处如下:

  • document对象随时可用。=> 只有页面渲染出可点击的元素,就能够无提早地起作用。
  • 既能够节俭DOM援用,也能够节省时间(设置页面事件处理程序的事件)。
  • 缩小整个页面所需的内存,晋升整体性能。

最适宜应用事件委托的事件包含:click、mousedown、mouseup、keydown和keypress。

删除事件处理程序

把事件处理程序指定给元素后,在浏览器代码和负责页面交互的JavaScript代码之间就建设了分割。这种分割建设得越多,页面性能就越差。除了应用事件委托缩小这种分割外,还应及时删除不必的事件处理程序。

很多Web利用性能不佳都是因为无用的事件处理程序长驻内存导致的。起因如下:

  • 删除带有事件处理程序的元素。如应用办法removeChild()或replaceChild()删除节点,或应用innerHTML整体替换页面的某一部分。=> 被删除的元素上若有事件处理程序,就不会被垃圾收集程序失常清理。(特地是IE8及更早版本,元素的援用和事件处理程序的援用)

    如果晓得某个元素会被删除,那么最好在删除它之前手工删除它的事件处理程序(或者不间接给它增加事件处理程序,应用事件委托)。=>确保内存被回收,元素也能够平安地从DOM中删掉。

    留神:在事件处理程序中删除元素会阻止事件冒泡。只有事件指标依然存在于文档中时,事件才会冒泡。

  • 页面卸载导致内存中残留援用。事件处理程序没有被清理,会残留在内存中。

    最好在onunload事件处理程序中趁页面尚未卸载先删除所有事件处理程序。=> 应用事件委托的劣势:事件处理程序很少。

模仿事件

通常事件都是由用户交互或浏览器性能触发。

能够通过JavaScript在任何时候触发任意事件 => 在测试Web利用时特地有用

DOM3标准指明了模仿特定类型事件的形式。

DOM事件模仿

步骤:

  1. 应用document.createEvent()办法创立一个event对象。

    createEvent()办法接管一个参数,一个示意要创立事件类型的字符串。DOM2是英文复数模式,DOM3中是英文复数模式。可用值为以下之一:

    • ”UIEvents“(DOM3是”UIEvent“):通用用户界面事件(鼠标事件和键盘事件都继承于此)
    • ”MouseEvents“(DOM3是”MouseEvent“):通用鼠标事件
    • ”HTMLEvents“(DOM3中无):通用HTML事件(已扩散到其余事件大类中)
    • 键盘事件(DOM3 Events中减少)
    • “Events”:通用事件
    • “CustomEvent”(DOM3中减少):自定义事件
  2. 应用事件相干的信息来初始化

    每种类型的event对象都有特定的办法,取决于调用createEvent()时传入的参数

  3. 触发事件

    事件指标调用dispatchEvent()办法。该办法存在于所有反对事件的DOM节点上。

    接管一个参数,即要触发事件的event对象

  4. 冒泡并触发事件处理程序执行

不同事件类型的模仿:

  1. 鼠标事件

    • 调用createEvent()并传入”MouseEvents“参数
    • 调用返回的event对象的initMouseEvent()办法,为新对象指定鼠标的特定信息。接管15个参数,别离对应鼠标事件会裸露的属性,如type、bubbles、cancelable、view等,这四个是正确模仿事件惟一重要的几个参数,因为浏览器要用到,其余参数则是事件处理程序要用的。
    • event对象的target属性会主动设置为调用dispatchEvent()办法的节点

    所有鼠标事件都能够在DOM合规的浏览器中模仿进去

  2. 键盘事件

    • DOM3中创立键盘事件的形式是给createEvent()办法传入参数”KeyboardEvent“
    • 调用返回的event对象的initKeyboardEvent()办法。接管8个参数,包含type、bubbles、cancelable、view等。

    DOM3 Events中废除了keypress事件,因而只能通过上述形式模仿keydown和keyup事件。

    在应用document.createEvent("KeyboardEvent")之前,最好检测一下浏览器对DOM3键盘事件的反对状况document.implementation.hasFeature("KeyboardEvents", "3.0")

    测试Chrome,调用initKeyboardEvent()办法传入的key和modifier参数与在事件处理程序中打印进去的属性不统一,能够应用new KeyboardEvent()(参数与在事件处理程序中打印出的统一),另,两种模仿都不会使文本框中有内容

    FF限定:

    • 给createEvent()传入”KeyEvents“来创立键盘事件
    • 调用event对象的initKeyEvent()办法。接管10个参数,包含type、bubbles、cancelable、view等。

    测试:ff(88.0)显示不反对 Uncaught DOMException: Operation is not supported

    其余不反对键盘事件的浏览器:

    • 创立一个通用的事件,给createEvent()办法传入参数”Events“
    • 调用event对象的initEvent()办法
    • 通过event.xxx形式指定特定于键盘的信息

    必须应用通用事件而不是用户界面事件,因为用户界面事件不容许间接给event对象增加属性

  3. 其余事件

    HTML事件:

    • 调用createEvent()办法并传入“HTMLEvents”
    • 调用返回的event对象的initEvent()办法来初始化信息

    测试:模仿focus,能监听到事件,然而没有光标

  4. 自定义DOM事件

    DOM3新增自定义事件类型。不触发原生DOM事件。

    • 调用createEvent()并传入参数“CustomEvent”
    • 调用返回的event对象的initCustomEvent()办法,接管4个参数:type、bubbles、cancelable、detail
    • 调用dispatchEvent()

IE事件模仿

在IE8及更早版本中模仿事件。

步骤:

  1. 应用document对象的createEventObject()办法来创立event对象。不接管参数,返回一个通用event对象
  2. 手工给返回的对象指定心愿该对象具备的所有属性。(无初始化办法)可指定任何属性,包含IE8及更早版本不反对的属性。这些属性值对于事件来说并不重要,只有事件处理程序才会应用它们。
  3. 在事件指标上调用fireEvent()办法。接管2个参数:事件处理程序的名字和event对象。srcElement和type属性会主动指派到event对象。

IE反对的所有事件都能够通过雷同的形式来模仿。

小结

最常见的事件是在DOM3 Events标准或HTML5中定义的。

须要思考内存与性能问题:

  • 限度页面中事件处理程序数量。=> 防止占用过多内存导致页面响应慢,清理起来更不便

    • 应用事件委托
    • 在页面卸载或元素从页面删除前,清理掉相干的事件处理程序