【问题标题】:Creating a jQuery plugin that initializes itself based on HTML class and name创建一个基于 HTML 类和名称初始化自身的 jQuery 插件
【发布时间】:2014-08-31 19:25:40
【问题描述】:

我是编写 jQuery 插件的新手,但我对使用它们有一定的经验。我使用过的一些插件只能以编程方式初始化,即

$("input[name='blah']").fancyPants();

我觉得这很烦人,但我已经设法从教程和查看其他插件的代码中弄清楚如何编写这样的基本插件。

其他的,比如Bootstrap的modal组件,可以纯粹通过markup来初始化,即

<input type='text' name='blah' class='fancy-pants'>

然后只需包含插件 .js 文件,任何带有 fancy-pants 类的输入都会自动初始化为 fancyPants 插件。我喜欢这种行为,即使我知道它占用了一个特定的类名。

问题 1:他们是如何做到的?我浏览了bootstrap.js,但里面似乎有很多我不明白的地方。

问题 2: 假设我有一个包含 DOM 中多个元素的插件。例如,

<button class="bootstrapradio" name="primary_group" value="epicurist"><i class='fa fa-cutlery'></i></button>
<button class="bootstrapradio" name="primary_group" value="futurist"><i class='fa fa-space-shuttle'></i></button>
<button class="bootstrapradio" name="primary_group" value="stoic"><i class='fa fa-tree'></i></button>        

<button class="bootstrapradio" name="beverage" value="beer"><i class='fa fa-beer'></i></button>
<button class="bootstrapradio" name="beverage" value="martini"><i class='fa fa-glass'></i></button>

在不改变标记结构(例如添加包装容器)的情况下,我怎样才能自动初始化我的插件的两个实例,一个覆盖所有按钮的类为bootstrapradio,名称为@987654329 @,另一个覆盖了类为bootstrapradio、名称为beverage的所有按钮?

编辑:这是我现在正在使用的内容。

(function ( $ ) {

    var methods = {
        init : function(options) {
            // Initialization method, adds classes to elements and binds events
            ...
            return this;
        },
        value : function( new_val ) {
            // Method to get/set value
            ...
        }
    };

    $.fn.bootstrapradio = function(methodOrOptions) {
        if ( methods[methodOrOptions] ) {
            return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
        } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
            // Default to "init"
            return methods.init.apply( this, arguments );
        } else {
            $.error( 'Method ' +  methodOrOptions + ' does not exist on jQuery.bootstrapradio' );
        }    
    };    
}( jQuery ));

我希望这个插件如何转换 DOM 的示例:

document.ready之前:

    <button class="bootstrapradio" name="primary_group" value="epicurist" title='Epicurist' disabled><i class='fa fa-cutlery'></i></button>
    <button class="bootstrapradio" name="primary_group" value="futurist" title='Futurist'><i class='fa fa-space-shuttle'></i></button>
    <button class="bootstrapradio" name="primary_group" value="stoic" title='Stoic'><i class='fa fa-tree'></i></button>        

    <button class="bootstrapradio" name="beverage" value="beer" title='Beer'><i class='fa fa-beer'></i></button>
    <button class="bootstrapradio" name="beverage" value="martini" title='Martini'><i class='fa fa-glass'></i></button>

document.ready之后:

    <button class="btn btn-xs bootstrapradio" name="primary_group" value="epicurist" title='Epicurist' disabled><i class='fa fa-cutlery'></i></button>
    <button class="btn btn-xs bootstrapradio" name="primary_group" value="futurist" title='Futurist'><i class='fa fa-space-shuttle'></i></button>
    <button class="btn btn-xs bootstrapradio" name="primary_group" value="stoic" title='Stoic'><i class='fa fa-tree'></i></button>
    <input type="hidden" name="primary_group">

    <button class="btn btn-xs bootstrapradio" name="beverage" value="beer" title='Beer'><i class='fa fa-beer'></i></button>
    <button class="btn btn-xs bootstrapradio" name="beverage" value="martini" title='Martini'><i class='fa fa-glass'></i></button>
    <input type="hidden" name="beverage">

【问题讨论】:

  • 初始化到底是什么意思?
  • 我认为 OP 想知道如何利用 say data-role=modal 和类似的属性来触发特定的 JS 事件。 @ArtOfCode
  • 是的,我明白了,我只是不知道他想在这个初始化中做什么。
  • 是的,我希望插件在具有特定属性(例如classdata-*)的元素上自动调用其init 方法。
  • 如果你要走这条路,你还应该考虑设置一些默认值,这样用户就可以通过传入他们自己的选项来覆盖这些默认值。这也是很常见的模式:stackoverflow.com/questions/8905507/…

标签: javascript jquery html twitter-bootstrap jquery-plugins


【解决方案1】:

您可以通过这种方式使用 jQuery 选择属性

$("[data-role='modal']");

所以,在你的例子中,你会有类似的东西

$(".bootstrapradio[name='primary_group']").myPlugin();
$(".bootstrapradio[name='beverage']").myPlugin();

许多插件在没有类的情况下通过插件特定的属性名称来做到这一点。

一个例子是Lightbox2,它使用$("[data-lightbox]")查找所有可能的元素,然后根据这些属性的值将它们分成单独的灯箱组。

如果您希望它自动执行,您只需将相同的功能包装在插件文件中准备好的文档中即可。

例如,对于这个 HTML:

<div data-bootstrapradio='type1'></div>
<div data-bootstrapradio='type1'></div>
<div data-bootstrapradio='type2'></div>

还有这个 Javascript:

$(function(){

    $.fn.bootstrapradio = function(params) {
        return $(this).each(function() {
            // Your code
        });
    }

    // Get all the radios on the page currently
    var $radios = $("[data-bootstrapradio]");

    // Get the groupings of them
    var radioTypes = [];
    $radios.each(function(i,el){
        var type = $(this).attr("data-bootstrapradio");
        if (radioTypes.indexOf(type) === -1){
            radioTypes.push(type);
        }
    });

    // Initialize the plugin using all radios of each type
    for (var i=0; i<radioTypes.length; i++){

        var type = radioTypes[i];

        var radioGroup = $radios.filter(function(i,el){
            return $(el).is("[data-bootstrapradio="+type+"]");
        }).bootstrapradio();

        // Do whatever else with radioGroup you want for initialization
    }

});

你最终得到了 2 个 bootstrapradios,第一个用两个元素初始化,第二个用一个元素初始化。

【讨论】:

  • 好的,但请记住我想对其进行结构化,以便用户无需编写任何额外的 JS 来初始化插件。
  • 对,这里准备好的文档在您的插件文件中,并且将在所有这些元素上初始化您的插件,而无需用户做任何事情。
【解决方案2】:

这确实取决于您在初始化时想要做什么。一般来说,我猜您将不得不使用带有一些属性检查逻辑和自定义初始化代码的文档就绪子句。

这是 jQuery 插件的标准构造:

(function($) {
    $.fn.myPlugin = function(params) {
         return $(this).each(function() {
             // do your plugin stuff
         });
    }
})(jQuery);

我认为您必须对其进行修改,以便在准备好文档时,您的插件初始化,然后如果您仍然希望能够调用它,您也可以添加该函数:

(function($) {
    $(document).ready(function() {
        $(".bootstrapradio").each(function() {
            if($(this).attr("name") == "primary-group") {
                // do something specific for name 'primary-group'
            }
            else if($(this).attr("name") == "something-else") {
                // do something else
            }
            else {
                // either catch-all or just drop the element
            }
        });
    });

    $.fn.myPlugin = function(params) {
        // do your plugin stuff
    }
})(jQuery);

如果您想使用现有的 init 方法并为其命名,那就更简单了:

(function($) {
    var self = this;

    $(document).ready(function() {
        $(".bootstrapradio").each(function() {
            self.methods.init($(this).attr("name"));
        });
    });

    $.fn.myPlugin = function(params) {
        // do your plugin stuff
    }
})(jQuery);

self 非常重要,可以让您在此处访问正确的范围。

此外,您当前的代码存在语法错误。你的最后一行:

}( jQuery ));

应该是:

})(jQuery);

编辑:值得注意的是,根据下面评论中链接的article,最后一点实际上并不是语法错误。两者都可以。

【讨论】:

  • 参见“立即调用函数表达式 (IIFE)”中的 benalman.com/news/2010/11/…
  • 我想我们已经开始实现目标了,但是如果我想让它作用于任意的named 元素怎么办?我试图弄清楚如何将$(this).attr("name") 传递给我的init 方法。这行得通吗?
  • 很容易传进去。你想用它做什么?如果你想用每个不同的名字做不同的事情,你会被 if-else 语句困住。
  • 嗯,基本上我想对每组按钮执行相同的转换(其中集合由名称确定)。其中一些转换需要应用于集合中的每个元素,而另一些则每个集合只执行一次。
  • 我们已经接近了,但我现在应该把我的methods 放在哪里?如果我将它嵌套在 (function($) { 中,我会收到控制台错误(self.methods 未定义)。
【解决方案3】:

试试(这个模式)

// hold jquery `ready` event until `plugin` "ready"
$.holdReady(true);
$(function() {
  // `deferred` until _after_ `plugin` defined ,
  // `ready` `hold` `resolved` within `plugin`
  $.bootstrapradio().css("font-size", "24px")
});

// `plugin` 
(function($, window, undefined) {
    // define `plugin` as `function` and `object`
    // check document for `bootstrapradio` `class` elements ,
    // if true , proceed , if not ,
    // create empty jquery object , proceed
    // define logic checks for elements , objects , other
    // in window , document , other
    $.fn.bootstrapradio = $.bootstrapradio = function(options) {
        $elems = $(".bootstrapradio").is("*") ? $(".bootstrapradio") : $({});
        // settings
        $.bootstrapradio.settings = {
            "primary_group" : "green"
            , "beverage" : "blue"
        };
        // extend settings to options
        var options = $.extend($.bootstrapradio.settings, options);
        // do stuff
        // e.g., apply `settings`/`options` to `$elem`
        // based on `name` , `value` attributes in `html`
        $elems.each(function(k, v) {
            $.each(options, function(_name, _value) {
                if (_name === v.name) {
                  $(v).html(v.value).css("color", _value)
                }
            })
        });
        // return `$elems`
        return $elems;
    } || {};
    // call `plugin`
    $.when($.bootstrapradio())
    .done(function(_$elem) {
        // check if `plugin` type cast
        // to both `function` and `object` ,
        // redundant check if `plugin` `in` window
        if (typeof $.fn.bootstrapradio === "function" 
           && typeof $.bootstrapradio === "object"
           && $.bootstrapradio in window) {
               // resolve jquery `ready` (`document.ready`) `hold` ,
               // do other stuff ,
               // e.g., `$.bootstrapradio().css("font-size", "24px")`
               $.holdReady(false);
        };
    })

}(jQuery, window));

jsfiddle http://jsfiddle.net/guest271314/f2asu701/

【讨论】:

    【解决方案4】:

    嗯,@ArtOfCode 的回答让我走上了正轨。基本上,我最终在$(document).ready 中使用了.each 模式,然后检查是否已为元素所属的命名组添加了隐藏的input 字段。这样一来,我可以将btn btn-xs 类添加到所有元素,但只添加一次隐藏输入。

    然后,我使用init 方法保留了我的原始代码,以允许以编程方式初始化插件。

    这是我想出的,我在 MIT 许可下发布它:)

    https://github.com/alexweissman/bootstrapradio

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-23
      • 1970-01-01
      • 2013-08-28
      • 2019-09-24
      • 2021-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多