Search code examples
javascriptjqueryeachjs-amdjquery-1.9

jquery AMD plugin - each() does not loop?


I tried to make a jquery plugin in AMD pattern and it seems that I can't get the each() to loop. It only return the first item but I have three items.

index.html,

<body>
    <div class="element">
        1
    </div>

    <div class="element">
        2
    </div>

    <div class="element">
        3
    </div>
</body>

in the html head,

$(document).ready(function(){
    $(".element").plugin2().data('plugin_plugin2').someMethod();
});

plugin.js,

!function(root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['jquery'], factory);
    } else {
        factory(root.jQuery);
    }
}(this, function($) {

    var pluginName = 'plugin2',
        defaults = {
            param1:       'param1',
            param2:       'param2'
        };

    var Plugin = function(element, options) {
        this.element    = element;
        this.options    = options;
    };

    Plugin.prototype = {
        constructor: Plugin,

        someMethod: function(options) {
            var rootScope = this;

            return rootScope.element.each(function(e){

                console.log(e); // you get 0 only
                console.log(this); // the first <div class="element"> only

            });
        }
    };

    $.fn[pluginName] = function(options) {
        options = $.extend(true, {}, defaults, options);

        return this.each(function() {
            var $this = $(this);
            $this.data('plugin_' + pluginName, new Plugin($this, options));
        });
    };

    $.fn[pluginName].defaults = defaults;
    $.fn[pluginName].Plugin   = Plugin;
});

any ideas what have I done wrong?


Solution

  • Like all of jQuery's getter/setter methods, data is asymmetrical: When setting, it sets on all elements in a jQuery set, but when getting, it only gets from the first.

    So this line:

    $(".element").plugin2().data('plugin_plugin2').someMethod();
    

    ...will only give you the data from the first element in the set. If you want to see the data for all three, you need to loop there, too:

    $(".element").plugin2().each(function() {
        $(this).data('plugin_plugin2').someMethod();
    });
    

    But fundamentally, what your code is doing is not how you implement methods in plugins (not least for this reason). Instead, the usual way to implement methods in plugins is to have the main plugin method accept a string (the method name), e.g.:

    $(".element").plugin2().plugin2("someMethod");
    

    thanks. so how does it look for boilerplate for doing this -$(".element").plugin2().plugin2("someMethod");

    Here's a very minimal example:

    (function($) {
      var methods = {
        init: function() {
          // If you have to do different things on different
          // elements, use this.each(...) or similar
          this.css("color", "red");
          return this;
        },
        color: function(color) {
          // If you have to do different things on different
          // elements, use this.each(...) or similar
          this.css("color", color);
          return this;
        },
        increment: function(color) {
          // For example, this is effectively a `this.each(...)`:
          this.text(function() {
            return +$(this).text() + 1;
          });
          return this;
        }
      };
      var slice = Array.prototype.slice;
      $.fn.foo = function(method) {
        var f = methods[method || "init"];
        if (!f) {
          throw "Unknown method for plugin `foo`: '" + method + "'";
        }
        return f.apply(this, slice.call(arguments, 1));
      };
    })(jQuery);
    
    setTimeout(function() {
      $(".element").foo();
    }, 500);
    setTimeout(function() {
      $(".element").foo("color", "green");
    }, 1000);
    setTimeout(function() {
      $(".element").foo("increment");
    }, 1500);
    <div class="element">1</div>
    <div class="element">2</div>
    <div class="element">3</div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>