2

我正在将一个库转换为一个 jQuery 插件,以使其更易于初始化。这个想法是插件的行为取决于它运行的元素的类型。

如果元素是 a<p>那么我正确初始化。但是,如果该元素是另一个块元素,我<p>会在它前面加上 a。如果元素是内联元素,则需要将其从列表中移除。这工作得很好,数据附加到适当的元素(<p>)。对于连锁能力,我想<p>每次都返回 , 。

如果我从 开始$('p').Plugin({...data...}),它确实会保留<p>。但是,如果我以 开始链$('div).Plugin({...data...})<div>则返回 。这是必需的,因为插件可能仅适用于特定元素。

这是让我有些困惑的代码:

(function($) {   
    var methods = {
        init: function(options) {
        // Maintain chainability
            return this.each(function () {
                var me;
                if($(this).prop('tagName') != 'P')
                {   $(this).append('<p></p>');
                    me = $(this).children('p');
                }
                else me = $(p);
            // The following 2 lines error (I was desperate)
                $(this) = me;
                this = me;
            // The following line does nothing (that I can tell)
                return me;
            });
        },
        ... other methods ...
    };
    $.fn.Plugin = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
        // Run Init
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery Plugin');
        }
    };
}
(jQuery));

我还尝试在 init 函数中重新创建列表,以便它只获取新创建<p>的 s。有没有人有一个有效的工作建议来引导我朝着正确的方向前进?

注意:我知道上面的代码可以让我更有效率......为了解决这个问题,为了清楚起见,它被分解了。

4

2 回答 2

2

的返回this.each始终this是不可修改的,在您的情况下是您调用 .Plugin() 的选定元素的列表。在您的情况下,您不想简单地返回被选择的元素列表......而是一个改变的列表。(你是 100% 正确的,你在 .each 循环中的返回没有做任何事情来影响 this.each 本身的返回。)

如果您在this.each循环外定义变量以收集新的 dom 元素列表,在循环内添加正确的 dom 元素(见注 1),然后在循环外返回新的元素列表,您将看到您想要的结果. 但是,这里有几个陷阱。

注 1:我们使用 this.each 进行插件处理的原因是因为选择器可以返回多个元素。您将需要收集一个新的元素列表,因为只要包含非 p 标记,您基本上将返回一组与所选选择器不同的元素。要考虑的情况是$('div.myDiv, p.myP').Plugin()选择具有不同标签类型的多个元素。

主要问题之一是我们经常在我们选择的元素上设置样式,甚至使用 id 来帮助识别插件附加到的 dom 元素。通过更改作为插件根的 dom 元素,可能会出现很多需要解决或至少考虑的 css 或 id 位置问题。

(我并不是说它不能完成,特别是如果你只是转换你已经在使用的东西而不是构建一个供其他人普遍使用的插件 - 只是它带来了很多棘手的问题。)

(function($) {   
    var methods = {
        init: function(options) {
            var mes;
            this.each(function () {
                var me;
                if($(this).prop('tagName') != 'P')
                {   $(this).append('<p></p>');
                    me = $(this).children('p');
                }
                else me = $(p);
            }
            mes.push(me);
            return mes;
        },
        ... other methods ...
    };
    $.fn.Plugin = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
        // Run Init
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery Plugin');
        }
    };
}
(jQuery));
于 2013-03-15T14:39:45.380 回答
1

正如purgatory101提到的,这里确实有两个问题。首先是要了解它$().each()有自己的返回值。二是可链性。Overcoming$().each()的返回值相当简单。只需要提供一个不同的$().each()或 jQuery 对象数组。可链接性取决于您/您的用户需要/期望什么。

jQuery 和可链接性

由于$().each()经常使用的性质,大多数人认为可链接性是指返回的原始 jQuery 对象的可链接性。但是,在 jQuery 核心中有几种情况,链可能由不同的元素继续。这一切都归结为最有可能使用您的插件。以下是 jQuery 使用的两个最常见的链:

示例 1:面向结果的链接 (ROL)

.add()允许链从结果继续,而不是从原点继续。假设是用户想要修改他们新添加的元素。更常见的例子是.parent()和.children( ),尽管这些是不言自明的,并且自然期望人们想要使用适当命名的元素。

示例 2:面向源的链接 (OOL)

.append()返回原始对象。这是直接对应的.add(),因为它完成了类似的目的,但返回了不同的值。这是大多数数据、样式和元素更改方法所使用的。

OOL 解决方案

由于插件通常有许多方法,因此能够链接这些方法非常重要。在 OOL 中,解决方案是提出一个程序,该程序<p>每次都会一致地产生一组 child。这样做是这样的:

(function($) { 
// Search function
    var findP = function(obj) {
        // ... Search function for your <p> in THIS object
        return p;
    };
    var methods = {
        init: function(options) {
        // Maintain chainability
            return this.each(function () {
                var me;
                if($(this).prop('tagName') != 'P')
                {   $(this).append('<p></p>');
                    me = $(this).children('p');
                }
                else me = $(p);

                // ... other functionality...
            });
        },
        other1: function() {
            var searchMe = findP;
            return this.each(function() {
                var me = searchMe($(this));
            });
        }
    };
    $.fn.Plugin = function (method) {
        ... Route to method ...
    };
}
(jQuery));

这样做的问题是源对象可能在方法调用之间发生了变化,因此必须考虑它们可能不存在的可能性。这需要更多的处理器密集型,但可能更安全并符合预期的行为。

ROL 解决方案

仅当要立即对结果采取行动时,才应使用 ROL。这与 jQuery 的.add,.children.parent. 有几种方法可以做到这一点。这里的问题是,虽然这可以通过多种方式完成,但purgatory101 的推送解决方案是最安全的。必须提到它,因为使用不同的 $() 列表的快捷方式可能很有吸引力,但有抓取您不希望对其采取行动的元素的危险。

    var methods = {
        init: function(options) {
        // Option 1) Build new list
            return newlist.each(function () {
                var me;
                if($(this).prop('tagName') != 'P')
                {   $(this).append('<p></p>');
                    me = $(this).children('p');
                }
                else me = $(p);

                // ... other functionality...
            });
        },
        // ... other methods ...
    };
    $.fn.Plugin = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
        // ... OR Option 2) build new list ...
            return methods.init.apply(newlist, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery Plugin');
        }            
    };

应该注意的是,我不确定这两个选项之间的区别是否存在。

于 2013-03-15T16:47:12.353 回答