在jQuery插件中返回正确的元素

时间:2013-03-15 13:54:33

标签: jquery jquery-plugins

我正在将库转换为jQuery插件,以便初始化更简单。这个想法是插件的行为不同,具体取决于它运行的元素的类型。

如果元素是<p>,那么我正确初始化。但是,如果元素是另一个块元素,我会在其前面添加<p>。如果元素是内联元素,则需要从列表中删除它。这工作正常,数据附加到适当的元素(<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>。有没有人有正确的工作建议来引导我朝着正确的方向前进?

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

2 个答案:

答案 0 :(得分: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));

答案 1 :(得分:1)

purgatory101所述,这里确实存在两个问题。首先要了解$().each()有自己的返回值。第二是可链接性。克服$().each()的返回值非常简单。只需要提供不同的$().each()或jQuery对象数组。可链接性取决于您/您的用户需要/期望的内容。

jQuery和可链接性

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

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

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

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

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

OOL解决方案

由于插件通常有很多方法,因此能够链接这些方法很重要。在OOL中,解决方案提出了一个程序,每次都会始终生成一组子<p>。这样做是这样的:

(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具有相同的精神。有几种方法可以实现这一目标。这里的关注点是虽然这可以通过多种方式实现,但是炼狱101的推动解决方案是最安全的。必须提到它,因为使用不同的$()列表的快捷方式可能很有吸引力,但有抓住你不想采取行动的元素的危险。

    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');
        }            
    };

应该注意的是,我不确定这两个选项之间的区别是,如果有的话。