事件

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

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

推荐阅读更多精彩内容