高级技巧

目录

1 高级函数

1.1 安全的类型检测

1.2 作用域安全的构造函数

1.3 惰性载入函数

1.4 函数绑定

1.5 函数柯里化

2 防篡改对象

2.1 不可扩展对象 Object.preventExtensions()

2.2 密封的对象Object.seal()

2.3 冻结的对象的 Object.freeze()

3 高级定时器

4 自定义事件

5 拖放

5.1 修缮拖动功能

5.2 添加自定义事件


1 高级函数

1.1 安全的类型检测

alert(Object.prototype.toString.call(value)); //"[object Array]" 
function isArray(value){
 return Object.prototype.toString.call(value) == "[object Array]";
}

这一技巧也广泛应用于检测原生 JSON 对象。Object 的 toString()方法不能检测非原生构造函 数的构造函数名。因此,开发人员定义的任何构造函数都将返回[object Object]。有些 JavaScript 库会包 含与下面类似的代码。

var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON) ==
"[object JSON]"; 

1.2 作用域安全的构造函数

作用域安全的构造函数在进行任何更改前,首先确认 this 对象是正确类型的实例。如果不是,那 么会创建新的实例并返回。请看以下例子:

function Person(name, age, job){
 if (this instanceof Person){
 this.name = name;
 this.age = age;
 this.job = job;
 } else {
 return new Person(name, age, job);
 }
}
var person1 = Person("Nicholas", 29, "Software Engineer");
alert(window.name); //""
alert(person1.name); //"Nicholas"
var person2 = new Person("Shelby", 34, "Ergonomist");
alert(person2.name); //"Shelby" 

这段代码中的 Person 构造函数添加了一个检查并确保 this 对象是 Person 实例的 if 语句,它 表示要么使用 new 操作符,要么在现有的 Person 实例环境中调用构造函数。任何一种情况下,对象初 始化都能正常进行。如果 this 并非 Person 的实例,那么会再次使用 new 操作符调用构造函数并返回 结果。最后的结果是,调用 Person 构造函数时无论是否使用 new 操作符,都会返回一个 Person 的新 实例,这就避免了在全局对象上意外设置属性。

关于作用域安全的构造函数的贴心提示。实现这个模式后,你就锁定了可以调用构造函数的环境。 如果你使用构造函数窃取模式的继承且不使用原型链,那么这个继承很可能被破坏。这里有个例子:

function Polygon(sides){
 if (this instanceof Polygon) {
 this.sides = sides;
 this.getArea = function(){ 
return 0;
 };
 } else {
 return new Polygon(sides);
 }
}
function Rectangle(width, height){
 Polygon.call(this, 2);
 this.width = width;
 this.height = height;
 this.getArea = function(){
 return this.width * this.height;
 };
}
var rect = new Rectangle(5, 10);
alert(rect.sides); //undefined 

在这段代码中,Polygon 构造函数是作用域安全的,然而 Rectangle 构造函数则不是。新创建一 个 Rectangle 实例之后,这个实例应该通过 Polygon.call()来继承 Polygon 的 sides 属性。但是, 由于 Polygon 构造函数是作用域安全的,this 对象并非 Polygon 的实例,所以会创建并返回一个新 的 Polygon 对象。Rectangle 构造函数中的 this 对象并没有得到增长,同时 Polygon.call()返回 的值也没有用到,所以 Rectangle 实例中就不会有 sides 属性。

   如果构造函数窃取结合使用原型链或者寄生组合则可以解决这个问题。考虑以下例子:

function Polygon(sides){
 if (this instanceof Polygon) {
 this.sides = sides;
 this.getArea = function(){
 return 0;
 };
 } else {
 return new Polygon(sides);
 }
}
function Rectangle(width, height){
 Polygon.call(this, 2);
 this.width = width;
 this.height = height;
 this.getArea = function(){
 return this.width * this.height;
 };
}
Rectangle.prototype = new Polygon();
var rect = new Rectangle(5, 10);
alert(rect.sides); //2 

上面这段重写的代码中,一个Rectangle实例也同时是一个Polygon实例,所以Polygon.call() 会照原意执行,最终为 Rectangle 实例添加了 sides 属性

多个程序员在同一个页面上写 JavaScript 代码的环境中,作用域安全构造函数就很有用了。届时, 对全局对象意外的更改可能会导致一些常常难以追踪的错误。除非你单纯基于构造函数窃取来实现继 承,推荐作用域安全的构造函数作为最佳实践。

1.3 惰性载入函数

惰性载入表示函数执行的分支仅会发生一次。有两种实现惰性载入的方式,第一种就是在函数被调 用时再处理函数。在第一次调用的过程中,该函数会被覆盖为另外一个按合适方式执行的函数,这样任 何对原函数的调用都不用再经过执行的分支了。例如,可以用下面的方式使用惰性载入重写 createXHR()。

function createXHR(){
 if (typeof XMLHttpRequest != "undefined"){
 createXHR = function(){ 
return new XMLHttpRequest();
 };
 } else if (typeof ActiveXObject != "undefined"){
 createXHR = function(){
 if (typeof arguments.callee.activeXString != "string"){
 var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
 "MSXML2.XMLHttp"],
 i, len;
 for (i=0,len=versions.length; i < len; i++){
 try {
 new ActiveXObject(versions[i]);
 arguments.callee.activeXString = versions[i];
 break;
 } catch (ex){
 //skip
 }
 }
 }
 return new ActiveXObject(arguments.callee.activeXString);
 };
 } else {
 createXHR = function(){
 throw new Error("No XHR object available.");
 };
 }
 return createXHR();
} 

在这个惰性载入的 createXHR()中,if 语句的每一个分支都会为 createXHR 变量赋值,有效覆 盖了原有的函数。最后一步便是调用新赋的函数。下一次调用 createXHR()的时候,就会直接调用被 分配的函数,这样就不用再次执行 if 语句了。

第二种实现惰性载入的方式是在声明函数时就指定适当的函数。这样,第一次调用函数时就不会损 失性能了,而在代码首次加载时会损失一点性能。以下就是按照这一思路重写前面例子的结果。

var createXHR = (function(){
 if (typeof XMLHttpRequest != "undefined"){
 return function(){
 return new XMLHttpRequest();
 };
 } else if (typeof ActiveXObject != "undefined"){
 return function(){
 if (typeof arguments.callee.activeXString != "string"){
 var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
 "MSXML2.XMLHttp"],
 i, len;
 for (i=0,len=versions.length; i < len; i++){
 try {
 new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
 } catch (ex){
 //skip
 }
 } 
}
 return new ActiveXObject(arguments.callee.activeXString);
 };
 } else {
 return function(){
 throw new Error("No XHR object available.");
 };
 }
})(); 

这个例子中使用的技巧是创建一个匿名、自执行的函数,用以确定应该使用哪一个函数实现。实际 的逻辑都一样。不一样的地方就是第一行代码(使用 var 定义函数)、新增了自执行的匿名函数,另外 每个分支都返回正确的函数定义,以便立即将其赋值给 createXHR()。

1.4 函数绑定

另一个日益流行的高级技巧叫做函数绑定。函数绑定要创建一个函数,可以在特定的 this 环境中 以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用,以便在将函数作为变量 传递的同时保留代码执行环境。请看以下例子:

var handler = {
 message: "Event handled",
 handleClick: function(event){
 alert(this.message);
 }
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", handler.handleClick); 

在上面这个例子中,创建了一个叫做 handler 的对象。handler.handleClick()方法被分配为 一个 DOM 按钮的事件处理程序。当按下该按钮时,就调用该函数,显示一个警告框。虽然貌似警告框 应该显示 Event handled ,然而实际上显示的是 undefiend 。这个问题在于没有保存 handler.handleClick()的环境,所以 this 对象最后是指向了 DOM 按钮而非 handler(在 IE8 中, this 指向 window。)可以如下面例子所示,使用一个闭包来修正这个问题。

var handler = {
 message: "Event handled",
 handleClick: function(event){
 alert(this.message);
 }
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", function(event){
 handler.handleClick(event);
}); 

这个解决方案在 onclick 事件处理程序内使用了一个闭包直接调用 handler.handleClick()。 当然,这是特定于这段代码的解决方案。创建多个闭包可能会令代码变得难于理解和调试。因此,很多 JavaScript 库实现了一个可以将函数绑定到指定环境的函数。这个函数一般都叫 bind()。

一个简单的 bind()函数接受一个函数和一个环境,并返回一个在给定环境中调用给定函数的函数, 并且将所有参数原封不动传递过去。语法如下:

function bind(fn, context){
 return function(){
 return fn.apply(context, arguments);
 };
}

这个函数似乎简单,但其功能是非常强大的。在 bind()中创建了一个闭包,闭包使用 apply()调 用传入的函数,并给 apply()传递 context 对象和参数。注意这里使用的 arguments 对象是内部函 数的,而非 bind()的。当调用返回的函数时,它会在给定环境中执行被传入的函数并给出所有参数。 bind()函数按如下方式使用:

var handler = {
 message: "Event handled",
 handleClick: function(event){
 alert(this.message);
 }
}; 
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler)); 

在这个例子中,我们用 bind()函数创建了一个保持了执行环境的函数,并将其传给 EventUtil. addHandler()。event 对象也被传给了该函数,如下所示:

var handler = {
 message: "Event handled",
 handleClick: function(event){
 alert(this.message + ":" + event.type);
 }
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler)); 

ECMAScript 5 为所有函数定义了一个原生的 bind()方法,进一步简单了操作。换句话说,你不用 再自己定义 bind()函数了,而是可以直接在函数上调用这个方法。例如:

var handler = {
 message: "Event handled",

 handleClick: function(event){
 alert(this.message + ":" + event.type);
 }
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler)); 

只要是将某个函数指针以值的形式进行传递,同时该函数必须在特定环境中执行,被绑定函数的效 用就突显出来了。它们主要用于事件处理程序以及 setTimeout() 和 setInterval()。然而,被绑 定函数与普通函数相比有更多的开销,它们需要更多内存,同时也因为多重函数调用稍微慢一点,所 以最好只在必要时使用。

1.5 函数柯里化

柯里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和必要参数。下 面是创建柯里化函数的通用方式。

function curry(fn){
 var args = Array.prototype.slice.call(arguments, 1);
 return function(){
 var innerArgs = Array.prototype.slice.call(arguments);
 var finalArgs = args.concat(innerArgs);
 return fn.apply(null, finalArgs);
 };
} 

2 防篡改对象

2.1 不可扩展对象 Object.preventExtensions()

默认情况下,所有对象都是可以扩展的。也就是说,任何时候都可以向对象中添加属性和方法。例 如,可以像下面这样先定义一个对象,后来再给它添加一个属性。

var person = { name: "Nicholas" };
person.age = 29; 

即使第一行代码已经完整定义 person 对象,但第二行代码仍然能给它添加属性。现在,使用 Object.preventExtensions()方法可以改变这个行为,让你不能再给对象添加属性和方法。 例如:

var person = { name: "Nicholas" };
Object.preventExtensions(person);
person.age = 29;
alert(person.age); //undefined 

在调用了 Object.preventExtensions()方法后,就不能给 person 对象添加新属性和方法了。 在非严格模式下,给对象添加新成员会导致静默失败,因此 person.age 将是 undefined。而在严格 模式下,尝试给不可扩展的对象添加新成员会导致抛出错误。

虽然不能给对象添加新成员,但已有的成员则丝毫不受影响。你仍然还可以修改和删除已有的成员。 另外,使用 Object.istExtensible()方法还可以确定对象是否可以扩展。

var person = { name: "Nicholas" };
alert(Object.isExtensible(person)); //true
Object.preventExtensions(person);
alert(Object.isExtensible(person)); //false 

2.2 密封的对象Object.seal()

ECMAScript 5 为对象定义的第二个保护级别是密封对象(sealed object)。密封对象不可扩展,而 且已有成员的[[Configurable]]特性将被设置为 false。这就意味着不能删除属性和方法,因为不能 使用 Object.defineProperty()把数据属性修改为访问器属性,或者相反。属性值是可以修改的。 要密封对象,可以使用 Object.seal()方法。

var person = { name: "Nicholas" };
Object.seal(person);
person.age = 29;
alert(person.age); //undefined
delete person.name;
alert(person.name); //"Nicholas" 

2.3 冻结的对象的 Object.freeze()

最严格的防篡改级别是冻结对象(frozen object)。ECMAScript 5 定义的 Object.freeze()方法可以用来冻结对象。

var person = { name: "Nicholas" };
Object.freeze(person);
person.age = 29;
alert(person.age); //undefined
delete person.name;
alert(person.name); //"Nicholas"
person.name = "Greg";
alert(person.name); //"Nicholas" 

对 JavaScript 库的作者而言,冻结对象是很有用的。因为 JavaScript 库最怕有人意外(或有意)地修 改了库中的核心对象。冻结(或密封)主要的库对象能够防止这些问题的发生

3 高级定时器

3.1 重复的定时器

3.2 Yielding Processes

这是一种叫做数组分块(array chunking)的技术,小块小块地处理数组,通 常每次一小块。基本的思路是为要处理的项目创建一个队列,然后使用定时器取出下一个要处理的项目 进行处理,接着再设置另一个定时器。基本的模式如下。

function chunk(array, process, context){
 setTimeout(function(){
 var item = array.shift();
 process.call(context, item);
 if (array.length > 0){
 setTimeout(arguments.callee, 100);
 }
 }, 100);
} 

chunk()方法接受三个参数:要处理的项目的数组,用于处理项目的函数,以及可选的运行该函数 的环境。函数内部用了之前描述过的基本模式,通过 call()调用的 process()函数,这样可以设置一 个合适的执行环境(如果必须)。定时器的时间间隔设置为了 100ms,使得 JavaScript 进程有时间在处 理项目的事件之间转入空闲。你可以根据你的需要更改这个间隔大小,不过 100ms 在大多数情况下效果 不错。可以按如下所示使用该函数: 

var data = [12,123,1234,453,436,23,23,5,4123,45,346,5634,2234,345,342];
function printValue(item){
 var div = document.getElementById("myDiv");
 div.innerHTML += item + "<br>";
}
chunk(data, printValue); 

4 自定义事件

事件是与 DOM 交互的最常见的方式,但它们也可以用于非 DOM 代码中——通过实现自定义事件。 自定义事件背后的概念是创建一个管理事件的对象,让其他对象监听那些事件。实现此功能的基本模式 可以如下定义:

function EventTarget(){
 this.handlers = {};
}
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 = 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); 
}
 }
}; 

EventTarget 类型有一个单独的属性 handlers,用于储存事件处理程序。还有三个方法: addHandler() ,用于注册给定类型事件的事件处理程序; fire() ,用于触发一个事件; removeHandler(),用于注销某个事件类型的事件处理程序。

addHandler()方法接受两个参数:事件类型和用于处理该事件的函数。当调用该方法时,会进行 一次检查,看看 handlers 属性中是否已经存在一个针对该事件类型的数组;如果没有,则创建一个新 的。然后使用 push()将该处理程序添加到数组的末尾。

如果要触发一个事件,要调用 fire()函数。该方法接受一个单独的参数,是一个至少包含 type 属性的对象。fire()方法先给 event 对象设置一个 target 属性,如果它尚未被指定的话。然后它就 查找对应该事件类型的一组处理程序,调用各个函数,并给出 event 对象。因为这些都是自定义事件, 所以 event 对象上还需要的额外信息由你自己决定。

removeHandler()方法是 addHandler()的辅助,它们接受的参数一样:事件的类型和事件处理 程序。这个方法搜索事件处理程序的数组找到要删除的处理程序的位置。如果找到了,则使用 break 操作符退出 for 循环。然后使用 splice()方法将该项目从数组中删除。

然后,使用 EventTarget 类型的自定义事件可以如下使用:

function handleMessage(event){
 alert("Message received: " + event.message);
}
//创建一个新对象
var target = new EventTarget();
//添加一个事件处理程序
target.addHandler("message", handleMessage);
//触发事件
target.fire({ type: "message", message: "Hello world!"});
//删除事件处理程序
target.removeHandler("message", handleMessage);
//再次,应没有处理程序
target.fire({ type: "message", message: "Hello world!"}); 

在这段代码中,定义了 handleMessage()函数用于处理 message 事件。它接受 event 对象并输 出 message 属性。调用 target 对象的 addHandler()方法并传给"message"以及 handleMessage() 函数。在接下来的一行上,调用了 fire()函数,并传递了包含 2 个属性,即 type 和 message 的对象 直接量。它会调用 message 事件的事件处理程序,这样就会显示一个警告框(来自 handleMessage())。 然后删除了事件处理程序,这样即使事件再次触发,也不会显示任何警告框。

为这种功能是封装在一种自定义类型中的,其他对象可以继承 EventTarget 并获得这个行为,如下例所示:

function Person(name, age){
 EventTarget.call(this);
 this.name = name;
 this.age = age;
}
inheritPrototype(Person,EventTarget);
Person.prototype.say = function(message){
 this.fire({type: "message", message: message});
}; 

5 拖放

拖放的基本概念很简单:创建一个绝对定位的元素,使其可以用鼠标移动。这个技术源自一种叫做“鼠标拖尾”的经典网页技巧。鼠标拖尾是一个或者多个图片在页面上跟着鼠标指针移动。 单元素鼠标 拖尾的基本代码需要为文档设置一个 onmousemove 事件处理程序,它总是将指定元素移动到鼠标指针 的位置,如下面的例子所示:

EventUtil.addHandler(document, "mousemove", function(event){
 var myDiv = document.getElementById("myDiv");
 myDiv.style.left = event.clientX + "px";
 myDiv.style.top = event.clientY + "px";
}); 

在这个例子中,元素的 left 和 top 坐标设置为了 event 对象的 clientX 和 clientY 属性,这 就将元素放到了视口中指针的位置上。它的效果是一个元素始终跟随指针在页面上的移动。只要正确的 时刻(当鼠标按钮按下的时候)实现该功能,并在之后删除它(当释放鼠标按钮时),就可以实现拖放 了。最简单的拖放界面可用以下代码实现:

var DragDrop = function(){
 var dragging = null;
 function handleEvent(event){
 //获取事件和目标
 event = EventUtil.getEvent(event);
 var target = EventUtil.getTarget(event);
 //确定事件类型
 switch(event.type){
 case "mousedown":
 if (target.className.indexOf("draggable") > -1){
 dragging = 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(){
 EventUtil.addHandler(document, "mousedown", handleEvent);
 EventUtil.addHandler(document, "mousemove", handleEvent);
 EventUtil.addHandler(document, "mouseup", handleEvent);
 }, 
disable: function(){
 EventUtil.removeHandler(document, "mousedown", handleEvent);
 EventUtil.removeHandler(document, "mousemove", handleEvent);
 EventUtil.removeHandler(document, "mouseup", handleEvent);
 }
 }
}(); 
DragDrop.enable();

DragDrop 对象封装了拖放的所有基本功能。这是一个单例对象,并使用了模块模式来隐藏某些实 现细节。dragging 变量起初是 null,将会存放被拖动的元素,所以当该变量不为 null 时,就知道正 在拖动某个东西。handleEvent()函数处理拖放功能中的所有的三个鼠标事件。它首先获取 event 对 象和事件目标的引用。之后,用一个 switch 语句确定要触发哪个事件样式。当 mousedown 事件发生 时,会检查 target 的 class 是否包含"draggable"类,如果是,那么将 target 存放到 dragging 中。这个技巧可以很方便地通过标记语言而非 JavaScript 脚本来确定可拖动的元素。

DragDrop 还有两个公共方法:enable()和 disable(),它们只是相应添加和删除所有的事件处 理程序。这两个函数提供了额外的对拖放功能的控制手段。

要使用 DragDrop 对象,只要在页面上包含这些代码并调用 enable()。拖放会自动针对所有包含 "draggable"类的元素启用,如下例所示:

<div class="draggable" style="position:absolute; background:red"> </div> 
//注意为了元素能被拖放,它必须是绝对定位的。

注意为了元素能被拖放,它必须是绝对定位的。

5.1 修缮拖动功能

var DragDrop = function(){
 var dragging = null;
 diffX = 0;
 diffY = 0;
 function handleEvent(event){
 //获取事件和目标
 event = EventUtil.getEvent(event);
 var target = EventUtil.getTarget(event);
 //确定事件类型
 switch(event.type){
 case "mousedown":
 if (target.className.indexOf("draggable") > -1){
 dragging = target;
 diffX = event.clientX - target.offsetLeft;
 diffY = event.clientY - 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;
 }
 };
 //公共接口
 return {
 enable: function(){
 EventUtil.addHandler(document, "mousedown", handleEvent);
 EventUtil.addHandler(document, "mousemove", handleEvent);
 EventUtil.addHandler(document, "mouseup", handleEvent);
 },
 disable: function(){
 EventUtil.removeHandler(document, "mousedown", handleEvent);
 EventUtil.removeHandler(document, "mousemove", handleEvent);
 EventUtil.removeHandler(document, "mouseup", handleEvent);
 }
 }
}(); 
DragDrop.enable();

5.2 添加自定义事件

由于 DragDrop 对象是一个使用了模块模式的单例,所以需要进行一些更改来使用 EventTarget 类型。首先,创建一个新的 EventTarget 对象,然后添加 enable()和 disable()方法,最后返回这 个对象。看以下内容。

var DragDrop = function(){
 var dragdrop = new EventTarget(),
 dragging = null,
 diffX = 0,
 diffY = 0;
 function handleEvent(event){
 //获取事件和对象
 event = EventUtil.getEvent(event);
 var target = EventUtil.getTarget(event);
 //确定事件类型
switch(event.type){
 case "mousedown":
 if (target.className.indexOf("draggable") > -1){
 dragging = target;
 diffX = event.clientX - target.offsetLeft;
 diffY = event.clientY - target.offsetTop;
 dragdrop.fire({type:"dragstart", target: dragging,
 x: event.clientX, y: event.clientY});
 }
 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});
 }
 break;
 case "mouseup":
 dragdrop.fire({type:"dragend", target: dragging,
 x: event.clientX, y: event.clientY});
 dragging = null;
 break;
 }
 };
 //公共接口
 dragdrop.enable = function(){
 EventUtil.addHandler(document, "mousedown", handleEvent);
 EventUtil.addHandler(document, "mousemove", handleEvent);
 EventUtil.addHandler(document, "mouseup", handleEvent);
 };
 dragdrop.disable = function(){
 EventUtil.removeHandler(document, "mousedown", handleEvent);
 EventUtil.removeHandler(document, "mousemove", handleEvent);
 EventUtil.removeHandler(document, "mouseup", handleEvent);
 };
 return dragdrop;
}(); 
DragDrop.enable();
/*这段代码定义了三个事件:dragstart、drag 和 dragend。它们都将被拖动的元素设置为了 target,并给出了 x 和 y 属性来表示当前的位置。它们触发于 dragdrop 对象上,之后在返回对象前给对象增加 enable()和 disable()方法。这些模块模式中的细小更改令 DragDrop 对象支持
了事件,如下:*/
DragDrop.addHandler("dragstart", function(event){
 var status = document.getElementById("status");
 status.innerHTML = "Started dragging " + event.target.id;
});
DragDrop.addHandler("drag", function(event){
 var status = document.getElementById("status");
 status.innerHTML += "<br/> Dragged " + event.target.id + " to (" + event.x +
 "," + event.y + ")";
});
DragDrop.addHandler("dragend", function(event){
 var status = document.getElementById("status");
 status.innerHTML += "<br/> Dropped " + event.target.id + " at (" + event.x +
 "," + event.y + ")";
});

这里,为 DragDrop 对象的每个事件添加了事件处理程序。还使用了一个元素来实现被拖动的元素 当前的状态和位置。一旦元素被放下了,就可以看到从它一开始被拖动之后经过的所有的中间步骤。

为 DragDrop 添加自定义事件可以使这个对象更健壮,它将可以在网络应用中处理复杂的拖放 功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值