Search code examples
javascriptjqueryjquery-pluginsevent-driven

how to write object-oriented jQuery plugins?


So I have a bit of experience writing normal plugins to do whatever, but I want to move towards an object-based event-driven system that can be more dynamic and customizable for the end user. For the sake of my question I have written up a small plugin that simply highlights text on the $(selector).hover() event.

Here is the JS/jQuery:

(function($) {
  var objs = [];

  var defaults = {
       color: "blue",
      normal: "black",
     onHover: function() {},
    offHover: function() {}
  };

  var Text = function (settings, self) {
    this.self     = $(self);
    this.color    = settings.color;
    this.normal   = settings.normal;
    this.show     = function () { this.self.css( "color", this.color); }
    this.noShow   = function () { this.self.css( "color", this.normal);}
    this.onHover  = settings.onHover;
    this.offHover = settings.offHover;
  };

  $.fn.myPlugin = function(opts) {
    this.each(function() {
      var settings = $.extend({}, defaults, opts);

      $(this).data('index', objs.push(new Text(settings, this)) -1);
      // I feel like this should be handled differently, maybe
      // attach an event to the inside of the object?
  });

    this.hover(
      function(e) {
        objs[$(e.currentTarget).data('index')].show();
        objs[$(e.currentTarget).data('index')].onHover();
      }, function(e) {
        objs[$(e.currentTarget).data('index')].noShow();
        objs[$(e.currentTarget).data('index')].offHover();
    });
  };
}(jQuery));

Basically, this line...

(this).data('index', objs.push(new Text(settings, this)) -1);

...could be handled much differently and more efficiently. The problem is I need a global array that holds all objects generated by the plugin. So if I call the plugin twice on two separate 'p' tags, then there should be two objects in that array, so on so forth. Right now, that aspect is 'working' but I need to store a reference to what index that object is at by attaching an 'index' data type to the DOM element. This feels like a very wrong way to have an object oriented approach. So how can I, on an event, trigger the function...

myObject.show();

...where myObject is a reference to the element in the array that I want to highlight.

I hope my question is clear, it is a weird issue to describe I feel, but also a very powerful concept if it can be applied the way I am thinking of it. Let me know if anything is unclear and I would be happy to clarify.


Solution

  • In doing a bit more reading and trying to understand how object oriented programming works in respect to javascript, jquery and the DOM, I stumbled upon my own answer. Here is what the code looks like for anyone that may have been as confused as I was going into plugin development:

    (function($) {
      var defaults = {
           color: "blue",
          normal: "black",
         onHover: function() {},
        offHover: function() {}
      };
    
      var Text = function(opts, self) {
        var settings  = $.extend({}, defaults, opts);
        this.self     = $(self);
        this.color    = settings.color;
        this.normal   = settings.normal;
        this.onHover  = settings.onHover;
        this.offHover = settings.offHover;
        this.show     = function () { this.self.css( "color", this.color);  };
        this.noShow   = function () { this.self.css( "color", this.normal); };
      };
    
      $.fn.myPlugin = function(opts) {
        this.each(function() {
          this.text = new Text(opts, this);
        });
    
        this.hover(
          function() {
            this.text.show();
            this.text.onHover.call();
          }, function() {
            this.text.noShow();
            this.text.offHover.call();
          });
      };
    }(jQuery));
    

    The issue I was dealing with was an appropriate understanding of name space and closure, as well as what things you can and cannot do with DOM elements. I am not sure if this is the common way or not, but it is working very well for my uses and might work for yours.