JQuery 扩展与插件


当我们使用 jQuery 对象的方法时,由于 jQuery 对象可以操作一组 DOM,而且支持链式操作,所以用起来非常方便。

但是 jQuery 内置的方法永远不可能满足所有的需求。比如,我们想要高亮显示某些 DOM 元素,用 jQuery 可以这么实现:

$('span.hl').css('backgroundColor', '#fffceb').css('color', '#d85030');
$('p a.hl').css('backgroundColor', '#fffceb').css('color', '#d85030');

总是写重复代码可不好,万一以后还要修改字体就更麻烦了,能不能统一起来,写个highlight()方法?

$('span.hl').highlight();
$('p a.hl').highlight();

答案是肯定的。我们可以扩展 jQuery 来实现自定义方法。将来如果要修改高亮的逻辑,只需修改一处扩展代码。这种方式也称为编写 jQuery 插件。

编写jQuery插件

版本一

给 jQuery 对象绑定一个新方法是通过扩展 $.fn 对象实现的。让我们来编写第一个扩展——highlight1():

$.fn.highlight1 = function () {
    // this已绑定为当前jQuery对象:
    this.css('backgroundColor', '#fffceb').css('color', '#d85030');
    return this;
}

注意到函数内部的 this 在调用时被绑定为 jQuery 对象,所以函数内部代码可以正常调用所有 jQuery 对象的方法。

<!-- HTML结构 -->
<div id="test-highlight1">
    <p>什么是<span>jQuery</span></p>
    <p><span>jQuery</span>是目前最流行的<span>JavaScript</span>库。</p>
</div>
<script>
'use strict';
$.fn.highlight1 = function () {
    // this已绑定为当前jQuery对象:
    this.css('backgroundColor', '#fffceb').css('color', '#d85030');
    return this;
}
$('#test-highlight1 span').highlight1();
</script>

为什么最后要 return this;?因为 jQuery 对象支持链式操作,我们自己写的扩展方法也要能继续链式下去:

$('span.hl').highlight1().slideDown();

不然,用户调用的时候,就不得不把上面的代码拆成两行。

版本二

但是这个版本并不完美。有的用户希望高亮的颜色能自己来指定,怎么办?

我们可以给方法加个参数,让用户自己把参数用对象传进去。于是我们有了第二个版本的 highlight2():

$.fn.highlight2 = function (options) {
    // 要考虑到各种情况:
    // options为undefined
    // options只有部分key
    var bgcolor = options && options.backgroundColor || '#fffceb';
    var color = options && options.color || '#d85030';
    this.css('backgroundColor', bgcolor).css('color', color);
    return this;
}

完整代码:

<!-- HTML结构 -->
<div id="test-highlight2">
    <p>什么是<span>jQuery</span> <span>Plugin</span></p>
    <p>编写<span>jQuery</span> <span>Plugin</span>可以用来扩展<span>jQuery</span>的功能。</p>
</div>
<script>
'use strict';
// 自定义颜色
$.fn.highlight2 = function (options) {
    // 要考虑到各种情况:
    // options为undefined
    // options只有部分key
    var bgcolor = options && options.backgroundColor || '#fffceb';
    var color = options && options.color || '#d85030';
    this.css('backgroundColor', bgcolor).css('color', color);
    return this;
}
$('#test-highlight2 span').highlight2({
    backgroundColor: '#00a8e6',
    color: '#ffffff'
});
</script>

对于默认值的处理,我们用了一个简单的&&||短路操作符,总能得到一个有效的值。

[word-gray]另一种方法是使用 jQuery 提供的辅助方法 $.extend(target, obj1, obj2, ...),它把多个 object 对象的属性合并到第一个 target 对象中,遇到同名属性,总是使用靠后的对象的值,也就是越往后优先级越高[/word-gray]:

// 把默认值和用户传入的options合并到对象{}中并返回:
var opts = $.extend({}, {
    backgroundColor: '#00a8e6',
    color: '#ffffff'
}, options);

版本三

紧接着用户对 highlight2() 提出了意见:每次调用都需要传入自定义的设置,能不能让我自己设定一个缺省值,以后的调用统一使用无参数的 highlight2()?

也就是说,我们设定的默认值应该能允许用户修改。

那默认值放哪比较合适?放全局变量肯定不合适,最佳地点是 $.fn.highlight2 这个函数对象本身。

于是最终版的 highlight() 终于诞生了:

$.fn.highlight = function (options) {
    // 合并默认值和用户设定值:
    var opts = $.extend({}, $.fn.highlight.defaults, options);
    this.css('backgroundColor', opts.backgroundColor).css('color', opts.color);
    return this;
}
// 设定默认值:
$.fn.highlight.defaults = {
    color: '#d85030',
    backgroundColor: '#fff8de'
}

这次用户终于满意了。用户使用时,只需一次性设定默认值:

$.fn.highlight.defaults.color = '#fff';
$.fn.highlight.defaults.backgroundColor = '#000';

然后就可以非常简单地调用 highlight() 了。
最终,我们得出编写一个 jQuery 插件的原则:

  • $.fn 绑定函数,实现插件的代码逻辑;
  • 插件函数最后要 return this; 以支持链式调用;
  • 插件函数要有默认值,绑定在 $.fn.<pluginName>.defaults 上;
  • 用户在调用时可传入设定值以便覆盖默认值。

针对特定元素的扩展

我们知道 jQuery 对象的有些方法只能作用在特定 DOM 元素上,比如 submit() 方法只能针对 form。如果我们编写的扩展只能针对某些类型的 DOM 元素,应该怎么写?

还记得 jQuery 的选择器支持 filter() 方法来过滤吗?我们可以借助这个方法来实现针对特定元素的扩展。

举个例子,现在我们要给所有指向外链的超链接加上跳转提示,怎么做?

先写出用户调用的代码:

$('#test-external a').external();

然后按照上面的方法编写一个 external 扩展:

$.fn.external = function () {
    // return返回的each()返回结果,支持链式调用:
    return this.filter('a').each(function () {
        // 注意: each()内部的回调函数的this绑定为DOM本身!
        var a = $(this);
        var url = a.attr('href');
        if (url && (url.indexOf('http://')===0 || url.indexOf('https://')===0)) {
            a.attr('href', '#0')
             .removeAttr('target')
             .append(' <i class="uk-icon-external-link"></i>')
             .click(function () {
                if(confirm('你确定要前往' + url + '?')) {
                    window.open(url);
                }
            });
        }
    });
}

HTML 结构:

<!-- HTML结构 -->
<div id="test-external">
    <p>如何学习<a href="http://jquery.com">jQuery</a>?</p>
    <p>首先,你要学习<a href="https://www.qingtingshu.com/qianduanjiaocheng/javascript">JavaScript</a>,并了解基本的<a href="https://developer.mozilla.org/en-US/docs/Web/HTML">HTML</a>。</p>
</div>

[word-green]扩展 jQuery 对象的功能十分简单,但是我们要遵循 jQuery 的原则,编写的扩展方法能支持链式调用、具备默认值和过滤特定元素,使得扩展方法看上去和 jQuery 本身的方法没有什么区别[/word-green]。


<< JQuery AJAX JavaScript 错误处理 >>


没有账号? 忘记密码?

社交账号快速登录