写在前面:
这篇是《js高程》13.4-13.6和14.1-14.4的笔记,记在网上也是方便自己以后随时随地可以回看。
13章前面的内容在 JS冒泡与捕获 那篇单独拎出来整理了。
其他详见代码注释 : )
PS :前面的EventUtil具体怎么来的可以看冒泡和捕获那篇↑
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){
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);
}else(element.detachEvent){
element.detachEvent('on'+type,handler);
}else{
element['on'+type]=null;
}
},
stopPropagation:function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble=true;
}
}
};
【事件类型】
// UI事件-load\unload
var isSupport=document.implementation.hasFeature('HTMLEvents','2.0');
//两种指定方式
//一:js,推荐
//这里传入的event对象不包含有关这个事件的任何附加信息,
//但在兼容DOM的浏览器中event.target被设为document,
//而IE并不会为这个事件设置srcElement属性
EventUtil.addHandler(window,'load',function(event){
alert('Loaded!');
});
//二:向后兼容 <body onload="alert('...')">
//在图像上触发load时,要先指定事件再指定src,因为图像在指定src属性后就会开始下载
EventUtil.addHandler(window,'load',function(){
//先向window添加load,因为要在dom中添加新元素,首先要确保文档加载完毕
var image=document.createElement('img');
EventUtil.addHandler(image,'load',function(event){
event=EventUtil.getEvent(event);
alert(EventUtil.getTarget(event).src);
});
document.body.appendChild(image);
image.src='...';
});
//注:在不属于DOM文档的图像上触发load,IE8及以前版本不会生产event对象
//IE9+、Firefox、Opera、Chrome、Safari3+中,<script>也可触发load
//不同于img,只有在设置了<script>的src属性并将添加到文档后,才会开始下载js文件
//所以指定事件处理程序和src属性的先后顺序就不重要
//IE和Opera支持<link>上的load,未指定href并添加到文档之前也不会下载样式表
EventUtil.addHandler(window,'unload',function(event){
alert('Unloaded!');
});
//生成的event对象在兼容dom的浏览器中只包含target,IE8及以前提供了srcElement
//鼠标与滚轮事件
//dom通过event的relatedTarget属性提供相关元素信息,
//这个属性只对mouseover、mouseout才包含值,对其他事件来说为null,
//IE8及以前不支持relatedTarget,但在mouseover中有fromElement,在mouseout中有toElement
//添加取得相关元素方法:
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;
}
},
//...
};
var div=document.getElementById('myDiv');
EventUtil.addHandler(div,'mouseout',function(event){
event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);
var relatedTarget=EventUtil.getRelatedTarget(event);
alert('moused out of'+target.tagName+'to'+relatedTarget.tagName);
});
//鼠标按钮
//对mouseup和mousedown,event有一个button属性:
//值为0表示左键,1滚轮,2右键
//IE8及以前:
//0没有按,1左键,2右键,3左右,4滚轮,5左键滚轮,6右键滚轮,7左右滚轮
//Opera中不是操作左键不会触发mouseup和mousedown
var EventUtil={
//...
getButton:function(event){
if(document.implementation.hasFeature('MouseEvents','2.0')){
return event.button;
}else{
switch(event.button){
case 0:
case 1:
case 3:
case 5:
case 7:
return 0;//01357都返回0
case 2:
case 6:
return 2;//26都返回2
case 4:
return 1;//4返回1
}
}
},
//...
};
var div=document.getElementById('myDiv');
EventUtil.addHandler(div,'mousedown',function(event){
event=EventUtil.getEvent(event);
alert(EventUtil.getButton(event));
});
//鼠标滚轮事件
//mousewheel事件对应的event有一个wheelDelta属性,向前滚动是值为120的倍数,向后-120的倍数
//Opera9.5以前正负相反
//Firefox支持DOMMouseScroll事件,detail属性,向前是-3的倍数,向后3的倍数
//键盘与文本事件
//IE9、Firefox、Chrome、Safari的event支持一个charCode属性,
//只有在发生keypress时才包含值,保存按键代表字符ASCII码,
//keyCode通常为0或按键键码;
//IE8及以前和Opera则在keyCode中保存ASCII码;
var EventUtil={
//...
getCharCode:function(event){
if(typeof event.charCode=='number'){
//先检查charCode是否包含数值,在不支持这个属性的浏览器中值为undefined
return event.charCode;
}else{
return event.keyCode;
}
},
//...
};
var textbox=document.getElementById('myText');
EventUtil.addHandler(textbox,'keypress',function(event){
event=EventUtil.getEvent(event);
var ch=EventUtil.getCharCode(event);
alert(ch);
alert(String.fromCharCode(ch));
//取得字符编码后可用String.fromCharCode()将其转换成实际字符
});
//HTML5事件
//contextmenu事件,用以表示合适应该显示上下文菜单,冒泡,属于鼠标事件
//IE、Firefox、Safari、Chrome、Opera 11+
EventUtil.addHandler(window,'load',function(event){
var div=document.getElementById("myDiv");
EventUtil.addHandler(div,'contextmenu',function(event){
event=EventUtil.getEvent(event);
EventUtil.preventDefault(event);//取消浏览器默认菜单
var menu=document.getElementById('myMenu');
menu.style.left=event.clientX+'px';
menu.style.top=event.clientY+'px';//确定放置菜单(<ul>)的位置
menu.style.visibility='visible';//显示菜单
});
EventUtil.addHandler(document,'click',function(event){
document.getElementById('myMenu').style.visibility='hidden';
});//单词鼠标隐藏菜单
});
//beforeunload事件,在页面卸载操作之前阻止卸载操作
//IE、Safari、Firefox、Chrome支持,但Opera11及以前不支持
EventUtil.addHandler(window,'beforeunload',function(event){
event=EventUtil.getEvent(event);
var message="Sure to navigate away from this page?";
event.returnValue=message;//IE、Firefox
return message;//Safari、Chrome
});
【内存与性能】
//事件委托,是对"事件处理程序过多"的解决方案,利用冒泡,只指定一个事件处理程序,管理某一类型所以事件。
//最适合采用事件委托的事件有:click、mousedown、mouseup、keydown、keyup、keypress
//如果可行,也可在document对象上添加一个事件处理程序,用以处理某种特定类型的事件
/*
<ul id="myLinks">
<li id="goSomewhere">Go somewhere</li>
<li id="doSomething">Do something</li>
<li id="sayHi">Say hi</li>
</ul>
*/
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;
}
});
//移除事件处理程序
//内存中留有那些过时不用的"空事件处理程序"的两种原因:
//1、从文档中移除带事件处理程序的元素,如纯粹DOM操作:removeChild()、replaceChild(),或用innerHTML替换
btn.onclick=function(){
btn.onclick=null;//先移除事件处理程序再替换
document.getElementById("myDiv").innerHTML="...";
};
//注:在事件处理程序中删除按钮也能阻止事件冒泡,元素在文档中是冒泡的前提
//2、卸载页面时(可能是在两个页面来回切换或刷新),内存中滞留的对象数目会增加
// 最好的做法是,卸载之前先通过onunload事件处理程序移除所有事件处理程序
//可用js在任意时刻触发特定事件,在测试时非常有用
//DOM中的事件模拟
//在document对象上使用createEvent()创建event对象,接收一个参数,表示要创建事件类型的字符串
//如:UIEvents\MouseEvents\MutationEvents\HTMLEvents
//调用dispatchEvent()触发事件,接收一个参数,表示要触发事件的event对象,这样触发的事件照样冒泡
//模拟鼠标事件
//创建的对象有一个initMouseEvent()方法,用于指定与该鼠标事件有关的信息,接收15个参数,前5个为:
//type要触发的事件类型、bubbles是否冒泡、cancelable是否可以取消、view与事件关联的视图、detail与事件有关的详细信息
var btn=document.getElementById("myBtn");
var event=document.createEvent("MouseEvents");
//初始化对象
event.initMouseEvent("click",true,true,document.defaultView,0,0,0,0,0,false,false,false,false,0,null);
btn.dispatchEvent(event);
//模拟键盘事件
var textbox=document.getElementById("myTextbox"),event;
//以DOM3级方式创建事件对象
if(document.implementation.hasFeature("KeyboardEvents","3.0")){
event=document.createEvent("KeyboardEvent");
event.initKeyboardEvent("keydown",true,true,document.defaultView,"a",0,"Shift",0);
//'a' 按下的键码-按下了哪里的键,0是主键盘-空调图-在一行中按了多少次这个键,0
}
textbox.dispatchEvent(event);
//在Firefox中
var textbox=document.getElementById("myTextbox"),event;
event=document.createEvent("KeyEvents");
event.initKeyEvent('keypress',true,true,document.defaultView,false,false,false,false,65,65);
//ctrl-alt-shift-meta-keyCode键码-charCode ASCII码
textbox.dispatchEvent(event);
//其他浏览器中,创建一个通用的事件
var textbox=document.getElementById("myTextbox"),event;
event=document.createEvent("Events");
event.initEvent(type,bubbles,cancelable);
event.view=document.defaultView;
event.altKey=false;
event.ctrlKey=false;
event.shiftKey=false;
event.metaKey=false;
event.keyCode=65;
event.charCode=65;
textbox.dispatchEvent(event);
//像这样模拟事件虽然会触发键盘事件,但却不会向文本框中写入文本,这是由于无法精确模拟键盘事件造成的
//DOM3还定义了自定义事件,调用createEvent("CustomEvent"),
//返回的对象有一个initCustomEvent()方法,接收四个参数:
//type、bubbles、cancelable、detail
//IE9+、Firefox6+
var div=document.getElementById('myDiv'),event;
EventUtil.addHandler(div,'myevent',function(event){
alert('DIV:'+event.detail);
});
EventUtil.addHandler(document,"myevent",function(event){
alert('DOCUMENT:'+event.detail);
});
if(document,implementation.hasFeature("CustomEvents","3.0")){
event=document.createEvent("CustomEvent");
event.initCustomEvent("myevent",true,false,"hello world!");
div.dispatchEvent(event);
}
//IE中的事件模拟
//调用document.createEventObject(),无参数,返回一个通用event对象,要手动添加必要信息,
//在目标上调用fireEvent()方法,接收两个参数:事件处理程序名称和event对象,
//在调用这个方法时会自动为event对象添加srcElement和type属性,其他属性手动添加
var btn=document.getElementById('myBtn');
var event=document.createEventObject();
event.screenX=100;
event.screenY=0;
event.clientX=0;
event.clientY=0;
event.ctrlKey=false;
event.altKey=false;
event.shiftKey=false;
event.button=0;
btn.fireEvent("onclick",event);
var textbox=document.getElementById("myTextbox");
var event=document.createEventObject();
event.altKey=false;
event.ctrlKey=false;
event.shiftKey=false;
event.keyCode=65;
textbox.fireEvent("onkeypress",event);
【表单基础】
//避免多次提交表单
EventUtil.addHandler(form,'submit',function(event){
event=EventUtil.getEvent(event);
target=EventUtil.getTarget(event);
var btn=target.elements['submit-btn'];
btn.disabled=true;
});
//focus()\blur()
EventUtil.addHandler(window,'load',function(event){
document.forms[0].elements[0].focus();
});
//如果第一个表单字段是<input>,且type为'hidden',上面代码会出错
//如果css的display和visibility隐藏了该字段,同样致错
//HTML5新增autofocus属性,自动把焦点移到相应字段
//Firefox4+、Safari5+、Chrome、Opera9.6
//<input type='text' autofocus>
EventUtil.addHandler(window,'load',function(event){
var element=document.forms[0].elements[0];
if(element.autofocus!==true){
//autofocus在支持的浏览器中是true,不支持的是空字符串
element.focus();
}
});
//change事件常用于验证用户在字段中输入的数据
var textbox=document.forms[0].elements[0];
EventUtil.addHandler(textbox,'focus',function(event){
event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);
if(target.style.backgroundColor!='red'){
target.style.backgroundColor='yellow';
}
});
EventUtil.addHandler(textbox,'blur',function(event){
event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);
if(/[^\d]/.test(target.value)){
target.style.backgroundColor='red';
}else{
target.style.backgroundColor='';
}
});
EventUtil.addHandler(textbox,'change',function(event){
event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);
if(/[^\d]/.test(target.value)){
target.style.backgroundColor='red';
}else{
target.style.backgroundColor='';
}
});
【文本框脚本】
//选择文本select(),无参。
//应用:让用户在文本框获得焦点时选择所有文本,不必一个一个删除。
//IE9+、Opera、Firefox、Chrome、Safari,只有用户选择且释放鼠标才触发select事件;
//IE8及以前,只要选择一个字符,不释放鼠标,就触发;
//调用select()会触发select事件;
//HTML5新增selectionStart和selectionEnd属性
//IE9+、Opera、Firefox、Chrome、Safari
//IE8及以前用document.selection对象,保存着用户在整个文档范围选择的文本信息
function getSelectedText(textbox){
if(typeof textbox.selectionStart=='number'){
return textbox.value.substring(textbox.selectionStart,textbox.selectionEnd);
}else if(document.selection){
return document.selection.createRange().text;
}
}
//HTML5选择部分文本:setSelectionRange(),两个参数,起始结束索引
//IE9+、Opera、Firefox、Chrome、Safari
//IE8及以前用范围选择文本
function selectText(textbox,startIndex,stopIndex){
if(textbox.setSelectionRange){
textbox.setSelectionRange(startIndex,stopIndex);
}else if(textbox.createTextRange){
var range=textbox.createTextRange();
range.collapse(true);//折叠范围,true表示折叠刀范围起点
range.moveStart('character',startIndex);
range.moveEnd('character',stopIndex-startIndex);
range.select();
}
textbox.focus();
}
textbox.value='hello world!';
selectText(textbox,0,textbox.value.length);
//过滤输入屏蔽字符
//在Firefox中所有由非字符键触发的keypress事件对应字符编码0,Safari3以前为8
EventUtil.addHandler(textbox,'keypress',function(event){
event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);
var charCode=EventUtil.getCharCode(event);
if(!/\d/.test(String.fromCharCode(charCode))&&charCode>9&&!event.ctrlKey){
EventUtil.preventDefault(event);
}
});
//剪切板事件:
//beforecopy、copy、beforecut、cut、beforepaste、paste
//在Safari、Chrome、Firefox中三个before事件只会显示针对文本框的上下文菜单的情况触发,
//IE会在触发copy、cut、paste之前先行触发before。
//
//通过before可以向剪贴板发送数据,或者从剪贴板取得之前修改数据,
//访问数据用clipboardData对象,IE中是window的属性,Firefox4+、Safari、Chrome中是event的属性,
//在Firefox、Safari、Chrome中只有在处理剪贴板事件期间这个对象才有效,IE中可随时访问。
//
//clipboardData的三个方法:getData()、setData()、clearData()
//在Firefox、Safari、Chrome中只允许在onpaste事件处理程序中访问getData()
//
//补充:可以在paste事件中确认剪贴板中的值是否有效,若无效可取消默认行为
var EventUtil={
//...
getClipboardText:function(event){
var clipboardData=(event.clipboardData||window.clipboardData);
return clipboardData.getData('text');
},
setClipboardText:function(event){
if(event.clipboardData){
return event.clipboardData.setData('text/plain',value);
}else if(window.clipboardData){
return window.clipboardData.setData('text',value);
}
},
//...
};
//自动切换焦点
//在前一个文本框字符数达到最大后,自动切换焦点到下一个文本框
(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('txtTel1');
var textbox2=document.getElementById('txtTel2');
var textbox3=document.getElementById('txtTel3');
EventUtil.addHandler(textbox1,'keyup',tabForward);
EventUtil.addHandler(textbox2,'keyup',tabForward);
EventUtil.addHandler(textbox3,'keyup',tabForward);
//keyup会在用户输入了新字符后出发,此时是检测内容长度最佳时机
})();
【选择框脚本】
//取得所有选中项
//遍历选项集合,然后测试每个选项的selected属性
function getSelectedOptions(selectbox){
var result=new Array();
var option=null;
for (var i = 0,len=selectbox.options.length; i < len; i++) {
option=selectbox.options[i];
if(option.selected){
result.push(option);
}
}
return result;
}
//动态创建选项的三种方法:
//1、DOM方法
var selectbox=document.forms[0].elements['location'];
var newOption=document.createElement('option');
newOption.appendChild(document.createTextNode('Option text'));
newOption.setAttribute('value','Option value');
selectbox.appendChild(newOption);
//2、使用Option构造函数
//IE中存在bug,不能正确设置新选项的文本
var newOption=new Option('Option text','Option value');
selectbox.appendChild(newOption);
//3、使用选择框的add(newOpt,relOpt)方法
//该方法在relOpt之前插入newOpt,若要插入到最后,应将relOpt设为null,
//IE中第二个参数可选,但必须是新选项之后选项的索引,
//兼容DOM的浏览器要求必须指定第二个参数,
//所以,
//将relOpt设为undefined,兼容所有浏览器插入到最后
//(插入其他位置用DOM+insertBefore()方法)
var newOption=new Option('Option text','Option value');
selectbox.add(newOption,undefined);
//移除选项的三种方式:
//1、DOM的removeChild()方法
selectbox.removeChild(selectbox.options[0]);
//2、使用选择框的remove()方法
selectbox.remove(0);
//3、将相应选项设为null
selectbox.options[0]=null;
//移动和重排选项
//将第一个选择框中的第一个选项移动到第二个选择框
var selectbox1=document.getElementById('selLocations1');
var selectbox2=document.getElementById('selLocations2');
selectbox2.appendChild(selectbox1.options[0]);
//在选择框中向前移动一个选项的位置
var optionToMove=selectbox.options[1];
selectbox.insertBefore(optionToMove,selectbox.options[optionToMove.index-1]);
//在选择框中向后移动一个选项的位置
var optionToMove=selectbox.options[1];
selectbox.insertBefore(optionToMove,selectbox.options[optionToMove.index+2]);
//注:IE7存在页面重绘问题,有时用DOM方法重排的选项不能马上正确显示
【表单序列化】
function serialize(form){
var parts=[],
field=null,
i,len,j,optLen,option,optValue;
for (i = 0,len=form.elements.length; i < len; i++) {
field=form.elements[i];
switch(field.type){
//type属性未定义的不需要序列化
case "select-one":
case "selsct-multiple":
if(field.name.length){
for(j=0,optLen=field.options.length;j<optLen;j++){
option=field.options[j];
if(option.selected){
optValue='';
if(option.hasAttribute){
//兼容DOM的浏览器中
optValue=(option.hasAttribute('value')?option.value:option.text);
//如果不存在value特性,或存在但为空字符串,用选项的文本来代替
}else{
//IE
optValue=(option.attributes['value'].specified?option.value:option.text);
}
parts.push(encodeURIComponet(field.name)+'='+encodeURIComponet(optValue));
}
}
}
break;
case undefined:
case "file":
case "submit":
case "reset":
case "button":
break;
case "radio":
case "checkbox":
if(!field.checked){
break;
}
default:
//不包含没有名字的表单字段
if(field.name.length){
parts.push(encodeURIComponet(field.name)+'='+encodeURIComponet(optValue));
}
}
}
return parts.join("&");
}