Event模型主要包括Event接口本身的描述以及在DOM节点上注册的事件
一开始浏览器处理事件的时候只有原始事件模型,通过javascript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理属性。在DOM0级事件处理程序中只支持事件冒泡的过程。
var btn = document.getElementById("btn");
btn.onclick = function(){}
btn.onclick = null; //删除事件,直接设置为null即可
<a href="http://google.com" onclick="alert('123');"></a>
以上两种方式同时存在时,上一种在js中定义事件处理程序会覆盖掉后面在html标签定义的事件处理程序
优点:简单且具有跨浏览器的优势
缺点:一个事件处理程序只能绑定一个函数。
实例:
<!-- 由于DOM0级事件只能绑定一个函数,最后的结果是alert出123 -->
<button class="btn" id="btn" onclick="doClick()">click me</button>
var btn = document.getElementById("btn");
btn.onclick = function(){
alert('123');
};
function doClick() {
alert('456');
}
在IE10及以下版本中,只支持事件冒泡,在IE11中同时支持事件捕获与事件冒泡。在IE10及以下版本中,通过以下方法添加和删除事件。
element.attachEvent("on"+ eventName, handler); //添加事件处理程序
element.detachEvent("on"+ eventName, handler); //删除事件处理程序
在IE11及其他非IE浏览器中,同时支持事件捕获和事件冒泡两个过程,通过以下方法添加和删除事件
addEventListener(eventName, handler, useCapture); //添加事件处理程序
removeEventListener(eventName, handler, useCapture); //删除事件处理程序
其中的useCapture参数表示是否支持事件捕获,true表示支持事件捕获,false表示支持事件冒泡。默认状态为false
(1)在DOM2级事件处理程序中,不管是IE浏览器还是非IE浏览器都支持对同一个事件绑定多个处理函数
var handler1 = function (){}
var handler2 = function (){}
---------------IE10及以下------------------
btn.attachEvent('onclick', handler1);
btn.attachEvent('onclick', handler2);
---------------IE11及非IE-----------------
btn.addEventListener('click', handler1);
btn.addEventListener('click', handler2);
<!-- 在id为wrap元素上点击会先后alert出123和456 -->
var wrap = document.getElementById('wrap');
wrap.addEventListener('click', function() {
alert('123');
}, false);
wrap.addEventListener('click', function () {
alert('456');
}, false);
(2)在需要删除绑定的事件时,不能删除掉匿名函数。意思是说添加和删除的必须是同一个函数。
var wrap = document.getElementById('wrap');
var outer = document.getElementById('outer');
var inner = document.getElementById('inner');
var handler = function () {
alert('789');
};
// 第一中方式绑定和取消的是同一个函数,因此可以取消绑定的事件
wrap.addEventListener('click', handler, false);
wrap.removeEventListener('click', handler);
//第二种方式采取的是匿名函数的形式,则不会取消其绑定的click事件
wrap.addEventListener('click', function () {
alert('123');
}, false);
wrap.removeEventListener('click', function () {
});
(1)在使用attachEvent为同一个事件添加多个事件处理函数时,按照添加的相反顺序执行
//以下代码先alert出hello world,后alert出clicked
var btn=document.getElementById("mybtn");
btn.attachEvent("onclick",function(){
alert("clicked");
});
btn.attachEvent("onclick",function(){
alert("hello world!");
});
(2)在使用attachEvent添加的事件处理程序会在全局作用域中运行,因此this指向全局作用域window。而通过addEventListener添加的事件处理程序在指定的元素内部执行,this指向绑定的元素。
var EventUtil = {
addEventHandler: function(element, type, handler){
if(element.addEventListener){
element.addEventListener(type, handler);
}else if(element.attachEvent){
element.attachEvent("on" + type, handler);
}else{
element["on" + type] = handler;
}
},
removeEventHandler:function(element, type, handler){
if(element.addEventListener){
element.removeEventListener(type, handler);
}else if(element.detachEvent){
element.detachEvent("on" + type, handler);
}else{
element["on"+type] = null;
}
}
}
根据定义事件的规则不同,事件处理过程也将会不同。如果同时定义了事件捕获和事件冒泡,则会按照先捕获后冒泡的顺序,在目标阶段则按照事件定义的先后顺序执行。
DOM3级事件处理程序是在DOM2级事件的基础上重新定义了事件,也添加了一些新的事件。最重要的区别是DOM3允许自定义事件,自定义事件可以由createEvent("CustomEvent")
方法创建,返回的对象有一个initCustomEvent()
。
方法接收如下四个参数。
可以像分配其他事件一样在DOM中分派创建的自定义事件对象。如:
var div = document.getElementById("myDiv");
EventUtil.addEventHandler(div,"myEvent", function () {
alert("div myEvent!");
});
EventUtil.addEventHandler(document,"myEvent",function(){
alert("document myEvent!");
});
if(document.implementation.hasFeature("CustomEvents","3.0")){
var e = document.createEvent("CustomEvent");
e.initCustomEvent("myEvent",true,false,"hello world!");
div.dispatchEvent(e);
}
这个例子中创建了一个冒泡事件“myEvent”。而event.detail的值被设置成了一个简单的字符串,然后在div和document上侦听该事件,因为在initCustomEvent中设置了事件冒泡。所以当div激发该事件时,浏览器会将该事件冒泡到document。
实例:
var myDiv = document.getElementById('myDiv');
EventUtil.addEventHandler(myDiv, 'myEvent', function () {
alert('myDiv clicked');
});
EventUtil.addEventHandler(document, 'myEvent', function (e) {
alert('document clicked');
console.log(e);
});
if(document.implementation.hasFeature('CustomEvent', '3.0')) {
var e = document.createEvent('CustomEvent');
e.initCustomEvent('myEvent', true, false, {name: '13'});
myDiv.dispatchEvent(e);
console.log(e.detail); // {name: '13'}
}
https://segmentfault.com/a/1190000003497939
http://www.cnblogs.com/zichi/p/4713038.html
event = new Event(typeArg, eventInit);
其中typeArg表示创建事件的名称,eventInit中有两个参数bubbles,cancelable分别代表是否冒泡,能否被取消
给document对象添加上新创建的look事件,并自动触发look事件
<script>
var ev = new Event('look', {
'bubbles': true,
'cancelable': false
});
document.addEventListener('look', function () {
console.log('look event invoke!');
});
document.dispatchEvent(ev);
</script>
bubbles属性返回一个布尔值,代表当前事件是否会向上层DOM元素冒泡.
实例:
<body onload="load(event)" onkeypress="load(event)" onkeydown="load(event)">
<button onclick="doClick(event)">click me</button>
<script>
function load(e) {
console.log(e.bubbles); //body的onload事件返回false,表明不能冒泡
}
function doClick(e) {
console.log(e.bubbles); //true
}
</script>
</body>
cancelable属性返回一个布尔值,表明事件是否可以被取消。一个事件的默认动作是否被取消,是在该事件初始化时规定的
实例:
var ev = new Event('look', {
bubbles: true,
cancelable: false
});
var a = document.getElementById('cancelableA');
a.addEventListener('look', function (e) {
console.log(e.cancelable); //false
e.preventDefault();
});
a.dispatchEvent(ev);
target在事件的目标阶段,currentTarget在事件的捕获、冒泡和目标阶段。只有当事件流同时处在目标阶段,target与currentTarget才会同时指向同一个对象。而在捕获和冒泡阶段,target指向当前元素,currentTarget指向当前元素的父级
该属性返回一个布尔值,表明当前事件的默认动作是否取消,也就是是否执行了event.preventDefault()方法
实例:
<a href="http://www.google.com" onclick="doPreventDefault(event);">点击我</a>
<script>
function doPreventDefault(e) {
console.log(e.defaultPrevented); //false
e.preventDefault();
console.log(e.defaultPrevented); //true
}
</script>
该属性返回一个数字,表示当前事件所处在的整个事件流过程中的某一阶段。根据定义事件的不同
常量 | 值 | 描述 |
---|---|---|
Event.NONE | 0 | 没有事件处理 |
Event.CAPTURING_PHAS | 1 | 在事件流的捕获阶段 |
Event.AT_TARGET | 2 | 在目标阶段 |
Event.BUBBLING_PHASE | 3 | 在冒泡阶段 |
返回一个布尔值,表明当前事件是否是由用户行为触发(比如真实的鼠标点击触发的一个click事件),还是由一个脚本生成的。
实例:
<!-- 在outer点击一次时,会产生outer true,wrap true,
然后会有outer false和wrap false -->
<div id="wrap">
<div id="outer">
</div>
</div>
<script>
wrap.addEventListener('click', function (event) {
console.log('wrap ' + event.isTrusted);
outer.click();
});
outer.addEventListener('click', function (event) {
console.log('outer ' + event.isTrusted);
});
</script>
返回一个字符串,表示该事件对象的事件类型
实例:
<body
onkeydown="getEvtType(event)"
onkeyup="getEvtType(event)"
onkeypress="getEvtType(event)"
onmousedown="getEvtType(event)"
onmouseup="getEvtType(event)">
<!-- 会随着按键和鼠标按下显示出对应的事件类型名称 -->
function getEvtType(evt) {
currEvent = evt.type;
var text = document.getElementById("Etype").innerText;
document.getElementById("Etype").innerHTML = text + currEvent + ' | ';
}
以下属性都是非标准属性,不建议在生产环境中使用
返回从1970年1月1日到事件发生时的时间戳。经过测试后,在Safari下可以返回正确的时间戳。而在Chrome浏览器下返回的是打开页面到首次触发事件的时间间隔。
实例:
<body onkeypress="getTime(event)"></body>
<script type="text/javascript">
function getTime(event) {
document.getElementById("time").firstChild.nodeValue = event.timeStamp;
}
</script>
创建一个新的事件,随之必须调用自身的initEvent方法进行初始化
语法: document.createEvent()
,返回一个event对象
document.createEvent()
创建的event对象。已经在web标准中移除,不建议在生产环境中使用,推荐使用Event的构造方法实例:
var myCheckbox = document.getElementById('myCheckbox');
myCheckbox.addEventListener('click', function (e) {
console.log(e.cancelable);
Object.defineProperty(e, 'cancelable', {
writable: true
});
e.cancelable = false;
console.log(e.cancelable);
e.preventDefault();
}, false);
keyCode与charCode参考资料: http://blog.csdn.net/xqg666666/article/details/41308809
event. stopImmediatePropagation()
方法,则后续的事件处理程序不会被调用实例:
<!-- 点击button的时候,控制台打印出first和second,但不会打印出third -->
var btn = document.getElementById('btn');
btn.addEventListener('click', function (e) {
console.log('first');
});
btn.addEventListener('click', function (e) {
console.log('second');
e.stopImmediatePropagation();
});
btn.addEventListener('click', function (e) {
console.log('third');
});
注意:在document上绑定focus事件时,需要通过以下语法,后面的true不能省略。
document.addEventListener('focus', function () {}, true);
click事件
return false
,DOM0级推荐使用js下绑定事件的方法;如果在DOM2事件类型下,使用event.preventDefault()
dblclick事件
mousedown和mouseup事件
事件触发顺序
mouseover
mouseout
mouseenter
mouseleave
mousemove
执行顺序
实例: mouseMove.html
wheel
keydown
keypress
keyup
开启输入法后,输入框文本输入问题
Chrome浏览器下
在Chrome浏览器下,中文输入法不支持keypress事件,在整个中文输入乃至最后字符进入输入框的过程中都不会触发keypress事件。要想在输入过程中采用keypress监听输入是不可取的。但是在项目中的回车保存的uixInputEdit指令采用keypress比较可行。keydown在中文输入过程中keyCode始终为229
Firefox浏览器
在Firefox浏览器下,开启中文输入法后,在整个输入过程中只会触发一次keydown事件,在输入结束后触发一次keyup事件。
Safari浏览器下
在Safari浏览器下,同Chrome浏览器是相同的效果,不同的是其对字母键也不支持keypress事件。
keyCode
组合键
采用keyup事件,通过keyCode和ctrlKey,shiftKey,altKey来进行组合
if (event.keyCode == 90 && event.ctrlKey) {
console.log('save');
}
textInput事件
支持键盘事件的元素
在一个web页面中,有一些默认的可拖拽元素,包括选中的文本,image图片和超链接。除了这些元素外,其他元素默认是不可拖拽的,为了能让其他元素也能拖拽,必须进行以下三步:
在拖拽过程中发生的事件:
发生于拖拽目标的事件
dataTransfer.setData
方法存储拖拽数据发生于放置目标的事件
拖拽通常有以下几个步骤:
定义可拖动的目标。将希望拖动的元素的draggable属性设为true
<div draggable="true" ondragstart="dragStart(event)">
This text <strong>may</strong> be dragged.
</div>
定义被拖动的数据,可能会有多种不同的格式,例如文本、图片、URL链接等。使用setData()方法在dataTransfer中设置数据。需要提供两个参数:数据类型和数据值。例如
var dt = event.dataTransfer;
dt.setData("application/x-bookmark", bookmarkString);
dt.setData("text/uri-list", "http://www.mozilla.org");
dt.setData("text/plain", "http://www.mozilla.org");
也可以通过clearData()方法清除存储的数据,如果没有传递参数则清除所有存储的数据。那么后续的拖拽行为也不会触发
event.dataTransfer.clearData("text/uri-list");
允许设置的拖放效果
在拖拽行为发生时,会从拖拽的目标处产生一个半透明的图片。该图片在拖拽过程中会随着鼠标指针移动。默认情况下图片的内容就是拖动内容在页面上的显示状态。我们也可以通过setDragImage
方法自定义拖拽时的效果。以下例子就是自定义拖拽时的图片
var img = document.createElement('img');
img.src = 'http://pic2.52pk.com/files/120908/1288606_103339_1_lit.jpg;
e.dataTransfer.setDragImage(img, 0, 0);
定义拖放结束后放置的区域。默认情况下,浏览器会组织任何向HTML元素的拖拽行文,所以要使一个元素成为可放置区域,必须组织浏览器的默认行为,这需要在dragenter和dragover事件中进行监听
placeElement.addEventListener('dargenter', function(event){
event.preventDeafult();
});
dragElement.addEventListener('dragover', function(event){
event.preventDefault();
});
在drop时进行一些数据处理
var text = e.dataTransfer.getData('text/plain');
e.target.textContent = text;
实现将列表内容拖拽进垃圾箱的一次完整演示
创建一个可拖拽对象,这里为一个列表
<div class="dragbox">
<div class="draglist" title="拖拽我" draggable="true">列表1</div>
<div class="draglist" title="拖拽我" draggable="true">列表2</div>
<div class="draglist" title="拖拽我" draggable="true">列表3</div>
<div class="draglist" title="拖拽我" draggable="true">列表4</div>
<div class="draglist" title="拖拽我" draggable="true">列表5</div>
<div class="draglist" title="拖拽我" draggable="true">列表6</div>
</div>
给dragstart设置事件监听器来存储拖拽数据
eleDrags[i].ondragstart = function (ev) {
/*拖拽开始*/
//拖拽效果
ev.dataTransfer.effectAllowed = "move";
ev.dataTransfer.setData("text", ev.target.innerHTML);
ev.dataTransfer.setDragImage(ev.target, 0, 0);
eleDrag = ev.target;
return true;
};
给dragenter设置事件监听程序,设置拖拽目标进入时的行为
eleDustbin.ondragenter = function (ev) {
/*拖拽元素进入目标元素头上的时候*/
this.style.color = "#ffffff";
return true;
};
给dragover设置事件箭筒程序,用来确定给用户的反馈信息,如果需要drop事件触发,则必须event.preventDefault()
eleDustbin.ondragover = function (ev) {
/*拖拽元素在目标元素头上移动的时候*/
ev.preventDefault();
}
drop事件监听程序
eleDustbin.ondrop = function (ev) {
/*拖拽元素进入目标元素头上,同时鼠标松开的时候*/
if (eleDrag) {
var text = document.createElement('text');
text.innerHTML = '<strong>"' + eleDrag.innerHTML + '"</strong>被扔进了垃圾箱 <br/>';
eleRemind.appendChild(text);
eleDrag.parentNode.removeChild(eleDrag);
}
this.style.color = "#000000";
return false;
};
dragend事件监听程序
eleDrags[i].ondragend = function (ev) {
/*拖拽结束,清空存储的拖拽数据*/
ev.dataTransfer.clearData("text");
eleDrag = null;
return false
};
移动端事件主要指的是TouchEvent,这类事件用来响应用户对触摸屏或者触摸板的操作。每个touch对象代表一个触点,每个触点都由其位置,大小,形状,压力大小和目标element描述。
TouchEvent
代表当触摸行为在触摸平面发生变化的事件
Touch
代表用户与触摸平面的一个触点
{
screenX: 511,
screenY: 400,//触点相对于屏幕左边沿的Y坐标
clientX: 244.37899780273438,
clientY: 189.3820037841797,//相对于可视区域
pageX: 244.37,
pageY: 189.37,//相对于HTML文档顶部,当页面有滚动的时候与clientX=Y 不等
force: 1,//压力大小,是从0.0(没有压力)到1.0(最大压力)的浮点数
identifier: 1036403715,//一次触摸动作的唯一标识符
radiusX: 37.565673828125, //能够包围用户和触摸平面的接触面的最小椭圆的水平轴(X轴)半径
radiusY: 37.565673828125,
rotationAngle: 0,//它是这样一个角度值:由radiusX 和 radiusY 描述的正方向的椭圆,需要通过顺时针旋转这个角度值,才能最精确地覆盖住用户和触摸平面的接触面
target: {} // 此次触摸事件的目标element
}
TouchList
代表一系列的Touch;因为用户可能同时会有多个手指触摸平面。例如event.changedTouches
的结果返回的就是TouchList
。针对不同事件类型,event.changedTouches
返回的结果不同,如下所示:
DocumentTouch
包含一些创建Touch对象和TouchList对象的便捷方法
touchstart
用户在触摸屏上放置一个触点时触发。事件的目标element就是触点位置上的目标element。
touchend
一个触点被用户从触摸屏删除时触发。当触点移除屏幕外面时也将触发,例如用户手指划出屏幕边缘。事件的目标element和这个touchend对应的touchstart的目标element相同,哪怕touchend事件触发时,触点已经移除了该element。
touchmove
用户在触摸平面上移动时触发。事件的目标element和对应的touchstart对应的目标元素相同,哪怕当touchmove事件触发时已经移除了该element
touchcancel
触点由于某些原因被中断触发,有以下几种可能:
如果同时绑定了touch事件和鼠标事件,则在touch事件后会触发鼠标事件,为了消除鼠标事件带来的影响一般会使用event.preventDefault()
来取消鼠标的默认事件
实例: touchCanvas.html
创建一个canvas画布
<canvas id="canvas" width="600" height="600" style="border: 1px solid black;">
你的浏览器不支持canvas
</canvas>
设置事件监听器
在页面加载时,给canvas注册触摸事件
function startup() {
var canvas = document.getElementById('canvas');
canvas.addEventListener('touchstart', touchStart, false);
canvas.addEventListener('touchmove', touchMove, false);
canvas.addEventListener('touchend', touchEnd, false);
canvas.addEventListener('touchleave', touchLeave, false);
console.log('initialize');
}
跟踪新的触摸行为
由于在触摸过程中,触摸点会频繁的变换,使用一个数组来存放当前触摸点的集合
//跟踪正在进行的触摸点
var ongoingTouches = new Array();
给canvas绑定touchstart事件,当用户触摸屏幕时,触发touchstart事件,增加一个触摸点至ongoingTouches中
function touchStart(event) {
event.preventDefault();
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var touches = event.changedTouches;
for (var i = 0; i < touches.length; i++) {
ongoingTouches.push(touches[i]);
var color = '#0000ff';
ctx.strokeStyle = color;
ctx.fillRect(touches[i].pageX - 2, touches[i].pageY - 2, 1, 1);
}
}
触摸移动时绘制图形
function touchMove(event) {
event.preventDefault();
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var touches = event.changedTouches;
ctx.lineWidth = 4;
for (var i = 0; i < touches.length; i++) {
var color = '#00ffff';
var idx = ongoingTouchIndexById(touches[i].identifier);
ctx.strokeStyle = color;
// beginPath开始一条路径或者重置当前路径
ctx.beginPath();
// moveTo将一个新的子路径的起始点移动到(x,y)坐标
ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY);
// lineTo使用直线连接子路径到(x,y)坐标,这个方法并不会真正绘制
ctx.lineTo(touches[i].pageX, touches[i].pageY);
// 绘制当前的路径
ctx.stroke();
//改变当前的触摸点的位置
ongoingTouches.splice(idx, 1, touches[i]);
}
}
触摸行为结束时
当触摸行为结束时,绘制最后一条线段,然后删除行为结束的触摸点
function touchEnd(event) {
event.preventDefault();
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var touches = event.changedTouches;
ctx.lineWidth = 4;
for (var i = 0; i < touches.length; i++) {
var color = '#00ffff';
var idx = ongoingTouchIndexById(touches[i].indentifier);
ctx.strokeStyle = color;
ctx.beginPath();
ctx.moveTo(ongoingTouches[i].pageX, ongoingTouches[i].pageY);
ctx.lineTo(touches[i].pageX, touches[i].pageY);
// 删除掉行为结束的触摸点
ongoingTouches.splice(idx, 1);
}
}