65.9K
CodeProject 正在变化。 阅读更多。
Home

创建简单的 jQuery 插件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (7投票s)

2010 年 9 月 27 日

CPOL

5分钟阅读

viewsIcon

42665

downloadIcon

554

本文演示了使用示例 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 的插件可以帮助我们做到这一点。
  • 将事件处理程序绑定到元素的 mousedownmousemovemouseup 事件。
  • 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);

mouseupmousedownmousemove 事件创建事件处理程序,并将它们包装在一个名为 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();
        ....
    });
    
};

等等!上面的 returneach() 是做什么的?$() 返回一个 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 是对象,而 xy 是对象必须水平和垂直滚动的像素距离。我们必须找到一种方法在鼠标移动处理程序中获取此方法。这非常简单!我们只需使用 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 页面,即可看到我们的插件生效。

如果您认为本文有用,请不要忘记投票。

**编程即诗歌**

© . All rights reserved.