自定义事件
事件是一种叫做观察者的设计模式,观察者模式由两类对象组成:主体和观察者。主体负责发布事件,同时观察者知道通过订阅这些事件来观察主体。主体可以独自存在并正常运作即使观察者不存在。在DOM结构中,DOM元素便是主体,事件处理代码便是观察者
自定义事件背后的概念是创建一个管理事件的对象,让其他对象监听那些事件。
function EventTarget(){ //用于管理事件的对象
this.handlers = {}; //handler属性用于存储各种事件类型的处理函数
}
EventTarget.prototype = {
constructor: EventTarget,
addHandler: function(type, handler){ //添加事件处理程序,接受两个参数,事件类型和处理函数
if (typeof this.handlers[type] == "undefined"){ //如果是新的事件类型
this.handlers[type] = []; //新建一个数组,用于存储新事件类型的一系列处理函数
}
this.handlers[type].push(handler); //将处理函数推入对应事件类型的数组中
},
fire: function(event){ //触发事件,接受一个对象,包含事件类型和处理函数的输入参数
if (!event.target){ //设置event对象的target属性
event.target = this;
}
if (this.handlers[event.type] instanceof Array){
var handlers = this.handlers[event.type]; //取出指定事件类型的处理函数数组
for (var i=0, len=handlers.length; i < len; i++){ //依次调用数组中的每一个处理函数
handlers[i](event);
}
}
},
removeHandler: function(type, handler){ //移除事件
if (this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for (var i=0, len=handlers.length; i < len; i++){ //寻找对应事件函数在数组中的下标
if (handlers[i] === handler){
break;
}
}
handlers.splice(i, 1); //删除对应下标的处理函数
}
}
};
使用例子
function handleMessage(event){
alert("Message received:" + event.message);
}
var target = new EventTarget();
target.addHandler("message",handleMessage); //添加一个事件处理程序
target.addHandler({type:"message", message:"Hello world"}); //触发事件处理程序
target.removeHandler("message",handleMessage); //删除事件处理程序
拖放
实现拖放的基本思想是利用mousemove
事件,使绝对定位元素的坐标跟随鼠标指针的位置,在鼠标按下的时候(mousedown
事件)启用拖放,在鼠标释放的时候(mouseup
事件)关闭拖放,就可以实现。
var DragDrop = function(){
var dragging = null; //用于保存拖放的对象
function handleEvent(event){ //事件处理程序
switch(event.type){
case "mousedown": //鼠标按下,若元素可拖放,则启动拖放
if (event.target.className.indexOf("draggable") > -1){
dragging = event.target;
}
break;
case "mousemove": //拖放过程中使对象跟随鼠标移动
if (dragging !== null){
dragging.style.left = event.clientX + "px";
dragging.style.top = event.clientY + "px";
}
break;
case "mouseup": //关闭拖放
dragging = null;
break;
}
}
return {
enable:function(){ //启用拖放功能
document.addEventListener("mousedown",handleEvent,false);
document.addEventListener("mousemove",handleEvent,false);
document.addEventListener("mouseup",handleEvent,false);
},
disable:function(){ //启用拖放功能
document.removeEventListener("mousedown",handleEvent,false);
document.removeEventListener("mousemove",handleEvent,false);
document.removeEventListener("mouseup",handleEvent,false);
}
}
}();
上述例子,当鼠标开始移动时,元素好像是跳了一下,显得很不自然。原因在于,元素的left和top坐标默认为元素的左上角那一点。要想保持用户点击时那一点的坐标与鼠标坐标保持一致,需要计算鼠标与元素左上角的坐标差值
var dragging = null; //用于保存拖放的对象
var diffX = 0, //鼠标和元素左上角X方向上的差值
diffY = 0; //鼠标和元素左上角Y方向上的差值
function handleEvent(event){ //事件处理程序
switch(event.type){
case "mousedown": //鼠标按下,若元素可拖放,则启动拖放
if (event.target.className.indexOf("draggable") > -1){
dragging = event.target;
diffX = event.clientX - event.target.offsetLeft;
diffX = event.clientY - event.target.offsetTop;
}
break;
case "mousemove": //拖放过程中使对象跟随鼠标移动
if (dragging !== null){
dragging.style.left = (event.clientX - diffX) + "px";
dragging.style.top = (event.clientY - diffY) + "px";
}
break;
case "mouseup": //关闭拖放
dragging = null;
break;
}
}
以上只是实现了拖放功能,但是缺少事件,不知道什么时候拖放开始和结束,因此可以结合之前的自定义事件,为拖放添加dragstart,drag,dragend三个自定义事件
var DragDrop = function(){
var dragging = null;
var diffX = 0,
diffY = 0;
var dragdrop = new EventTarget(); //创建一个管理自定义事件的对象
function handleEvent(event){
switch(event.type){
case "mousedown":
if (event.target.className.indexOf("draggable") > -1){
dragging = event.target;
diffX = event.clientX - event.target.offsetLeft;
diffX = event.clientY - event.target.offsetTop;
dragdrop.fire({type:"dragstart",target:dragging,
x:event.clientX,y:event.clientY}); //触发dragstart事件
}
break;
case "mousemove":
if (dragging !== null){
dragging.style.left = (event.clientX - diffX) + "px";
dragging.style.top = (event.clientY - diffY) + "px";
dragdrop.fire({type:"drag",target:dragging,
x:event.clientX,y:event.clientY}); //触发drag事件
}
break;
case "mouseup":
dragdrop.fire({type:"dragend",target:dragging,
x:event.clientX,y:event.clientY}); //触发dragend事件
dragging = null;
break;
}
}
dragdrop.enable = function(){
document.addEventListener("mousedown",handleEvent,false);
document.addEventListener("mousemove",handleEvent,false);
document.addEventListener("mouseup",handleEvent,false);
};
dragdrop.disable = function(){
document.removeEventListener("mousedown",handleEvent,false);
document.removeEventListener("mousemove",handleEvent,false);
document.removeEventListener("mouseup",handleEvent,false);
};
return dragdrop; //返回事件对象
}();
添加事件处理程序
DragDrop.addHandler("dragstart",function(event){
var status = document.getElementById("status");
status.innerHTML = "start dragging" + event.target.id;
});
DragDrop.addHandler("drag",function(event){
var status = document.getElementById("status");
status.innerHTML = "dragged" + event.target.id + "to" + event.x + "," + event.y;
});
DragDrop.addHandler("dragend",function(event){
var status = document.getElementById("status");
status.innerHTML = "droped" + event.target.id + "at" + event.x + "," + event.y;
});