JavaScript高级程序设计(第三版) 第13章

1### Menu

第13章 事 件

13.2 事件处理程序
  • 13.2.1 HTML事件处理程序
  • 13.2.2 DOM0 级事件处理程序
  • 13.2.3 DOM2 级事件处理程序
  • 13.2.4 IE事件处理程序
  • 13.2.5 跨浏览器的事件处理程序
13.3 事件对象
  • 13.3.1 DOM中的事件对象
13.4 事件类型
  • 13.4.1 UI事件
  • 13.4.2 焦点事件
  • 13.4.3 鼠标与滚轮事件
  • 13.4.4 键盘与文本事件
  • 13.4.6 变动事件
  • 13.4.7 HTML5 事件
13.5 内存和性能
  • 13.5.1 事件委托
  • 13.5.2 移除事件处理程序
13.6 模拟事件
  • 13.6.1 DOM中的事件模拟

第13章 事 件

13.2 事件处理程序
13.2.1 HTML事件处理程序
<input type="button" value="Click Me" onclick="alert('Clicked')" />
  • 这个操作是通过指定 onclick 特性并将一些 JavaScript
    代码作为它的值来定义的。由于这个值是 JavaScript,因此不能在其中使用未经转义的 HTML 语法字符,例如和号(&)、双引号("")、小于号(<)或大于号(>)。为了避免使用 HTML 实体,这里使用了单引号。如果想要使用双引号,那么就要将代码改写成如下所示:
<input type="button" value="Click Me" onclick="alert(&quot;Clicked&quot;)" />
  • 在 HTML 中定义的事件处理程序可以包含要执行的具体动作,也可以调用在页面其他地方定义的脚本,如下面的例子所示
<script type="text/javascript">
    function showMessage(){
        alert("Hello world!");
    }
</script>
<input type="button" value="Click Me" onclick="showMessage()" />

13.2.2 DOM0 级事件处理程序
  • 使用 DOM0 级方法指定的事件处理程序被认为是元素的方法。因此,这时候的事件处理程序是在
    元素的作用域中运行;换句话说,程序中的 this 引用当前元素。来看一个例子。
<body>
<button id="myBtn">click me11.</button>
<script>
    var btn = document.getElementById("myBtn");
    btn.onclick = function(){
    this.style.color = "red";   //"myBtn"
    };
    </script>
  • 单击按钮,button的字体会变成红色,this就代表当前操作的元素;
  • 也可以删除通过 DOM0 级方法指定的事件处理程序;
btn.onclick = null; //删除事件处理程序

13.2.3 DOM2 级事件处理程序
  • “ DOM2 级事件” 定义了两个方法,用于处理指定和删除事件处理程序的操作: addEventListener()和 removeEventListener()。所有 DOM 节点中都包含这两个方法,并且它们都接受 3 个参数(“时间名”,处理事件程序,一个布尔值),最后这个布尔值参数如果是 true,表示在捕获阶段调用事件处理程序;如果是 false,表示在冒泡阶段调用事件处理程序(布尔值最好填false)。 代码如下:
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
  • 使用 DOM2 级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。
// 会按顺序触发,先alert“mybtn”,再alert“hello world”
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
btn.addEventListener("click", function(){
alert("Hello world!");
}, false);
  • removeEventListener()
    removeEventListener()中的事件处理程序函数必须与传入
    addEventListener()中的相同,如果用匿名函数的话就无法移除;
    有效例子如下:
var btn = document.getElementById("myBtn");
var handler = function(){
    alert(this.id);
};
btn.addEventListener("click", handler, false);
//这里省略了其他代码
btn.removeEventListener("click", handler, false); //有效!
</script>
  • 大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。最好只在需要在事件到达目标之前截获它的时候将事件处理程序添加到捕获阶段。如果不是特别需
    要,我们不建议在事件捕获阶段注册事件处理程序。

  • 13.2.5 跨浏览器的事件处理程序
  • 第一个要创建的方法是 addHandler(),它的职责是视情况分别使用 DOM0 级方法、 DOM2 级方法,这个方法属于一个名叫 EventUtil 的对象,本书将使用这个对象来处理浏览器间的差异。addHandler()方法接受 3 个参数:要操作的元素、事件名称和事件处理程序函数。
  • 这两个方法首先都会检测传入的元素中是否存在 DOM2 级方法。如果存在 DOM2 级方法,则使用该方法:传入事件类型、事件处理程序函数和第三个参数 false(表示冒泡阶段)。注意,为了在 IE8 及更早版本中运行,此时的事件类型必须加上"on"前缀。
    最后一种可能就是使用 DOM0 级方法(在现代浏览器中,应该不会执行这里的代码)。此时,我们使用的是方括号语法来将属性名指定为事件处理程序,或者将属性设置为 null。
var EventUtil = {
    addHandler: function(element, type, handler){
        if (element.addEventListener){
            element.addEventListener(type, handler, false);
        } else {
            element["on" + type] = handler;
        }
    },
    removeHandler: function(element, type, handler){
        if (element.removeEventListener){
            element.removeEventListener(type, handler, false);
        } else {
            element["on" + type] = null;
        }
    }
};
  • 可以像下面这样使用 EventUtil 对象:
var btn = document.getElementById("myBtn");
var handler = function(){
    alert("Clicked");
};
EventUtil.addHandler(btn, "click", handler);
//这里省略了其他代码
EventUtil.removeHandler(btn, "click", handler);

13.3 事件对象
  • 13.3.1 DOM中的事件对象
  • 兼容 DOM 的浏览器会将一个 event 对象传入到事件处理程序中。无论指定事件处理程序时使用什么方法(DOM0 级或 DOM2 级) ,都会传入 event 对象。来看下面的例子。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.type); //"click"
};
btn.addEventListener("click", function(event){
    alert(event.type); //"click"
}, false);
  • event 对象包含与创建它的特定事件有关的属性和方法。触发的事件类型不一样,可用的属性和方法也不一样。不过,所有事件都会有下表列出的成员。


    event的属性和方法

    event的属性和方法
  • 最新的属性和方法去w3c看;

  • 在事件处理程序内部,对象 this 始终等于 currentTarget 的值,而 target 则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则 this、 currentTarget 和 target 包含相同的值。来看下面的例子。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.currentTarget === this); //true
alert(event.target === this); //true
};
  • 这个例子检测了 currentTarget 和 target 与 this 的值。由于 click 事件的目标是按钮,因此这三个值是相等的。如果事件处理程序存在于按钮的父节点中(例如 document.body),那么这些值是不相同的。再看下面的例子。
document.body.onclick = function(event){
alert(event.currentTarget === document.body); //true
alert(this === document.body); //true
alert(event.target === document.getElementById("myBtn")); //true
};
  • 在需要通过一个函数处理多个事件时,可以使用 type 属性。例如:
<button id="myBtn">click me11.</button>
<script>
    var btn = document.getElementById("myBtn");
    var handler = function(event){
        switch(event.type){
            case "click":
                alert("Clicked");
                break;
            case "mouseover":
                event.target.style.backgroundColor = "red";
                break;
            case "mouseout":
                event.target.style.backgroundColor = "";
                break;
        }
    };
    btn.onclick = handler;
    btn.onmouseover = handler;
    btn.onmouseout = handler;
</script>
  • 只有 cancelable 属性设置为 true 的事件,才可以使用 preventDefault()来取消其默认行为。
  • 要阻止特定事件的默认行为,可以使用 preventDefault()方法。例如,链接的默认行为就是在被单击时会导航到其 href 特性指定的 URL。如果你想阻止链接导航这一默认行为,那么通过链接的onclick 事件处理程序可以取消它,如下面的例子所示:
var link = document.getElementById("myLink");
link.onclick = function(event){
    event.preventDefault();
};
  • 另外, stopPropagation()方法用于立即停止事件在 DOM 层次中的传播,即取消进一步的事件
    捕获或冒泡。例如,直接添加到一个按钮的事件处理程序可以调用 stopPropagation(),从而避免触
    发注册在 document.body 上面的事件处理程序,如下面的例子所示。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert("Clicked");
    // 调用后,阻止向上冒泡  body注册的onclick就不会运行了。
    event.stopPropagation();
};
document.body.onclick = function(event){
    alert("Body clicked");
};
  • 事件对象的 eventPhase 属性,可以用来确定事件当前正位于事件流的哪个阶段。如果是在捕获阶段调用的事件处理程序,那么 eventPhase 等于 1;如果事件处理程序处于目标对象上,则 eventPhase 等于 2;如果是在冒泡阶段调用的事件处理程序, eventPhase 等于 3。这里要注意的是,尽管“处于目标”发生在冒泡阶段,但 eventPhase 仍然一直等于 2。来看下面的例子。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.eventPhase); //2
};
document.body.addEventListener("click", function(event){
    alert(event.eventPhase); //1
}, true);
document.body.onclick = function(event){
    alert(event.eventPhase); //3
};

13.4 事件类型
  • 13.4.1 UI事件
  • UI 事件指的是那些不一定与用户操作有关的事件。这些事件在 DOM 规范出现之前,都是以这种或
    那种形式存在的,而在 DOM 规范中保留是为了向后兼容。现有的 UI 事件如下。
    1. load:当页面完全加载后在 window 上面触发,当所有框架都加载完毕时在框架集上面触发,
      当图像加载完毕时在<img>元素上面触发,或者当嵌入的内容加载完毕时在<object>元素上面
      触发。
    2. unload 事件:与 load 事件对应的是 unload 事件,这个事件在文档被完全卸载后触发。只要用户从一个页面切换到另一个页面,就会发生 unload 事件。而利用这个事件最多的情况是清除引用,以避免内存泄漏。
    3. resize 事件:当浏览器窗口被调整到一个新的高度或宽度时,就会触发 resize 事件。这个事件在 window(窗口)上面触发,因此可以通过 JavaScript 或者<body>元素中的 onresize 特性来指定事件处理程序。
function myResize(event){
    li.innerHTML = document.documentElement.clientWidth  +" " + document.documentElement.clientHeight    // 显示现在的窗口clientSize
}
document.body.onresize = myResize;     
  1. scroll 事件:虽然 scroll 事件是在 window 对象上发生的,但它实际表示的则是页面中相应元素的变化。在混杂模式下,可以通过<body>元素的 scrollLeft 和 scrollTop 来监控到这一变化;

  • 13.4.2 焦点事件
  • 焦点事件会在页面元素获得或失去焦点时触发。利用这些事件并与 document.hasFocus()方法及document.activeElement 属性配合,可以知晓用户在页面上的行踪。有以下 6 个焦点事件。
  • blur:在元素失去焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
  • focus:在元素获得焦点时触发。这个事件不会冒泡;所有浏览器都支持它
  • focusin:在元素获得焦点时触发。这个事件与 HTML 事件 focus 等价,但它冒泡。
  • focusout:在元素失去焦点时触发。这个事件是 HTML 事件 blur 的通用版本。
  • 要确定浏览器是否支持这些事件,可以使用如下代码:
    var isSupported = document.implementation.hasFeature("FocusEvent", "3.0");

  • 13.4.3 鼠标与滚轮事件

  • 要检测浏览器是否支持上面的所有事件,可以使用以下代码:
    var isSupported = document.implementation.hasFeature("MouseEvent", "3.0")

  • click:在用户单击主鼠标按钮(一般是左边的按钮)或者按下回车键时触发。这一点对确保
    易访问性很重要,意味着 onclick 事件处理程序既可以通过键盘也可以通过鼠标执行。

  • dblclick:在用户双击主鼠标按钮(一般是左边的按钮)时触发。从技术上说,这个事件并不
    是 DOM2 级事件规范中规定的,但鉴于它得到了广泛支持,所以 DOM3 级事件将其纳入了标准。

  • mousedown:在用户按下了任意鼠标按钮时触发。不能通过键盘触发这个事件。

  • mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且
    在光标移动到后代元素上不会触发。 DOM2 级事件并没有定义这个事件,但 DOM3 级事件将它
    纳入了规范。 IE、 Firefox 9+和 Opera 支持这个事件。

  • mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,而且
    在光标移动到后代元素上不会触发。 DOM2 级事件并没有定义这个事件,但 DOM3 级事件将它
    纳入了规范。 IE、 Firefox 9+和 Opera 支持这个事件。

  • mousemove:当鼠标指针在元素内部移动时重复地触发。不能通过键盘触发这个事件。

  • mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入的另
    一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。不能通过键盘触发这个事件。

  • mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触
    发。不能通过键盘触发这个事件。

  • mouseup:在用户释放鼠标按钮时触发。不能通过键盘触发这个事件。

    1. 客户区坐标位置: 鼠标事件都是在浏览器视口中的特定位置上发生的。这个位置信息保存在事件对象的 clientX 和 clientY 属性中。
    1. 页面坐标位置: 通过事件对象的 pageX 和pageY 属性,能告诉你事件是在页面中的什么位置发生的。
    1. 屏幕坐标位置 : 通过 screenX 和 screenY 属性就可以确定鼠标事件发生时鼠标指针相对于整个屏幕的坐标信息。
function myCoor(event){
           li.innerHTML = "client size = "+ event.clientX + " " + event.clientY + "<br>" + 
" page size = " + event.pageX + " " + event.pageY + "<br>" + 
" screen size = " +  event.screenX + " " + event.screenY
}
  1. 修改键 : 虽然鼠标事件主要是使用鼠标来触发的,但在按下鼠标时键盘上的某些键的状态也可以影响到所要采取的操作。这些修改键就是 Shift、 Ctrl、 Alt 和 Meta(在 Windows 键盘中是 Windows 键,在苹果机中
    是 Cmd 键),它们经常被用来修改鼠标事件的行为。 DOM 为此规定了 4 个属性,表示这些修改键的状
    态: shiftKey、 ctrlKey、 altKey 和 metaKey。这些属性中包含的都是布尔值,如果相应的键被按
    下了,则值为 true,否则值为 false。当某个鼠标事件发生时,通过检测这几个属性就可以确定用户
    是否同时按下了其中的键。
function myClick(event){
        li.innerHTML = "ctrl = " + String(event.ctrlKey) + " " + "shift = 
" + String(event.shiftKey)+ " " + "microKey = " + String(event.metaKey)
}
  1. 相关元素 : 在发生 mouseover 和 mouserout 事件时,还会涉及更多的元素。这两个事件都会涉及把鼠标指
    针从一个元素的边界之内移动到另一个元素的边界之内。对 mouseover 事件而言,事件的主目标是获得光标的元素,而相关元素(event.relatedTarget)就是那个失去光标的元素。类似地,对 mouseout 事件而言,事件的主目标是失去光标的元素,而相关元素(event.relatedTarget)则是获得光标的元素。来看下面的例子。
    <div id="myDivA" style="height:50px">in DIV A</div>
    <div id="myDivB" style="height:50px">in DIV B</div>

    <script>
        function mouseOver(event){
            event.target.style.fontSize = "50px";
            event.relatedTarget.style.fontSize="5px";
        }
        myDivA = document.getElementById("myDivA");
        myDivB = document.getElementById("myDivB");
        myDivA.style.backgroundColor = "yellow";
        myDivB.style.backgroundColor = "orange";
        myDivA.addEventListener("mouseover", mouseOver, false);
        myDivB.addEventListener("mouseover", mouseOver, false);
  • 6. 鼠标按钮
  • 只有在主鼠标按钮被单击(或键盘回车键被按下)时才会触发 click 事件,因此检测按钮的信息并不是必要的。但对于 mousedown 和 mouseup 事件来说,则在其 event 对象存在一个 button 属性,表示按下或释放的按钮。
  • A number representing a given button:
    0: Main button pressed, usually the left button or the un-initialized state
    1: Auxiliary button pressed, usually the wheel button or the middle button (if present)
    2: Secondary button pressed, usually the right button
    3: Fourth button, typically the Browser Back button
    4: Fifth button, typically the Browser Forward button
    <button onmousedown="mouseClick(event)">click</button>
    <script>
        function mouseClick(event) {
            alert(event.button);    // event.button 当鼠标左键按下时,值为0,滚轮为1,右键为2;
        }
  • 7. 更多的事件信息

  • “DOM2 级事件”规范在 event 对象中还提供了 detail 属性,用于给出有关事件的更多信息。对
    于鼠标事件来说, detail 中包含了一个数值,表示在给定位置上发生了多少次单击。(最高只能计数到3);

  • altLeft:布尔值,表示是否按下了 Alt 键。如果 altLeft 的值为 true,则 altKey 的值也为 true。

  • ctrlLeft:布尔值,表示是否按下了 Ctrl 键。如果 ctrlLeft 的值为 true,则 ctrlKey 的值
    也为 true。

  • shiftLeft:布尔值,表示是否按下了 Shift 键。如果 shiftLeft 的值为 true,则 shiftKey
    的值也为 true。

  • offsetX:光标相对于目标元素边界的 x 坐标。

  • offsetY:光标相对于目标元素边界的 y 坐标。

  • 8. 鼠标滚轮事件

  • IE 6.0 首先实现了 mousewheel 事件。除了火狐其它都支持。当用户通过鼠标滚轮与页面交互、在垂直方向上滚动页面时(无论向上还是向下),就会触发 mousewheel事件。当用户向上滚动鼠标滚轮时,wheelDelta 是 120 的倍数;当用户向下滚动鼠标滚轮时, wheelDelta 是-120 的倍数。

  • Firefox 支持一个名为 DOMMouseScroll 的类似事件,也是在鼠标滚轮滚动时触发。与 mousewheel事件一样, DOMMouseScroll 也被视为鼠标事件,因而包含与鼠标事件有关的所有属性。而有关鼠标滚轮的信息则保存在 detail 属性中,当向前滚动鼠标滚轮时,这个属性的值是-3 的倍数,当向后滚动鼠标滚轮时,这个属性的值是 3 的倍数。


  • 13.4.4 键盘与文本事件
  • 有 3 个键盘事件:
    • keydown:当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件。
    • keypress:当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件。
      按下 Esc 键也会触发这个事件。Safari 3.1 之前的版本也会在用户按下非字符键时触发 keypress
      事件。
    • keyup:当用户释放键盘上的键时触发。
    • 虽然所有元素都支持以上 3 个事件,但只有在用户通过文本框输入文本时才最常用到。
  • 1. 键码
    • 在发生 keydown 和 keyup 事件时, event 对象的 keyCode 属性中会包含一个代码,与键盘上一个特定的键对应。对数字字母字符键, keyCode 属性的值与 ASCII 码中对应小写字母或数字的编码相同。
        input = document.getElementById("myinput");

        function keyPre(event){
            li.innerHTML = event.key     // event.key 返回字符本身
            alert(event.keyCode);     // event.keyCode 返回键码, 
        }
  • 使用 String.fromCharCode()将字符的ASCII码转换成实际的字符
    <ul>
        <li id="myLi"></li>
    </ul>
    <input type="text" id="myInput" onkeypress="keyEvent(event)">
    <script>
        li = document.getElementById("myLi");
        function keyEvent(event) {
            cc = event.charCode
            li.innerHTML = cc + " " + String.fromCharCode(cc)
        }
    </script>

  • 13.4.6 变动事件
    (DOM3 级事件模块作废了很多变动事件, 建议改用MutationObserver构造函数代替。)
    • DOM2 级的变动(mutation)事件能在 DOM 中的某一部分发生变化时给出提示。
    • DOMSubtreeModified:在 DOM 结构中发生任何变化时触发。这个事件在其他任何事件触发后都会触发。
    • DOMNodeInserted:在一个节点作为子节点被插入到另一个节点中时触发。
    • DOMNodeRemoved:在节点从其父节点中被移除时触发。
    • DOMNodeInsertedIntoDocument:在一个节点被直接插入文档或通过子树间接插入文档之后
      触发。这个事件在 DOMNodeInserted 之后触发。
    • DOMNodeRemovedFromDocument:在一个节点被直接从文档中移除或通过子树间接从文档中移
      除之前触发。这个事件在 DOMNodeRemoved 之后触发。
    • DOMAttrModified:在特性被修改之后触发。
    • DOMCharacterDataModified:在文本节点的值发生变化时触发。
    • 使用下列代码可以检测出浏览器是否支持变动事件:
var isSupported = document.implementation.hasFeature("MutationEvents", "2.0");
  • 1. 删除节点

    • 在使用removeChild()或replaceChild()从 DOM中删除节点时,首先会触发DOMNodeRemoved事件。
    • 如果被移除的节点包含子节点,那么在其所有子节点以及这个被移除的节点上会相继触发DOMNodeRemovedFromDocument 事件。但这个事件不会冒泡,所以只有直接指定给其中一个子节点的事件处理程序才会被调用。
    • 紧随其后触发的是 DOMSubtreeModified 事件。这个事件的目标是被移除节点的父节点;
  • 2. 插入节点

    • 在使用 appendChild()、 replaceChild()或 insertBefore()向 DOM 中插入节点时,首先会触发 DOMNodeInserted 事件。
    <button id="myBtn">delete</button>
    <script>
        btn = document.getElementById("myBtn");
        btn.addEventListener("click", delElement);
        function delElement(event){
            img = document.createElement("img");
            img.src = "box.jpg";
            img.style.width = "100px";
            img.style.height = "100px";
            document.body.appendChild(img)
        }
        document.addEventListener("DOMNodeInserted", keyEvent)
        function keyEvent(event) {
            input.value = event.type + " " + event.target;
        }
    </script>

  • 13.4.7 HTML5 事件
  • 1. contextmenu 事件
    • 用以表示何时应该显示上下文菜单,以便开发人员取消默认的上下文菜单而提供自定义的菜单。上下文菜单是指在用户交互(例如右键点击)时出现的菜单。 HTML5允许我们自定义此菜单。
<div id="myDiv">Right click or Ctrl+click me to get a custom context menu. Click anywhere else to get the default context menu.</div>
    <ul id="myMenu" style="position:absolute; visibility:hidden; background-color:silver">
        <li><a href="http://www.nczonline.net">Nicholas’ site</a></li>
        <li><a href="http://www.wrox.com">Wrox site</a></li>
        <li><a href="http://www.yahoo.com">Yahoo!</a></li>
    </ul>
    <script>
        function addContextMenu(event){
            event.preventDefault();              //隐藏默认上下文菜单;
            menu = document.getElementById("myMenu");
            menu.style.left = event.clientX + "px";
            menu.style.top = event.clientY + "px";
            menu.style.visibility = "visible";
        }
        div  = document.getElementById("myDiv");
        div.addEventListener("contextmenu", addContextMenu)
        div.addEventListener("click", function (event) {
            menu = document.getElementById("myMenu");
            menu.style.visibility = "hidden";
        })
    </script>
  • 所有浏览器都支持 contextmenu 事件, contextmenu元素只有 Firefox 浏览器支持。
<div contextmenu="mymenu">右击这里显示上下文菜单。
    <menu type="context" id="mymenu">
        <menuitem label="Refresh"></menuitem>
        <menuitem label="Twitter"></menuitem>
    </menu>
</div>
  • 在所有浏览器中都可以取消这个事件:在兼容 DOM 的浏览器中,使用 event.preventDefalut();在 IE 中,将 event.returnValue 的值设置为 false。
  • 2. beforeunload 事件
    • 可以监听到浏览器关闭操作,能够在关闭之前,弹出一个对话框,让用户选择是否关闭。(只有IE支持?)
    • 为了显示这个弹出对话框,必须将 event.returnValue 的值设置为要显示给用户的字符串(对
      IE 及 Fiefox 而言),同时作为函数的值返回(对 Safari 和 Chrome 而言)(???Chrome和火狐不支持);
function myFunc(event){
    var message = "I'm really going to miss you if you go.";
    event.returnValue = message;
}
window.addEventListener("beforeunload", myFunc)
  • 3. DOMContentLoaded 事件

    • 在形成完整的 DOM 树之后就会触发,不理会图像、 JavaScript 文件、 CSS 文件或其他资源是否已经下载完毕。而 load 事件是在页面中的一切都加载完毕时触发。
    • 可以为 document 或 window 添加 DOMContentLoaded 事件(尽管
      这个事件会冒泡到 window,但它的目标实际上是 document)。
    • 这个事件始终都会在 load 事件之前触发。
  • 4. readystatechange 事件

    • 提供与文档或元素的加载状态有关的信息。
    • 当对象的readyState属性值发生变化时出发此事件。
    • readyState属性值有:
      • loading:加载(document 仍在加载。)
      • interactive / 互动(文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。)
      • complete / 完成(T文档和所有子资源已完成加载。状态表示 load 事件即将被触发。 )
document.addEventListener("readystatechange", function(event){
    alert("load completed")
})
  • 对于 document 而言,值为"interactive"的 readyState 会在与 DOMContentLoaded 大致相同的时刻触发 readystatechange 事件。
// 以下类似使用DOMContentLoaded 
document.addEventListener("readystatechange", function(event){
    if (document.readyState == "interactive"){
        alert("Content loaded");
    }
})
  • 交互阶段可能会早于也可能会晚于完成阶段出现,无法确保顺序。在包含
    较多外部资源的页面中,交互阶段更有可能早于完成阶段出现;而在页面中包含较少外部资源的情况下,完成阶段先于交互阶段出现的可能性更大。因此,为了尽可能抢到先机,有必要同时检测交互和完成阶段。
document.addEventListener("readystatechange", function(event){
    if (document.readyState == "interactive" || document.readyState == "complete"){
        document.removeEventListener("readystatechange", arguments.callee);
        alert("Content loaded");
    }
})
  • 另外, <script>(在 IE 和 Opera 中)和<link>(仅 IE 中)元素也会触发 readystatechange事件,可以用来确定外部的 JavaScript 和 CSS 文件是否已经加载完成。

  • 5. pageshow 和 pagehide 事件

    • Firefox 和 Opera 有一个特性,名叫“往返缓存”(back-forward cache,或 bfcache),可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度。将整个页面都保存在了内存里。如果页面位于 bfcache 中, 再次打开该页面时就不会触发 load 事件。但为了更形象地说明 bfcache 的行为, Firefox 还是提供了一些新事件:
    • pageshow:这个事件在页面显示时触发,无论该页面是否来自 bfcache。
      • 在重新加载的页面中, pageshow 会在 load 事件触发后触发;而对于 bfcache 中的页面, pageshow 会在页面状态完全恢复的那一刻触发。另外要注意的是,虽然这个事件的目标是 document,但必须将其事件处理程序添加到 window。
// 这个例子使用了私有作用域,以防止变量 showCount 进入全局作用域。
(function(event){
    var showCount = 0;
    window.addEventListener("load", function(){
        alert("Load fired");
    });
// 离开之后,又单击“后退”按钮返回该页面,就会看到 showCount每次递增的值。
    window.addEventListener("pageshow", function(event){
        showCount++;
        alert("Show has been fired " + showCount + " times.");
        alert("Show has been fired " + showCount + " times. Persisted? " + event.persisted);  // event有个persisted, 如果页面被保存在了 bfcache 中,则这个属性的值为 true;
    });
})();
  • pagehide 事件(??????):该事件会在浏览器卸载页面的时候触发,而且是在unload 事件之前触发。
    • 这个事件的 event 对象也包含 persisted 属性,不过其用途稍有不同;如果页面在卸载之后会被保存在 bfcache 中,那么 persisted 的值也会被设置为 true。因此,当第一次触发 pageshow 时, persisted 的值一定是 false,而在第一次触发 pagehide 时, persisted 就会变成 true(除非页面不会被保存在 bfcache 中)
        (function(){
            window.addEventListener("pagehide", function(event){
                alert("Hiding. Persisted? " + event.persisted);
            });
        })();
  • **6. hashchange 事件 **???????
    • 在 URL 的参数列表(及 URL 中“ #”号后面的所有字符串)
      发生变化时触发;
window.addEventListener("hashchange", function(event){
    alert("Old URL: " + event.oldURL + "\nNew URL: " + event.newURL);
});

13.5 内存和性能
  • 13.5.1 事件委托
    • 对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
    <ul id="myLinks">
        <li id="goSomewhere">Go somewhere</li>
        <li id="doSomething">Do something</li>
        <li id="sayHi">Say hi</li>
    </ul>
    <script>
        var list = document.getElementById("myLinks");
        list.addEventListener("click", function(event){
            switch(event.target.id){
                case "doSomething":
                    document.title = "I changed the document's title";
                    break;
                case "goSomewhere":
                    location.href = "http://www.wrox.com";
                    break;
                case "sayHi":
                    alert("hi");
                    break;
            }
        });
    </script>
  • 如果可行的话,也可以考虑为 document 对象添加一个事件处理程序,用以处理页面上发生的某种特定类型的事件。

    • document 对象很快就可以访问,而且可以在页面生命周期的任何时点上为它添加事件处理程序(无需等待 DOMContentLoaded 或 load 事件)。换句话说,只要可单击的元素呈现在页面上,就可以立即具备适当的功能。
    • 在页面中设置事件处理程序所需的时间更少。只添加一个事件处理程序所需的 DOM 引用更少,
      所花的时间也更少。
    • 整个页面占用的内存空间更少,能够提升整体性能。
  • 最适合采用事件委托技术的事件包括 click、mousedown、mouseup、keydown、keyup 和 keypress。

  • 13.5.2 移除事件处理程序

    • 使用 innerHTML 替换页面中某一部分的时候, 或使用 removeChild()和 replaceChild()方法时,内存中会留下那些过时不用的“空事件处理程序”(dangling event handler),也是造成 Web 应用程序内存与性能问题的主要原因。
    • 如果你知道某个元素即将被移除,那么最好手工移除事件处理程序,如下面的例子所示。
<div id="myDiv">
    <input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
    var btn = document.getElementById("myBtn");btn.onclick = function(){
        //先执行某些操作
        btn.onclick = null; //移除事件处理程序
        document.getElementById("myDiv").innerHTML = "Processing...";
    };
</script>
  • 导致“空事件处理程序”的另一种情况,就是卸载页面的时候。在页面卸载之前,先通过 onunload 事件处理程序移除所有事件处理程序。

  • 13.6 模拟事件

  • 13.6.1 DOM中的事件模拟

  • 创建和分派DOM事件。这些事件通常称为合成事件,而不是浏览器本身触发的事件。

    • 创建自定义事件: Events 可以使用构造函数创建;
//创建自定义事件
var event = new Event('click');
// 添加事件监听
elem.addEventListener('click', function (event) { do something.. }, false);
// 派发事件
elem.dispatchEvent(event);
  • 添加自定义数据 – CustomEvent()
  • 使用CustomEvent接口的事件可用于携带自定义数据。
  • IE浏览器不支持此方法,需要用 var event = document.createEvent('CustomEvent');
<script type="text/javascript">
    function onClick(event){alert(event.detail)}  // 调用函数
    div = document.getElementById("myDiv");
    nowDate = new Date(); // 时间实例
    // 创建CustomEvent Dict的可用event按键取值,可放任何数据;
    // 还有更多可选参数 可在MDN里查看
    var myCustomEvent = new CustomEvent('click', {'detail': nowDate.toLocaleDateString()});
    div.addEventListener("click", onClick, false);  // 添加监听;
    div.dispatchEvent(myCustomEvent)   // 向元素派发事件
</script>

2018/11/7

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容