前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何编写一个 jQuery 插件

如何编写一个 jQuery 插件

作者头像
Alan Zhang
发布2018-10-19 14:53:41
6890
发布2018-10-19 14:53:41
举报
文章被收录于专栏:Alan's LabAlan's Lab

参考资料

  1. How to Create a Basic Plugin

2016-8-13 更新

https://github.com/zcfan/sket... 重写了本文的初步功能实现,支持一个页面多个画图板。但为简单起见,本文保持不变。

图片描述
图片描述

正文

简单的说一个 jQuery 插件只是我们拿来扩展 jQuery prototype 对象的一个方法。通过扩展 prototype 对象,我们可以让所有的 jQuery 对象继承我们添加的方法。

下面我们以一个画图板插件为目标,完成后它将能够把一个 div 标记扩展成最基本的画图板。

图片描述
图片描述

基本的插件

从最简单开始,我们要做的第一件事是给选中的div加一个边框,好让用户能看到画板的区域。

创建 index.html 文件,引入 jQuery ,然后创建并引入我们的插件文件。尽管只是一个例子,但规范命名还是个好习惯,就叫做jquery.sketchpad.js好了

代码语言:javascript
复制
<!-- file: index.html -->
<!DOCTYPE HTML>
<html>

<head>
<script src="http://cdn.bootcss.com/jquery/3.1.0/jquery.min.js"></script>
<script src="jquery.sketchpad.js"></script>
<style>
  #sketchpad {
    width: 200px;
    height: 200px;
  }
</style>
</head>

<body>
<div id="sketchpad"></div>
</body>

</html>
代码语言:javascript
复制
// file: jquery.sketchpad.js
$.fn.sketchpad = function() {
  this.css('border', '1px solid #000');
};
代码语言:javascript
复制
// 使用
$('.sketchpad').sketchpad(); // 给所有.sketchpad加上边框

如上,想要添加一个名为 sketchpad 的插件,只需要给 $.fn 对象添加一个新方法就可以了。然后这个方法就可以在所有的 jQuery 对象上调用。

刷新页面,应该能够看到一个 200x200 的黑框

图片描述
图片描述

支持链式调用

上面的代码能工作,但是还欠缺了很多必要的东西。jQuery的一个特色就是允许链式调用,它使你可以对一个选择器选中的元素连着执行许多操作。

这个特性的实现方式是让所有的 jQuery 方法都返回一开始的 jQuery 对象。(有一些例外,比如.width()如果不提供参数的话就会返回所选元素的宽度,并且不可链式调用)

因此想让我们的插件能够支持链式调用只需要多一行代码:

代码语言:javascript
复制
// file: jquery.sketchpad.js
$.fn.sketchpad = function() {
  this.css('border', '1px solid #000');
  return this; // 返回该 jQuery 对象以支持链式调用
};
代码语言:javascript
复制
// 使用
$('.sketchpad').sketchpad().hide(); // 支持链式调用

保护 $ 化名并引入私有作用域

$作为一个简写化名确实非常方便,但是在实际使用中,总免不了会与其它 js 代码产生冲突。这时我们会需要调用jQuery.noConflict()让jquery不再使用$化名以避免冲突。

这个时候,我们前面的插件就会出问题,因为它编写的时候用到了$化名。为了能够和其它的诸多插件友好相处,并且能够继续使用方便的$,我们需要把所有代码扔进一个“立即执行函数的表达式”里,传入jQuery作为实参,形参处命名为$

代码语言:javascript
复制
// file: jquery.sketchpad.js
(function($){
    
  $.fn.sketchpad = function() {
    this.css('border', '1px solid #000');
    return this;
  };
    
}(jQuery));

这样做还有另一个重要的原因,加入这样一个函数还能允许我们引入一个私有的变量作用域,不至于插件里的一些变量污染公共空间。比如说我们现在要创建一个 canvas 对象用来画图,就可以这样做:

代码语言:javascript
复制
// file: jquery.sketchpad.js
(function($){

  // 本插件的私有函数
  function createCanvas(width, height) {
    var canvas = $('<canvas></canvas>');
    canvas[0].width = width;
    canvas[0].height = height;
    return canvas;
  }
    
  $.fn.sketchpad = function() {
    this.css('border', '1px solid #000');
    var canvas = createCanvas(this.width(), this.height());
    var ctx = canvas[0].getContext('2d'); // 获得 context 用于画图
    this.append(canvas);
    
    return this;
  };
    
}(jQuery));

我们创建了一个新的私有函数 createCanvas 用于创建画布,避免将冗长的初始化代码堆在主函数里。

完成剩余的插件功能

代码语言:javascript
复制
// jquery.sketchpad.js
(function($){

  function createCanvas(width, height) {
    var canvas = $('<canvas></canvas>');
    canvas[0].width = width;
    canvas[0].height = height;
    return canvas;
  }

  $.fn.sketchpad = function() {
    this.css('border', '1px solid #000');
    var canvas = createCanvas(this.width(), this.height());
    var ctx = canvas[0].getContext('2d');
    this.append(canvas);

    var isDrawing = false;
    var offset = {
      left: this[0].offsetLeft,
      top:  this[0].offsetTop
    }
    var pos
    var prevX = 0,
        prevY = 0,
        currX = 0,
        currY = 0;

    canvas.mousedown(mousedown);
    canvas.mouseup(mouseup);
    canvas.mousemove(mousemove);

    function calXY(e) {
      return {
        x: e.clientX - offset.left,
        y: e.clientY - offset.top
      };
    }

    function mousedown(e) {
      var pos = calXY(e);
      prevX = pos.x;
      prevY = pos.y;
      isDrawing = true;
    }

    function mouseup(e) {
      isDrawing = false;
    }

    function mousemove(e) {
      if (!isDrawing) return;
      var pos = calXY(e);
      currX = pos.x;
      currY = pos.y;

      ctx.beginPath();
      ctx.moveTo(prevX, prevY);
      ctx.lineTo(currX, currY);
      ctx.strokeStyle = '#000';
      ctx.lineWidth = 2;
      ctx.stroke();
      ctx.closePath();

      prevX = currX;
      prevY = currY;
    }

    return this;
  };
    
}(jQuery));

本插件仅作为范例,功能较为简单,只是使用鼠标事件以及 canvas 画线。到此已经实现了开头说明的功能,但仍然可以继续扩展下去:保存载入、橡皮擦、色板甚至滤镜直至成为一个可以真正投入使用的插件。但随着插件发展与复杂度的增加,还有许多其他的地方需要注意。

尽量减少插件名字占用

编写插件时应该只占用$.fn的一个位置。因为其它的插件也都在往这里塞东西,只占用一个名字能够避免我们的插件覆盖别人的名字或者被别人覆盖。举例说明,这样做不好:

代码语言:javascript
复制
(function( $ ) {
 
    $.fn.openPopup = function() {
        // Open popup code.
    };
 
    $.fn.closePopup = function() {
        // Close popup code.
    };
 
}( jQuery ));

这样就好多了,只占用一个名字,并且使用参数来调整插件的行为:

代码语言:javascript
复制
(function( $ ) {
 
    $.fn.popup = function( action ) {
 
        if ( action === "open") {
            // Open popup code.
        }
 
        if ( action === "close" ) {
            // Close popup code.
        }
 
    };
 
}( jQuery ));

使用each()方法

一个典型的 jQuery 对象会包含任意数量元素的引用,这也就是为什么 jQuery 对象经常是以集合的形式返回的。如果你想要对特定的某个元素做一些操作的话(比如获取数据属性,计算特定的位置),你就会需要使用each()来枚举这些元素。

代码语言:javascript
复制
$.fn.myNewPlugin = function() {
 
    return this.each(function() {
        // 对每个单独的元素做一些操作
    });
 
};

让插件能够配置

随着我们的插件以后越来越复杂,让它能够通过传入设置选项来自定义就十个好主意了。要做到这点有个最简单的办法(特别是当可配置项特别多的时候),那就是通过传入一个 object 。下面我们继续修改前面的 greenify 插件来接受配置:

代码语言:javascript
复制
(function($){
    
    $.fn.greenify = function(options) {
        
        // 实现默认设置的最简单方式
        var settings = $.extend({
            // 默认的设置
            color: '#00ff00',
            backgroundColor: 'white'
        }, options);
        
        return this.css({
            color: settings.color,
            backgroundColor: settings.backgroundColor
        });
    };
    
}(jQuery));

休整一下,择日再战

看完上面的内容之后,我们大概了解了一个简单的插件是怎么编写的,并且了解了一些社区总结的设计经验。当然这只是一个开端,还有许多进阶的内容没有介绍。比如参考资料中的下一篇 Advanced Plugin Concepts 就是进一步学习的好选择。

但今天已经够了,我们已经 get 了一个新技能,还有待实践来融汇贯通。欲速则不达,现在应该是开瓶可乐(你可以换啤酒),让自己沉浸在成就感中画幅骄傲的自画像的时候才对。

图片描述
图片描述

?干杯!~

我刚刚把代码扔到了 Github 上了,玩着蛮有意思的,打算做一个业余项目继续完善。然后一搜发现了好几个类似的项目。。。?https://github.com/zcfan/sket...

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 参考资料
  • 2016-8-13 更新
  • 正文
    • 基本的插件
      • 支持链式调用
        • 保护 $ 化名并引入私有作用域
          • 完成剩余的插件功能
            • 尽量减少插件名字占用
              • 使用each()方法
                • 让插件能够配置
                  • 休整一下,择日再战
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档