创建简单的 jQuery 插件
本文演示了使用示例 Map Scroller 创建简单 jQuery 插件的步骤。
引言
我们看到了许多 JavaScript 库。JavaScript 库通过避免开发者直接处理浏览器兼容性问题的麻烦,使开发者的生活更加轻松。它们允许程序员编写他们想要实现的代码,而不必担心浏览器,从而节省了大量时间。
jQuery 是我喜欢的库之一。它轻量级、易于使用、美观等。jQuery 的一个有趣特性是插件;它们是可以在需要时插入的 JavaScript 文件。
在本文中,我们将看到如何创建一个名为 Map Scroller 的简单插件,该插件可以使任何溢出的 HTML 元素像 Google Maps 中的一样滚动。
让我们深入文章 :)
要求
- jQuery v1.4.2
- jquery.DisableTextSelect.js
jQuery 技巧
- $是 jQuery 中的主要对象。jQuery 的一切都基于它,或以某种方式使用它。
- $包装了一个名为- fn的对象。要向 jQuery 添加插件,我们必须将我们的方法添加到此对象中。
$.fn.Method1 = function(){
};
$("div").Method1();
bind() 允许您将方法绑定到事件。$("input[type=button]").bind("mouseover mouseenter", function(){
    alert('hi');
});
data() 允许您将任何自定义数据存储为键值对。$(this).data("mouseposition", {x:200, y:300});
addClass() 允许您动态添加 CSS 类。$("p").addClass("class1 class2");
removeClass() 允许您删除 CSS 类。$("p").removeClass("class1");
scrollTop() 垂直滚动元素。$("myDiv").scrollTop(20);
//scrolls the element down to 20 pixels from the top position.
scrollLeft() 水平滚动元素。$("myDiv").scrollLeft(20);
//scrolls the element right to 20 pixels from the left position.
地图滚动器
高层计划
- 创建一个名为 mapScroll(direction)的方法。
- 输入参数 direction可以是以下任何值:“vertical”、“horizontal”或“both”。它表示滚动发生的が。如果您不传递任何参数,则默认为“both”。
- 将此方法添加到 jQuery 的 fn对象中,这样任何溢出的 HTML 元素都可以通过以下方式实现滚动:
$("#OverflowingDiv").mapScroll("vertical");
低层计划
- 要实现此滚动,我们必须禁用该元素的文本选择。一个名为 DisableTextSelect的插件可以帮助我们做到这一点。
- 将事件处理程序绑定到元素的 mousedown、mousemove和mouseup事件。
- 在 mousedown事件处理程序中启用滚动。
- 在 mouseup事件处理程序中禁用滚动。
- 在 mousemove事件处理程序中计算鼠标移动的距离(从旧鼠标位置到新鼠标位置),并滚动元素。
代码
让我们先创建骨架
$.fn.mapScroll = function(direction){
        
    //implementation starts
    
};
直接调用 $ 在全局范围内是不安全的,因为可能存在其他库有自己的 $ 实现。因此,为了避免全局变量冲突,我们需要创建一个私有空间。
(function($){
    $.fn.mapScroll = function(direction){
    
        //implementation starts
        
    };
})(jQuery); //$ and jQuery both are same.
现在,大括号之间的所有变量都不会被外部干扰。我们使用闭包来创建私有空间。
下面是一个典型的闭包
var x = 25;
/*Closure starts*/
(function(){
    var x = 35;
    alert(x);
})();
/*Closure ends*/
alert(x);
当浏览器处理完上述代码后,它会先弹出 35,然后弹出 25。闭包有助于创建私有作用域,等等。
我们将在插件中使用一些私有方法;让我们将它们放入私有空间。
(function($){
    //private space 
        //Add your private members here
    $.fn.mapScroll = function(direction){
    
        //implementation starts
        
    };
})(jQuery);
为 mouseup、mousedown 和 mousemove 事件创建事件处理程序,并将它们包装在一个名为 eventHandlers 的私有对象中。
(function($){
    //private methods
    var eventHandlers = {
        mousemove: function(e){            
            //calculate the distance moved and scroll
        },
        
        mouseup: function(e){
            //reset the flag
        },
        
        mousedown: function(e){
            //capture the mouse position at initial time
            //set a flag
        }
    };
    $.fn.mapScroll = function(direction){
    
        //hook the event handlers 
        
    };
})(jQuery);
我们的骨架已经基本准备好了。让我们先完成公共方法 mapScroll。
$.fn.mapScroll = function(direction){
    return this.each(function(){
        //disable text selection using the plugin jquery.DisableTextSelect.js
        $(this).disableTextSelect();
        ....
    });
    
};
等等!上面的 return 和 each() 是做什么的?$() 返回一个 HTML 元素数组,因此我们必须迭代所有这些元素,为此,我们使用 each() 方法。return 使我们的插件可链式调用。jQuery 以链式调用而闻名。链式调用允许我们在一个对象上调用一组方法,如下所示:
$("p").show().addClass("paragraphstyle").mapScroll("both"); //a chain
如果没有链式调用,我们就必须单独调用方法:
$("p").show();
$("p").addClass("paragraphstyle");
$("p").mapScroll("both");
我们正在使用链式调用,因此我们可以执行 $("scrollDiv").mapScroll().show()。应用链式调用非常简单,只需返回对象而不是什么都不返回。
回到 mapScroll()!绑定事件处理程序
$.fn.mapScroll = function(direction){
    return this.each(function(){
        //disable text selection
        $(this).disableTextSelect();    
        
        //binding event-handlers
        $(this).bind("mousemove", eventHandlers.mousemove)
               .bind("mouseup", eventHandlers.mouseup)
               .bind("mousedown", eventHandlers.mousedown);
        ...
        
    });
    
};
我们现在需要在这里讨论鼠标移动事件处理程序。在鼠标移动事件中,我们需要知道方向。如果方向是“vertical”,那么我们应该只在垂直方向上滚动。同样,如果它是“horizontal”,则只在水平方向上滚动,否则两者都滚动。
如果我们以某种方式将 direction 参数传递给鼠标移动处理程序,那么通过编写一组 if-else 语句,我们可以做到这一点。但我对此方法不太满意,因为每次鼠标移动时,条件都会被评估。如果它只发生一次,那将是很好的。
我考虑将滚动逻辑放在一个名为 scrollFn 的单独方法中。诀窍在于,scrollFn 的实现是根据 direction 参数动态确定的。有点令人困惑?请看下面的代码:
var scrollFn = (function(){
    switch(direction || "both")
    {
        case "vertical":
            return function($this,x,y){ 
                $this.scrollTop( $this.scrollTop() + y ); 
            };
        case "horizontal":
            return function($this,x,y){ 
                $this.scrollLeft( $this.scrollLeft() + x ); 
            };
        case "both":
            return function($this,x,y){ 
                $this.scrollTop( $this.scrollTop() + y).scrollLeft($this.scrollLeft() + x );
            };                
    }        
})();
同样的闭包概念!但这一次,与私有作用域不同。这种方法称为记忆化。函数 scrollFn 的主体部分在初始化时动态创建。
所以,如果 direction 的值为“vertical”,那么 scrollFn 是:
var scrollFn = function($this,x,y){
    //scrolls only in vertical direction
    $this.scrollTop( $this.scrollTop() + y ); 
}
同样,如果它是“both”,那么:
var scrollFn = function($this,x,y){
    //scrolls in both directions
    $this.scrollTop( $this.scrollTop() + y).scrollLeft($this.scrollLeft() + x );
}
其中 $this 是对象,而 x 和 y 是对象必须水平和垂直滚动的像素距离。我们必须找到一种方法在鼠标移动处理程序中获取此方法。这非常简单!我们只需使用 data() 方法将其存储起来:
$(this).data("scrollFn", scrollFn);
//"scrollFn" is the key, scrollFn is the value.
现在我们可以通过以下方式在插件的任何地方获取 scrollFn:
var scrollFn = $(this).data("scrollFn");
这是 mapScroll 方法的完整实现:
$.fn.mapScroll = function(direction){
    var scrollFn = (function(){
        switch(direction || "both")
        {
            case "vertical":
                return function($this,x,y){ 
                    $this.scrollTop( $this.scrollTop() + y ); 
                };
            case "horizontal":
                return function($this,x,y){ 
                    $this.scrollLeft( $this.scrollLeft() + x ); 
                };
            case "both":
                return function($this,x,y){ 
                    $this.scrollTop( $this.scrollTop() + y).scrollLeft(
                                     $this.scrollLeft() + x );
                };
        }
    })();
    return this.each(function(){
        //disabling text selection
        $(this).disableTextSelect();
            
        //binding event-handlers
        $(this).bind("mousemove", eventHandlers.mousemove)
               .bind("mouseup", eventHandlers.mouseup)
               .bind("mousedown", eventHandlers.mousedown);
        
        //adding a css class to set custom cursor 
        $(this).addClass("openhand");    
        
        //storing the scroll function.
        $(this).data("scrollFn", scrollFn)    
    });
    
};
现在让我们看看事件处理程序。
mousedown: function(e){
    $(this).data("scroll", true)
           .data("position", {x : e.pageX, y : e.pageY});
           
    $(this).removeClass("openhand")
           .addClass("closedhand");
}
在 mousedown 事件处理程序中,我们通过将标志 scroll 设置为 true 来启用滚动。标志和初始鼠标位置使用 data() 存储。此外,我们交换 CSS 类以更改光标。
mouseup: function(e){
    $(this).data("scroll", false);
    
    $(this).removeClass("closedhand")
           .addClass("openhand");
}
在 mouseup 事件处理程序中,我们通过将标志 scroll 设置为 false 并恢复光标 CSS 类来禁用滚动。
mousemove: function(e){
    var $this = $(this), 
        scroll = $this.data("scroll"), 
        prevPos = $this.data("position"),
        scrollFn = $this.data("scrollFn");
    
    if(scroll){
        var diffX = prevPos.x - e.pageX, 
            diffY = prevPos.y - e.pageY;
                
        scrollFn($this, diffX, diffY);
        
        $this.data("position", {x:e.pageX, y:e.pageY});
    }
}
在 mousemove 事件处理程序中,我们计算旧鼠标位置和新鼠标位置之间的差值,并使用存储的 scrollFn 方法滚动内容。最后,我们用新位置覆盖旧鼠标位置。
在查看完整代码之前,我发现了一个鼠标移出时的错误,这是我们没有监听的事件……所以,我做了与鼠标抬起时相同的事情,即当用户将鼠标移出元素时禁用滚动。
//both mouseup & mouseout binded to same handler
$(this).bind("mouseup mouseout", eventHandlers.mouseupout);
这是我们的完整代码:
(function($){
    var eventHandlers = {
        mousemove: function(e){
            var $this = $(this), 
                scroll = $this.data("scroll"), 
                prevPos = $this.data("position"),
                scrollFn = $this.data("scrollFn");
            if(scroll){            
                var diffX = prevPos.x - e.pageX, 
                    diffY = prevPos.y - e.pageY;
                        
                scrollFn($this, diffX, diffY);
                
                $this.data("position", {x:e.pageX, y:e.pageY});
            }
        },
        mouseupout: function(e){
            $(this).removeClass("closedhand")
                   .addClass("openhand");
                   
            $(this).data("scroll", false);
        },
        
        mousedown: function(e){
            $(this).removeClass("openhand")
                   .addClass("closedhand");
                   
            $(this).data("scroll", true)
                   .data("position", {x : e.pageX, y : e.pageY});
        }
    };
    $.fn.mapScroll = function(direction){
        //assigning scroll function
        var scrollFn = (function(){
            switch(direction || "both")
            {
                case "vertical":
                    return function($this,x,y){ 
                        $this.scrollTop( $this.scrollTop() + y ); 
                    };
                   case "horizontal":
                    return function($this,x,y){ 
                        $this.scrollLeft( $this.scrollLeft() + x ); 
                    };
                case "both":
                    return function($this,x,y){ 
                        $this.scrollTop( $this.scrollTop() + y).scrollLeft(
                                         $this.scrollLeft() + x );
                    };
            }
        })();
        return this.each(function(){
            //disabling text selection
            $(this).disableTextSelect();
            
            //binding event-handlers
            $(this).bind("mousemove", eventHandlers.mousemove)
                   .bind("mouseup mouseout", eventHandlers.mouseupout)
                   .bind("mousedown", eventHandlers.mousedown);
            //adding class
            $(this).addClass("openhand");s
            
            //caching the scroll function.
            $(this).data("scrollFn", scrollFn);
        });
    };
})(jQuery);
就是这样!
演示
在 IIS 中配置附加的代码,然后运行 MapScroller.html 页面,即可看到我们的插件生效。
如果您认为本文有用,请不要忘记投票。
**编程即诗歌**




