事件

  • 事件流
  • 事件处理程序
  • 事件对象
  • 事件类型
  • 内存和性能
  • 模拟事件

JavaScript 与 HTML 之间的交互是通过事件实现的,事件是文档或浏览器窗口中发生特定的交互瞬间。可以用侦听器或处理程序来预定事件,以便事件发生时执行相应的代码,在传统软件工程中被称为观察员模式的模型,支持页面行为与页面外观之间的松散耦合。

事件流

事件流描述的是从页面中接收事件的顺序,IE的事件流是事件冒泡流,Netscape Communicator的事件流是事件捕获流。事件冒泡是指事件开始时由最具体的元素接收,逐级向上传播到较为不具体的节点。事件捕获的思想是不太具体的节点应该更早接收到事件,最具体的节点应该最后接收到事件,事件捕获用于在事件到达预定目标之前捕获它。

DOM2级事件规定的事件流包括三个阶段:事件捕获阶段处于目标阶段事件冒泡阶段。首先发生事件捕获阶段,为截获事件提供了机会,然后实际的目标接收到事件,最后在冒泡阶段对事件做出响应。

事件处理程序

事件是用户或浏览器自身执行的某种动作,响应事件的函数叫做事件处理程序,事件处理程序的名字以on开头。

HTML事件处理程序中的代码在执行时,有权访问全局作用域中的任何代码,这样会创建一个封装着元素属性值的函数,这个函数中有一个局部变量event,即事件对象。缺点是

  • 当HTML元素绑定的事件触发时,当时的事件处理程序可能不具备执行条件
  • 扩展事件处理程序的作用域链在不同浏览器中会导致不同的结果,不同JavaScript 引擎遵循的标识符解析规则略有差异,很可能会在访问非限定对象成员时出错。
  • HTML代码与JavaScript代码紧密耦合,如果要更换事件处理程序,两个地方都需要改动。
<input type = "button" value = "Click me" onclick = "functionName()"/>

DOM 0级事件处理程序,将一个函数赋给事件处理程序属性,此时事件处理程序是在元素作用域中运行,以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。

var btn = document.getElementById("myBtn")
btn.onclick = function(){
    alert("Clicked")
}

DOM2级事件定义了两个方法用于处理指定和删除事件处理程序的操作,addEventListenerremoveEventListener,这个方法接收三个参数,要处理的事件名,作为事件处理程序的函数和一个布尔值。布尔值为true,表示在捕获阶段调用事件处理程序,如果是false,表示在冒泡阶段调用事件处理程序。这种方法可以添加多个事件处理程序,并且会按照它们添加的顺序触发。通过addEventListener添加的匿名函数无法通过removeEventListener移除。多数情况是将事件处理程序添加到事件流的冒泡阶段,可以最大限度兼容各种浏览器,只在需要在事件到达目标之前截获它的时候将事件处理程序添加到捕获阶段。

var btn = document.getElementById("myBtn")
btn.addEventListener("click", function(){
    alert(this.id)
}, false)

IE事件处理程序中定义了attachEvent()detachEvent()方法,接收相同的两个参数,事件处理程序名称与事件处理程序函数,事件会被添加到冒泡阶段。使用DOM0级方法的情况下,只支持一个事件处理程序,事件处理程序会在其所属元素的作用域运行,在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,此时this等于window

var btn = document.getElementById("myBtn")
btn.attachEvent("onclick", function(){
    alert("Clicked")
})

跨浏览器的事件处理程序能够处理浏览器之间的差异

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 {
            element["on" + type] = handler 
        }
    },
    removeHandler: function(element, type, handler){
        if(element.removeHandler){
            element.removeHandler(type, handler, false)
        } else if(element.detachEvent){
            element.detachEvent("on" + type, handler)
        } else {
            element["on" + type] = null
        }
    }
}

var btn = document.getElementById("myBtn")
var handler = function(){
    alert("Clicked")
}
EventUtil.addHandler(btn, "click", handler)
EventUtil.removeHandler(btn, "click", handler)
事件对象

在触发DOM上的某个事件时,会产生一个事件对象event ,这个对象中包含着所有与事件有关的信息。

属性/方法 说明
bubbles 是否冒泡
stopPropagation 取消事件的进一步捕获或冒泡
cancelable 是否可以取消事件的默认行为
preventDefault 取消事件默认行为
target 指向触发事件监听的对象
currentTarget 指向添加监听事件的对象
eventPhase 调用事件处理程序的阶段,1捕获 2目标 3冒泡

要阻止特定事件的默认行为,使用preventDefault()方法,只有cancelable属性设置为true的事件,才可以使用preventDefault()取消默认行为。如链接的默认行为就是在被单击时会导航到其href特性指定的URL,如果想阻止链接导航这一默认行为,通过链接的onclick事件处理程序可以取消它。

var link = document.getElementById("myLink")
link.onclick = function(event){
    event.preventDefault()
}

stopPropogation()方法用于立即停止在DOM事件中的传播,取消进一步的事件捕获或冒泡。直接添加到一个按钮的事件处理程序可以调用stopPropogation(),从而避免触发注册在document.body上面的事件处理程序。

var btn = document.getElementById("myBtn")
btn.onclick = function(event){
    alert("Clicked")
    event.stopPropagation()
}
document.body.onclick = function(event){
    alert("body clicked")
}

IE中的事件对象,使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在window.event

属性/方法 说明
cancelBubble 默认值为false,将其设置为true可以取消冒泡,相当于stopPropagation()
returnValue 默认值为true,将其设置为false可以取消事件的默认行为,相当于preventDefault()
cancelable 是否可以取消事件的默认行为
srcElement 事件的目标,相当于target

returnValue属性相当于DOM中的preventDefault()方法,作用是取消给定事件的默认行为。cancelBubble属性与DOM中的stopPropagation()方法作用相同,用来停止事件冒泡。由于IE不支持事件捕获,只能取消事件冒泡,但stopPropagation()可以同时取消事件捕获和冒泡。

var link = document.getElementById("myLink")
link.onclick = function(){
    window.event.returnValue = false
}

跨浏览器的事件对象

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 {
            element["on" + type] = handler
        }
    },
    getEvent: function(event){
        return event ? event : window.event
    },
    getTarget: function(event){
        event.target || event.srcElement
    },
    preventDefault: function(event){
        if(event.preventDefault){
            event.preventDefault()
        } else {
            event.returnValue = false
        }
    },
    removeHandler: function(element, type, handler){
        if(element,removeEventListener){
            element.removeEventListener(type, handler,false)
        } else if (element.detachEvent){
            element.detachEvent("on" + type, handler)
        } else {
            element["on" + type] = null
        }
    },
    stopPropagation: function(event){
        if(event.stopPropagation){
            event.stopPropagation()
        } else {
            event.cancellable = true
        }
    }
}

使用实例

var link = document.getElementById("myLink")
link.onclick = function(event){
    event = EventUtil.getEvent(event)
    var target = EventUtil.getTarget(event)
    EventUtil.preventDefault(event)
    EventUtil.stopPropagation(event)
}
事件类型

UI事件用户界面事件,用户与页面上的元素交互时触发

  • load 当页面完全加载后在window上触发
  • unload 当页面完全卸载后在window上触发,只要用户从一个页面切换到另一个页面,就会发生unload,利用这个事件可以清除引用,避免内存泄漏。
  • error 当发生JS错误时在window上触发
  • select 当用户选择文本框<input><texterea>中的一个或多个字符时触发
  • resize 当窗口或框架的大小变化时在window或框架上触发
  • scroll 当用户滚动带滚动条的元素中的内容时,在该元素上面触发,被添加的代码有可能被频繁执行。导致浏览器反应变慢

焦点事件会在页面元素获得或失去焦点时触发,利用这些事件与document.hasFocus()方法及document.activeElement属性配合,可以知晓用户在页面上的行踪

  • blur 元素失去焦点时触发,不会冒泡,所有浏览器都支持
  • focus 在元素获得焦点时触发,不会冒泡,所有浏览器都支持
  • focusin在元素获得焦点时触发,冒泡,支持的浏览器有IE5.5+,Safari5.1+,Opera11.5+和Chrome
  • focusout 在元素失去焦点时触发,冒泡,支持的浏览器有IE5.5+,Safari5.1+,Opera11.5+和Chrome

鼠标与滚轮事件DOM3级事件中定义的滚轮事件,页面上的所有元素都支持鼠标事件,除了mouseentermouseleave,所有鼠标事件都会冒泡,也可以被取消,取消鼠标事件将会影响浏览器的默认行为。clientXclientY表示事件发生时鼠标指针在视口中的水平和垂直坐标。pageXpageY表示鼠标光标在页面中的位置。在页面没有滚动的情况下,二者的值相等。screenXscreenY表示鼠标事件发生时鼠标指针相对于整个屏幕的坐标信息。offsetXoffsetY光标相对于目标元素边界的x坐标和y坐标。offsetLeftoffsetTop 元素的外边框至包含元素的内边框之间的距离。

  • click在用户单击鼠标或按下回车键时触发
  • dbclick 用户双击鼠标按钮时触发
  • mousedown 用户按下任意鼠标按钮时触发
  • mouseup 在用户释放鼠标按钮时触发
  • mouseenter 鼠标光标从元素外部首次移动到元素范围之内触发,不冒泡,在光标移动到后代元素上不会触发
  • mouseleave 位于元素上方的鼠标光标移动到元素范围之外时触发,不冒泡,在光标移动到后代元素上不会触发
  • mousemove 当鼠标指针在元素内部移动时重复触发
  • mouseout 在鼠标指针位于一个元素上方,用户将其移入另一个元素时触发
  • mouseover 鼠标指针位于一个元素外部,用户将其首次移入另一个元素边界之内时触发
  • mousewheel 鼠标滚轮事件

键盘与文本事件 用户在使用键盘时触发

  • keydown 用户按下键盘上的任意键触发
  • keypress 用户按下键盘上的字符键触发
  • keyup 用户释放键盘上的键时触发
  • textInput 用户在可编辑区域中输入字符时,就会触发这个事件

HTML5事件中得到浏览器完整支持的事件

  • contextmenu用以表示何时应该显示上下文菜单
  • beforeunload在浏览器卸载页面之前触发
  • DOMContentLoaded 在形成完整DOM树之后触发
  • readystatechange 提供与元素加载状态有关的信息
  • haschange 在URL参数、列表发生变化时通知开发人员
内存和性能

在 JavaScript 中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。在不需要的时候移除事件处理程序,如果带有事件处理程序的元素被innerHTML删除,那么原来添加到元素中的事件处理程序无法被当作垃圾回收,此时需要手工移除事件处理程序。在卸载页面时,通过onunload事件处理程序移除所有的事件处理程序。

var btn = document.getElementById("myBtn")
btn.onclick = function(){
    //先执行某些操作
    btn.onclick = null
    document.getElementById("myDiv").innerHTML = "processing"
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容