第13章,事件
事件冒泡
事件捕获:
HTML事件处理程序
<input type="button" value="Click Me" onclick="alert('Clicked')" />
单击,显示警告框。onclick特性的值不能使用未经转移的HTML语法字符:&,",<,>。若想使用双引号:
<input type="button" value="Click Me" onclick="alert("Clicked")" /> <!-- 输出 “click” --> <input type="button" value="Click Me" onclick="alert(event.type)" />
通过event变量,可以直接访问事件对象,且,在函数内部,this值等于事件的目标元素,例:
<!-- 输出 “Click Me” --> <input type="button" value="Click Me" onclick="alert(this.value)" />
还可以使用扩展作用域,在函数内部可以像访问局部对象一样访问document及该元素本身,so可以如下使用with扩展作用域:
function(){ with(document){ with(this){ //元素属性值 } } }
如此一来,事件处理程序访问属性就简单多了:
<!-- 输出 “Click Me” --> <input type="button" value="Click Me" onclick="alert(value)" />
可能是form表单输入元素,则作用域还会包含表单元素(父元素)的入口:
function(){ with(document){ with(this.form){ with(this){ //元素属性值 } } } }
实际上只是为了让事件处理程序无需引用表单元素就能访问其他表单字段:
<form> <input type="text" name="username" value=""> <input type="button" value="Echo Username" onclick="alert(username.value)"> </form>
<input type="button" value="Click Me" onclick="try{showMessage();}catch(ex){}" />
DOM0级事件处理程序
var btn = document.getElementById("myBtn"); btn.onclick = function(){ alert("Clicked"); };
使用DOM0级事件处理程序被认为是元素的方法,为此是在元素作用域中运行,即,程序中的this引用当前元素,例:
var btn = document.getElementById("myBtn"); btn.onclick = function(){ alert(this.id); //"myBtn" };
btn.onclick = null; //删除事件处理程序
DOM2级事件处理程序
定义两个方法用于处理指定和删除事件处理程序的操作:addEventListener(),removeEventListener(),所有DOM节点都包含这两个方法,且都接收3个参数:要处理的事件名,作为事件处理程序的函数,布尔值。布尔值为true,捕获阶段调用函数,false,冒泡阶段调用函数
var btn = document.getElementById("myBtn"); btn.addEventListener("click",function(){ alert(this.id); },false);
使用DOM2级好处:添加多个程序,触发顺序按添加顺序
var btn = document.getElementById("myBtn"); btn.addEventListener("click",function(){ alert(this.id); },false); btn.addEventListener("click",function(){ alert("hello"); },false);
通过addEventListener添加,只能通过removeEventListener删除,删除时传入参数与添加时参数相同,即若是添加时使用了匿名函数,则无法移除:
//无效的事件处理程序移除 var btn = document.getElementById("myBtn"); btn.addEventListener("click",function(){ alert(this.id); },false); //其他代码 btn.removeEventListener("click",function(){ //无效 alert(this.id); },false);
//有效的事件处理程序移除 var btn = document.getElementById("myBtn"); var handle = function(){ alert(this.id); } btn.addEventListener("click",handle,false); //其他代码 btn.removeEventListener("click",handle,false); //有效
IE事件处理程序
var btn = document.getElementById("myBtn"); btn.attachEvent("onclick",function(){ alert(this.id); });
跨浏览器的事件处理程序
//跨浏览器事件处理程序 var EventUtil = { addHandler:function(element,type,handler){ if(element.addEventListener){ element.addEventListener(type,handler,false); //DOM2级 }else if(element.attachEvent){ element.attachEvent("on" + type,handler); //兼容IE8及更早版本,加上“on”,IE方法 }else{ element["on" + type] = handler; //DOM0 } }, removeHandler:function(element,type,handler){ if(element.removeEventListener){ element.removeEventListener(type,handler,false);//DOM2级 }else if(element.detachEvent){ element.detachEvent("on" + type,handler) //兼容IE8及更早版本,加上“on”,IE方法 }else{ element["on" + type] = null; } } };
使用示例:
//使用示例 var btn = document.getElementById("myBtn"); var handler = function(){ alert("hello"); }; EventUtil.addHandler(btn,"click",handler); //其他代码 EventUtil.removeHandler(btn,"click",handler);
事件对象
属性/方法 | 类型 | 读/写 | 说明 |
bubbles | Boolean | 只读 | 是否冒泡 |
cancelable | Boolean | 只读 | 会否可取消默认行为 |
currentTarget | Element | 只读 | 当前处理的元素 |
defaultPrevented | Boolean | 只读 | true表示已调用preventDefault方法(DOM3新增) |
detail | Integer | 只读 | 与事件相关细节信息 |
eventPhase | Integer | 只读 | 1、捕获阶段,2、处于目标,3、冒泡阶段 |
preventDefault | Function | 只读 | 取消时间默认行为,cancelable为true可使用 |
stopImmediatePropagation | Function | 只读 | 取消事件的进一步捕获或冒泡,并阻止事件处理程序调用(DOM3新增) |
stopPropagation | Function | 只读 | 取消事件进一步捕获或冒泡,若bubbles为true,可使用此方法 |
target | Element | 只读 | 事件目标 |
trusted | Boolean | 只读 | true事件由浏览器生成,false事件由js生成(DOM3新增) |
type | String | 只读 | 被触发事件类型 |
view | AbstractView | 只读 | 与事件关联的抽象视图,等同于发生事件的window对象 |
在事件处理程序内部,this始终等于currentTarget,target只包含事件实际目标(个人理解:就是事件在哪个元素产生,就是那个元素),若直接将事件处理程序指定给目标元素,则三者相同值,例:
var btn = document.getElementById("myBtn"); btn.onclick = function(event){ alert(event.currentTarget === this); //true alert(event.target === this); //true }
若事件在按钮父节点,则不一样:
document.body.onclick = function(event){ alert(event.currentTarget === document.body); //true alert(this === document.body); //true alert(event.target === document.getElementById("myBtn")); //true }
在需要一个函数处理多个事件时,可以使用type属性:
//一个函数处理多个事件 var btn = document.getElementById("myBtn"); var handler = function(event){ switch(event.type){ case "click": alert("click"); break; case "mouseover": alert("mouseover"); break; case "mouseout": alert("mouseout"); break; } }; btn.onclick = handler; btn.onmouseover = handler; btn.onmouseout = handler;
IE中的事件对象
var btn = document.getElementById("myBtn"); btn.onclick = function(){ var event = window.event; alert(event.type); //"click" };
事件处理程序使用attachEvent添加,那么会有个event对象传入:
var btn = document.getElementById("myBtn"); btn.attachEvent("onclick",function(event){ alert(event.type); //"click" });
若通过HTML指定事件处理程序还可以通过一个名叫event的变量访问event对象,例:
<input type="button" value="Click Me" onclick="alert(event.type)">
属性/方法 | 类型 | 读/写 | 说明 |
cancelBubble | Boolean | 读/写 | 默认false,设为true,取消事件冒泡 |
returnValue | Boolean | 读/写 | 默认true,设为false可以取消事件默认行为,与DOM的preventDefault方法一样 |
serElement | Element | 只读 | 事件目标,与DOM的target相同 |
type | String | 只读 | 被触发的事件类型 |
因为事件处理程序的作用域是根据指定它的方式来确定的,所以不能认为this会始终等于事件目标,为此,最好使用event.serElement比较保险,例:
var btn = document.getElementById("myBtn"); btn.onclick = function(){ alert(window.event.srcElement === this); //true }; btn.attachEvent("onclick",function(event){ alert(event.srcElement === this); //false });
跨浏览器事件对象:
//跨浏览器事件对象 var EventUtil = { addHandler:function(element,type,handler){ if(element.addEventListener){ element.addEventListener(type,handler,false); //DOM2级 }else if(element.attachEvent){ element.attachEvent("on" + type,handler); //兼容IE8及更早版本,加上“on”,IE方法 }else{ element["on" + type] = handler; //DOM0 } }, getEvent:function(event){ //返回对event对象的引用 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; } }, removeHandler:function(element,type,handler){ if(element.removeEventListener){ element.removeEventListener(type,handler,false);//DOM2级 }else if(element.detachEvent){ element.detachEvent("on" + type,handler) //兼容IE8及更早版本,加上“on”,IE方法 }else{ element["on" + type] = null; } }, stopPropagation:function(event){ if(event.stopPropagation){ event.stopPropagation(); }else{ event.cancelBubble = true; } } }; //使用示例 btn.onclick = function(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); }; var link = document.getElementById("myLink"); link.onclick = function(event){ //可确保在所有浏览器中单击该链接都不会打开另一个页面 event = EventUtil.getEvent(event); EventUtil.preventDefault(event); }; //由于IE不支持事件捕获,所以只能用来阻止事件冒泡 var btn = document.getElementById("myBtn"); btn.onclick = function(event){ alert("Clicked"); event = EventUtil.getEvent(event); EventUtil.stopPropagation(event); }; document.body.onclick = function(event){ alert("body clicked"); };
事件类型
UI事件
//浏览器是否支持DOM3级事件定义的事件 var isSupported = document.implementation.hasFeature("UIEvent","3.0");
1、load事件
EventUtil.addHandler(window,"load",function(event){ alert("Loaded"); });
第二种指定onload事件处理程序方式是为<body>添加onload属性:
<!DOCTYPE html> <html> <head> <title>Load Event Example</title> </head> <body onload="alert('Loaded!')"> </body> </html>
EventUtil.addHandler(window,"load",function(){ var script = document.createElement("script"); EventUtil.addHandler(script,"load",function(event){ alert("Loaded"); }); script.src = "example.js"; document.body.appendChild(script); });
2、unload事件
3、resize事件
4、scroll事件
焦点事件
//确定浏览器是否支持焦点事件 var isSUpported = document.implementation.hasFeature("FocusEvent","3.0");
鼠标与滚轮事件
//确定浏览器是否支持DOM2级鼠标事件 var isSUpported = document.implementation.hasFeature("MouseEvents","2.0"); //确定浏览器是否支持所有鼠标事件 var isSUpported = document.implementation.hasFeature("MouseEvent","3.0"); //差了个s
1、客户区坐标位置
2、页面坐标位置
var div = document.getElementById("myDiv"); EventUtil.addHandler(div,"click",function(event){ event = EventUtil.getEvent(event); var pageX = event.pageX; pageY = event.pageY; if(pageX == undefined){ pageX = event.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft); } if(pageY == undefined){ pageY = event.clientY + (document.body.scrollTop || document.documentElement.scrollTop); } alert("Page coordinates: " + pageX + "," + pageY); });
3、屏幕坐标位置
4、修改键
5、相关元素
var EventUtil = { //其他代码 getRelatedTarget:function(event){ if(event.relatedTarget){ return event.relatedTarget; }else if(event.toElement){ return event.toElement; }else if(event.fromElement){ return event.fromElement; }else { return null; } }, //其他代码 };
6、鼠标按钮
var EventUtil = { //其他代码 getButton:function(event){ if(document.implementation.hasFeature("MouseEvents","2.0")){ //检测MouseEvents特性可以知道event对象存在的button属性是否包含正确的值,失败,说明是IE return event.button; }else{ switch(event.button){ case 0: case 1: case 3: case 5: case 7: return 0; case 2: case 6: return 2; case 4: return 1; } } }, //其他代码 }
7、更多的事件信息
8、鼠标滚轮事件
var EventUtil = { //其他代码 getWheelDelta:function(event){ if(event.wheelDelta){ return (client.engine.opera && client.engine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta); }else{ return -event.detail * 40; } }, //其他代码 }
9、触摸设备
10、无障碍性问题
键盘与文本事件
3个键盘事件:
1、键码
2、字符编码
var EventUtil = { //其他代码 getCharCode:function(event){ if(typeof event.charCode == "number"){ return event.charCode; }else{ return event.keyCode; } }, //其他代码 }
3、DOM3级变化
4、textInput事件
var textbox = document.getElementById("myText"); EventUtil.addHandler(textbox,"textInput",function(event){ event = EventUtil.getEvent(event); alert(event.data); });
5、设备中的键盘事件
复合事件
变动事件
//检查浏览器是否支持变动事件 var isSUpported = document.implementation.hasFeature("MutationEvents","2.0");
1、删除节点
<!DOCTYPE html> <html> <head> <title>Node Removal Events Example</title> </head> <body> <ul id="myList"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> </body> </html>
2、插入节点
HTML5事件
1、contexmenu事件
2、beforeunload事件
3、DOMContentLoaded事件
4、readystatechange事件
5、pageshow和pagehide事件
6、hashchange事件
设备事件
1、orientationchange事件
2、MozOrientation事件
3、deviceorientation事件
4、devicemotion事件
触摸与手势事件
1、触摸事件
2、手势事件
内存和性能
事件委托
<ul id="myLinks"> <li id="goSomewhere">Go somewhere</li> <li id="doSomething">Do something</li> <li id="sayHi">say hi</li> </ul>
包含3个单击后会执行操作的列表项,按之前的做法,需要每个都添加事件处理程序。若在复杂的web程序中如此做,将会有数不清的代码用于事件处理程序。使用事件委托,只需在DOM树中尽量高的层次添加一个事件处理程序,例:
var list = document.getElementById("myLinks"); EventUtil.addHandler(list,"click",function(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); switch(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; } });
移除事件处理程序
<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>
模拟事件
DOM中的事件模拟
1、模拟鼠标事件
2、模拟键盘事件
3、模拟其他事件
4、自定义DOM事件
IE中的事件模拟
第14章,表单脚本
表单基础知识
var form = document.getElementById("form1");
2、通过document.forms获取所有表单元素,通过数值索引或name值来取得特定表单
var firstForm = document.forms[0]; //取得页面中的第一个表单 var myForm = document.forms["form2"]; //取得页面中name值为“form2“的表单
提交表单
<!-- 通用提交按钮 --> <input type="submit" value="Submit Form"> <!-- 自定义提交按钮 --> <button type="submit">Submit Form</button> <!-- 图像按钮 --> <input type="image" src="graphic.gif">
var form = document.getElementById("myForm"); //提交表单 form.submit();
重置表单
<!-- 通用重置按钮 --> <input type="reset" value="Reset Form"> <!-- 自定义重置按钮 --> <button type="reset">Reset Form</button>
重置按钮也可以通过js,例:
var form = document.getElementById("myForm"); //重置表单 form.reset();
表单字段
1、共有的表单字段属性
2、共有的表单字段方法
3、共有的表单字段事件
文本框脚本
选择文本
1、选择(select)事件
var textbox = document.forms[0].elements["textbox1"]; EventUtil.addHandler(textbox,"select",function(event){ var alert("Text selected" + textbox.value); });
2、取得选择文本
3、选择部分文本
过滤输入
1、屏蔽字符
2、操作剪切板
var EventUtil = { //其他代码 getCliboardText:function(event){ var clipboardData = (event.clipboardData || window.clipboardData); return clipboardData.getData("text"); }, setCliboardText:function(event,value){ if(event.clipboardData){ return event.clipboardData.setData("text/plain",value); }else if(window.clipboardData){ return window.clipboardData.setData("text",value); } }, //其他代码 };
自动切换焦点
<input type="text" name="tel1" id="txtTel1" maxlength="3"> <input type="text" name="tel2" id="txtTel2" maxlength="3"> <input type="text" name="tel3" id="txtTel3" maxlength="4">
自动切换焦点功能实现:
(function(){ function tabForward(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); if(target.value.length == target.maxLength){ var form = target.form; for(var i=0,len=form.elements.length;i < len;i++){ if(form.elements[i] == target){ if(form.elements[i+1]){ form.elements[i+1].focus(); } return ; } } } } var textbox1 = document.getElementById("textTel1"); var textbox2 = document.getElementById("textTel2"); var textbox3 = document.getElementById("textTel3"); EventUtil.addHandler(textbox1,"keyup",tabForward); EventUtil.addHandler(textbox2,"keyup",tabForward); EventUtil.addHandler(textbox3,"keyup",tabForward); })();
HTML5约束验证API
1、必填字段
<input type="text" name="username" required>