Js以事件驱动来实现界面交互。事件驱动的核心:以消息为基础,以事件来驱动。通俗地说,事件就是文档或浏览器窗口中发生的一些特定
交互行为,如加载、单击、输入、选择等。
1.1事件基础
Js与HTML之间交互就是通过事件实现的,事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。事件在浏览器中是以对象的形式存在的,即event,触发一个事件,就会产生一个事件对象event,该对象包含着所有与事件有关的信息,包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。
1.1.1事件模型
在浏览器的发展历史中。先后出现了以下4种事件处理模型:
- 基本事件模型:也称为DOM0级事件模型,是浏览器发展初期出现的一种比较简单的事件模型,主要通过事件属性;为指定标签绑定事件处理函数。
优点:
由于这种模型应用比较广泛,获得了所有浏览器的支持,目前依然比较流行。
缺点:
但是这种模型对HTML文档标签的依赖较为严重,不利于Js独立开发。
2. DOM事件模型:有W3C制定,是目前标准的事件处理模型。所有符合标准的浏览器都支持该事件模型。DOM事件模型包括DOM2事件模型和DOM3事件模型,
DOM3事件模型为DOM2事件模型的升级版,略有完善,主要是新增了一些事情类型,以适应移动设备的开发需要,但大部分规范和用法保持一致。
3. IE事件模型:IE4.0及其以上版本浏览器支持,与DOM事件模型相似,但用法不同。
4. Netscape事件模型:由Netscape4浏览器实现,在Netscape6中停止支持。
1.1.2事件流
事件流就是多个节点对象对同一种事件进行响应的先后顺序。主要包括3种类型。
1.冒泡型
从最特定的目标向最不特定的目标依次触发事件,也就是事件从下向上进行响应,这个传递过程被形象地称为冒泡。
下面用法:
function bubble() { var div = document.getElementsByTagName('div'); var show = document.getElementById('show'); for (var i = 0; i < div.length; i++) { div[i].onclick = (function (i) { return function () { div[i].style.border = '1px dashed orange'; show.innerHTML += div[i].className + '>'; } })(i); } } window.onload = bubble;
2.捕获型
事件从最不特定的目标开始被触发,最后到最特定的目标,也就是事件从上向下进行响应。
下面用法:
for (var i = 0; i < div.length; i++) { div[i]. /*onclick*/ addEventListener('click', (function (i) { return function () { div[i].style.border = '1px dashed orange'; show.innerHTML += div[i].className + '>'; } })(i), true); }
3.混合型
W3C的DOM事件模型支持捕获型和冒泡型两种事件流,但是捕获型事件流先发生,然后才发生冒泡型事件流。两种事件流会触及DOM中的所
有层级对象,从document对象开始,最后返回document对象结束。
根据事件流类型。
以下可以把事件传播的整个过程分为3个阶段:
1.1.3事件类型
根据触发对象不同,可以将浏览器中发生的事件分成不同的类型。
DOM0级事件定义了以下事件类型:
① 鼠标事件:与鼠标操作相关的各种行为。可以细分为两类:
② 键盘事件:与键盘操作相关的各种行为,包括追踪键盘敲击及其上下文。
追踪键盘包括3种类型:
keyup
keydown
keypress
③ 页面事件:关于页面本身的行为。例如,当首次载入页面时触发load事件和离开页面时触发unload和beforeunload事件。
④ UI事件:追踪用户在页面中的各种行为。例如,监听用户在表单中的输入,可以通过focus和blur两个事件来实现。
DOM2事件模型中,事件模型包含4个子模块,每个子模块提供对某类事件的支持。
‘默认动作’列定义了事件类型是否支持preventDefault()方法,取消事件的默认动作。
DOM3事件模型新增了一些新事件。
DOM3事件类型简单说明如下:
1.1.4绑定事件
在基本事件模型中,Js支持两种绑定方式。
1. 静态绑定
把Js脚本作为属性值,直接赋予事件属性。
下面用法:
<button onclick="document.write('我是程序员!');">按钮</button>
2. 动态绑定
使用DOM对象的事件属性进行赋值。
下面用法:
var button = document.getElementById('btn'); button.onclick = function () { document.write('我是程序员!'); }
这种方法可以在脚本中直接为页面元素附加事件,不用破坏HTML结构,比上一种灵活。
1.1.5事件处理函数
事件处理函数是一类特殊的函数,其结构与函数直接量相同,主要任务是实现事件处理。使用方法是异步调用,由事件触发进行响应。
事件处理函数一般没有明确的返回值。不过在特定事件中,用户可以利用事件处理函数的返回值影响程序的执行。
下面用法:
function btn1(event) { event = event || window.event; var btn11 = event.srcElement ? event.srcElement : Event.target; btn11.style.background = 'green'; }
function btn2(event) { event = event || window.event; var src = event.srcElement ? event.srcElement : Event.target; src.style.background = 'skyblue'; }
1.1.6注册事件
在DOM事件模型中,通过调用对象的addEventListener()方法注册事件。
语法:
element.addEventListener(type, listener, useCapture);
参数:
type(表示监听事件类型的字符串。)
listener(当所监听的事件类型触发时,会接收到一个事件通知(实现了 Event 接口的对象)对象。
listener 必须是一个实现了 EventListener 接口的对象,或者是一个函数。)
useCapture(Boolean,在DOM树中,注册了listener的元素,是否要先于它下面的EventTarget,调用该listener。
当useCapture(设为true) 时,沿着DOM树向上冒泡的事件,不会触发listener。
当一个元素嵌套了另一个元素,并且两个元素都对同一事件注册了一个处理函数时,所发生的事件冒泡和事件捕获是两种不同的事件传播方式。
事件传播模式决定了元素以哪个顺序接收事件。如果没有指定,useCapture默认为 false 。)
下面用法:
var p1 = document.getElementById('p1'); p1.addEventListener('mouseover', function () { p1.style.background = 'green'; }, true); p1.addEventListener('mouseout', function () { p1.style.background = 'red'; }, true);
1.1.7销毁事件
在DOM事件模型中,使用removeEventListener()方法可以从指定对象中删除已经注册的事件处理函数。
用法:
element.removeEventListener(event, function, useCapture)
参数:
可能值:
true - 在捕获阶段移除事件句柄。
false- 默认。在冒泡阶段移除事件句柄。
注意: 如果添加两次事件句柄,一次在捕获阶段,一次在冒泡阶段,你必须单独移除该事件。
下面用法:
var span1 = document.getElementById('span1'); var f1 = function () { span1.style.background = 'pink'; }; var f2 = function () { span1.style.background = 'skyblue'; span1.removeEventListener('mouseover', f1); span1.removeEventListener('mouseout', f2); }; span1.addEventListener('mouseover', f1); span1.addEventListener('mouseout', f2);
1.1.8事件委托
Js事件委托就是利用冒泡的原理,把本应该添加到某个元素上的事件委托给他的父级,从而减少DOM交互达到网页优化。
这样做的好处:
真正把HTML和Js分离,也能防止在动态添加或删除节点的过程中注册的事件丢失。
下面用法:
var ul = document.getElementById('list'); ul.addEventListener('click', function (e) { var e = e || window.event; var target = e.target || e.srcElement; if (e.target && e.target.nodeName.toUpperCase() == 'LI') { document.write(e.target.innerHTML); } }, true);
var i = 4; var btn = document.getElementById('btn'); btn.addEventListener('click', function () { var li = document.createElement('li'); li.innerHTML = '我是程序员' + i++; ul.appendChild(li); });
1.2使用鼠标事件
鼠标事件是Web开发中最常用的事件类型。
1.2.1鼠标点击
鼠标点击事件包括4个:
其中click事件类型最为常用,而mousedown和mouseup事件类型多用于在鼠标拖放、拉伸操作中。当这些事件处理函数的返回值为false时,
则会禁止绑定对象的默认行为。
下面用法:
var a = document.getElementsByTagName('a'); for (var i = 0; i < a.length; i++) { if ((new RegExp(window.location.href)).test(a[i].href)) { a[i].onclick = function () { return false; } } }
1.2.2鼠标移动
mousemove事件类型是一个实时响应的事件,当鼠标指针的位置发生变化时(至少移动一个像素),就会触发mousemove事件。
该事件响应的灵敏度主要参考鼠标的指针的移动速度的快慢,以及浏览器跟新的速度。
下面用法:
var box = document.getElementById('box'); box.style.position = 'absolute'; box.style.width = '200px'; box.style.height = '200px'; box.style.background = 'green'; var mx, my, ox, oy; function e(event) { if (!event) { event = window.event; event.target = event.srcElement; event.layerX = event.offsetX; event.layerY = event.offsetY; } event.mx = event.pageX || event.clientX + document.body.scrollLeft; event.my = event.pageY || event.clientY + document.body.scrollTop; return event; } document.onmousedown = function (event) { event = e(event); o = event.target; ox = parseInt(o.offsetLeft); oy = parseInt(o.offsetTop); mx = event.mx; my = event.my; document.onmousemove = move; document.onmouseup = stop; } function move(event) { event = e(event); o.style.left = ox + event.mx - mx + 'px'; o.style.Top = oy + event.my - my + 'px'; } function stop(event) { event = e(event); ox = parseInt(o.offsetLeft); oy = parseInt(o.offsetTop); mx = event.mx; my = event.my; o = document.onmousemove = document.onmouseup = null; }
1.2.3鼠标经过
鼠标经过包括下面两种事件类型:
如果从父元素中移到子元素中时,也会触发父元素的mouseover事件类型。
下面用法:
var div = document.getElementsByTagName('div'); for (var i = 0; i < div.length; i++) { div[i].onmousemove = function (e) { this.style.color = 'orange'; this.style.fontSize = '24px'; this.style.fontWeight = '600'; } div[i].onmouseout = function () { this.style.color = 'red'; } }
1.3使用键盘事件
当用户操作键盘时会触发键盘事件。
键盘事件主要包括下面3种类型:
1.3.1键盘事件属性
键盘事件定义了很多属性,利用这些属性可以精确控制键盘操作。键盘事件属性一般只在键盘相关事件发生时才会存在于事件对象中。
下面用法:
var box = document.getElementById('box'); box.style.position = 'absolute'; box.style.width = '100px'; box.style.height = '100px'; box.style.background = 'green'; document.keyDown = keyDown; function keyDown(event) { var event = event || window.event; switch (event.keyCode) { case 37: box.style.left = box.offsetLeft - 5 + 'px'; break; case 38: box.style.left = box.offsetLeft + 5 + 'px'; break; case 39: box.style.top = box.offsetTop - 5 + 'px'; break; case 40: box.style.top = box.offsetTop + 5 + 'px'; break; } return false; }
1.4使用页面事件
所有页面事件都明确地处理整个页面的函数和状态。
1.4.1窗口重置
resize事件类型是在浏览器窗口被重置时触发的,当用户调整窗口大小,或者最大化,最小化、恢复窗口大小显示时均可触发resize事件。
利用该时间可以根据窗口大小的变化以便动态调整页面元素的显示大小。
下面用法:
var box = document.getElementById("box"); box.style.position = "absolute"; box.style.backgroundColor = "red"; box.style.width = w() * 0.8 + "px"; box.style.height = h() * 0.8 + "px"; window.onresize = function () { box.style.width = w() * 0.8 + "px"; box.style.height = h() * 0.8 + "px"; }
function w() { if (window.innerWidth) { //兼容DOM return window.innerWidth; } else if (document.body) { return document.body.clientWidth; //兼容IE } }
function h() { if (window.innerHeight) { return window.innerHeight; } else if (document.body) { return document.body.clientHeight; } }
1.4.2页面滚动
scroll事件类型是在元素滚动条在滚动时触发。
下面用法:
var box = document.getElementById("box"); box.style.position = 'absolute'; box.style.width = '100px'; box.style.height = '100px'; box.style.background = 'green'; window.onload = f; window.onscroll = f; function f() { box.style.left = 100 + parseInt(document.body.scrollLeft) + 'px'; box.style.top = 100 + parseInt(document.body.scrollTop) + 'px'; }
1.5使用UI事件
UI事件负责响应用户与页面元素的交互。
1.5.1焦点处理
焦点处理主要包括以下两种事件类型:
所谓获得焦点;就是激活表单字段,使其可以响应键盘事件。
所谓失去焦点:就是指定元素当失去焦点就会触发此事件。(通常应用于表单元素。)
下面用法:
var form = document.getElementById('myform'); var field = form.elements['name']; window.onload = function () { field.focus(); field.blur(); }
1.5.2选择文本
当在文本框或文本区域内选择文本时,将触发select事件。
下面用法:
var a = document.getElementsByTagName('input')[0]; var b = document.getElementsByTagName('input')[1]; a.onselect = function () { if (document.selection) { o = document.selection.createRange(); if (o.text.length > 0) b.value = o.text; } else { p1 = a.selectionStart; p2 = a.selectionEnd; b.value = a.value.substring(p1, p2); } }
1.5.3字符串变化检测
change事件类型是在表单元素的值发生变化时触发,它主要用于input、select、和textarea元素。
下面用法:
var a = document.getElementsByTagName('select')[0]; a.onchange = function () { window.open(this.value, ''); }
1.5.4提交表单
使用<input>或<button>标签都可以定义提交按钮。
下面用法:
var t = document.getElementsByTagName('input')[0]; var f = document.getElementsByTagName('form')[0]; f.onsubmit = function (e) { document.write(t.value); }
1.5.5重置表单
当单击重置按钮时,表单被重置,所有表单字段恢复初始值。这时会触发resct事件。
下面用法:
var t = document.getElementsByTagName('input')[0]; var f = document.getElementsByTagName('form')[0]; f.onreset = function (e) { document.write(t.value); }
1.5.6剪贴板数据
HTML5规范了剪贴板数据操作。
主要包括6个剪贴板事件:
至于copy、cut和paste事件,只要是在上下文菜单中选择了相应选项等,所有浏览器都会触发它们。
下面用法:
var form = document.getElementById('myform'); var field1 = form.elements[0]; var getClipboardText = function (event) { var clipboardData = (event.clipboardData || window.clipboardData); return clipboardData.getData('text'); };
var setClipboardText = function (event, value) { if (event.clipboardData) { event.clipboardData.setData('text/plain', value); } else if (window.clipboardData) { window.clipboardData.setData('text', value); } };
var addHandler = function (element, type, handler) { if (document.addEventListener) { document.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent('on' + type, handler); } else { element['on' + type] = handler; } };
addHandler(field1, 'paste', function (event) { event = event || window.event; var text = getClipboardText(event); if (!/^\d*$/.text(text)) { if (event.preventDefault) { event.preventDefault(); } else { event.returnValue = false; } } })
1.6 实战案例
1.6.1 设计弹出对话框
无论从事Web前端开发,还是从事Vue前端开发,事件都是经常用到的。
下面用法:
function Dialog(id) { this.id = id; var that = this; document.getElementById(id).children[0].onclick = function () { that.close(); } }
Dialog.prototype.show = function () { var dlg = document.getElementById(this.id); dlg.style.display = 'block'; dlg = null; }
Dialog.prototype.close = function () { var dlg = document.getElementById(this.id); dlg.style.display = 'none'; dlg = null; }
function openDialog() { var dlg = new Dialog('dlgText'); dlg.show(); }