Как иметь переменные-члены и публичные методы в плагине jQuery? - PullRequest
5 голосов
/ 07 октября 2009

Я пытаюсь создать плагин jQuery, который будет создавать что-то вроде autoCompleteBox, но с пользовательскими функциями. Как сохранить переменные-члены для каждого соответствующего элемента jQuery?

Например, мне нужно сохранить таймер для каждого. Я также хотел бы хранить ссылки на некоторые элементы DOM, которые составляют элемент управления.

Я хотел бы иметь возможность сделать публичный метод, который работает что-то вроде:

$("#myCtrl").autoCompleteEx.addItem("1");

Но в реализации addItem () как я могу получить доступ к переменным-членам для этого конкретного объекта, например, его timerID или чего-то еще?

Вот то, что у меня есть ...

Спасибо за любую помощь или предложения!

(function($)
{     
    //Attach this new method to jQuery   
    $.fn.autoCompleteEx = function(options)
    {    
        //Merge Given Options W/ Defaults, But Don't Alter Either
        var opts = $.extend({}, $.fn.autoCompleteEx.defaults, options);


        //Iterate over the current set of matched elements   
        return this.each(function() 
        {
            var acx = $(this); //Get JQuery Version Of Element (Should Be Div)

            //Give Div Correct Class & Add <ul> w/ input item to it
            acx.addClass("autoCompleteEx"); 
            acx.html("<ul><li class=\"input\"><input type=\"text\"/></li></ul>");

            //Grab Input As JQ Object
            var input = $("input", acx);

            //Wireup Div
            acx.click(function()
            {
                input.focus().val( input.val() );
            });


            //Wireup Input
            input.keydown(function(e)
            {
                var kc = e.keyCode;
                if(kc == 13)   //Enter
                {

                }
                else if(kc == 27)  //Esc
                {

                }
                else
                {
                    //Resize TextArea To Input
                    var width = 50 + (_txtArea.val().length*10);
                    _txtArea.css("width", width+"px");    
                }
            });

        });   //End Each JQ Element

    }; //End autoCompleteEx()

    //Private Functions
    function junk()
    {

    };

    //Public Functions
    $.fn.autoCompleteEx.addItem = function(id,txt)
    {
        var x = this;
        var y = 0;
    };

    //Default Settings
    $.fn.autoCompleteEx.defaults =
    {
        minChars:  2,
        delay:     300,
        maxItems:  1
    };

    //End Of Closure
})(jQuery); 

Ответы [ 6 ]

7 голосов
/ 08 октября 2009

Я обнаружил, что способ обработки jQuery UI, похоже, работает лучше всего. Вы создаете свои «дополнительные методы» в качестве строкового аргумента для вашего плагина:

$('#elem').autoCompleteEx('addItem', '1');

Тогда контекст «this» сохраняется, и вы можете сделать что-то вроде этого:

function addItem() {
  // this should be == jquery object when this get called
}

$.fn.autoCompleteEx = function(options) {
   if (options === 'addItem') {
     return addItem.apply(this, Array.prototype.splice.call(arguments, 1));
   }
};
4 голосов
/ 08 октября 2009

Вот шаблон, с которым я экспериментирую при создании более сложных плагинов виджетов:

(function($){

  // configuration, private helper functions and variables
  var _defaultConfig = {
        /* ... config ... */
      },
      _version = 1;


  // the main controller constructor
  $.myplugin = function ( elm, config ) {

    // if this contructor wasn't newed, then new it...
    if ( this === window ) { return new $.myplugin( elm, config || {} ); }

    // store the basics
    this.item = $( elm );
    this.config = new $.myplugin.config( config );

    // don't rerun the plugin if it is already present
    if ( this.item.data( 'myplugin' ) ) { return; }

    // register this controlset with the element
    this.item.data( 'myplugin', this );

    // ... more init ...

  };
  $.myplugin.version = _version;
  $.myplugin.config = function ( c ) { $.extend( this, $.myplugin.config, c ); };
  $.myplugin.config.prototype = _defaultConfig;
  $.myplugin.prototype = {

    /* ... "public" methods ... */

  };

  // expose as a selector plugin
  $.fn.myplugin = function ( config ) {
    return this.each(function(){
      new $.myplugin( this, config );
    });
  };

})(jQuery);

Я поставил конфигурацию и версию по умолчанию вверху просто потому, что это наиболее вероятно вещь, которую каждый, кто читает код, ищет. Большую часть времени вы просто хотите изучить блок настроек.

Это выставит «myplugin» в двух местах как конструктор для «контроллера» виджета на $ и в качестве метода сбора на $.fn. Как видите, метод $.fn на самом деле ничего не делает, кроме создания новых контроллеров.

Конфиг - это прототипно унаследованный объект, в котором по умолчанию используется прототип. Это дает расширенную гибкость с начальными значениями, как вы можете присвойте «следующие» значения по умолчанию $.myplugin.config или измените значение по умолчанию для каждого работающего плагина с помощью $.myplugin.config.prototype. Это требует от вас всегда назначайте их с помощью $ .extend, иначе вы сломаете систему. Больше кода может противостоять этому, но я предпочитаю знать, что я делаю. : -)

Экземпляр контроллера привязывается к элементу с помощью метода data() jQuery и фактически использует его для проверки того, что он не запускается дважды на одном и том же элементе (хотя вы можете разрешить его реконфигурирование).

Это дает вам следующий интерфейс к контроллеру:

// init:
$( 'div#myid' ).myplugin();

// call extraMethod on the controller:
$( 'div#myid' ).data('myplugin').extraMethod();

Самым большим недостатком этого подхода является то, что немного сложно поддерживать контекст "this" при каждом назначении события. Пока контекст для событий не появится в jQuery, это должно быть сделано с большим количеством замыканий.

Вот примерный пример того, как (неполный и бесполезный) плагин может выглядеть:

(function($){

  // configuration, private helper functions and variables
  var _defaultConfig = {
        openOnHover: true,
        closeButton: '<a href="#">Close</a>',
        popup: '<div class="wrapper"></div>'
      },
      _version = 1;

  // the main controller constructor
  $.myplugin = function ( elm, config ) {

    // if this contructor wasn't newed, then new it...
    if ( this === window ) { return new $.myplugin( elm, config || {} ); }
    this.item = $( elm );
    this.config = new $.myplugin.config( config );
    if ( this.item.data( 'myplugin' ) ) { return; }
    this.item.data( 'myplugin', this );

    // register some events
    var ev = 'click' + ( this.config.openOnHover ) ? ' hover' : '';
    this.item.bind(ev, function (e) {
      $( this ).data( 'myplugin' ).openPopup();
    });

  };
  $.myplugin.version = _version;
  $.myplugin.config = function ( c ) { $.extend( this, $.myplugin.config, c ); };
  $.myplugin.config.prototype = _defaultConfig;
  $.myplugin.prototype = {

    openPopup: function () {
      var C = this.config;
      this.pop = $( C.popup ).insertAfter( this.item );
      this.pop.text( 'This says nothing' );
      var self = this;
      $( C.closeButton )
          .appendTo( pop )
          .bind('click', function () {
            self.closePopup();  // closure keeps context
            return false;
          });
      return this;  // chaining
    },

    closePopup: function () {
      this.pop.remove();
      this.pop = null;
      return this;  // chaining
    }

  };

  // expose as a selector plugin
  $.fn.myplugin = function ( config ) {
    return this.each(function(){
      new $.myplugin( this, config );
    });
  };

})(jQuery);
1 голос
/ 12 апреля 2010

Вот мое мнение:

У меня есть объект внутри замыкания, который используется для создания экземпляров объектов. Объекты экземпляра присоединяются к узлу элемента с помощью метода data () jQuery. Эти объекты экземпляра имеют открытые методы, которые вы можете вызывать по мере необходимости.

(function($)
{     
// This is used to create AutoComplete object that are attatched to each element that is matched
// when the plugin is invoked
var AutoCompleteEx = function(options, acx) {

  // PRIVATE VARIABLES
  var timerID;
  var input;

  //Give Div Correct Class & Add <ul> w/ input item to it
  acx.addClass("autoCompleteEx"); 
  acx.html("<ul><li class=\"input\"><input type=\"text\"/></li></ul>");

  //Grab Input As JQ Object
  input = $("input", acx);

  //Wireup Div
  acx.click(function()
  {
      input.focus().val( input.val() );
  });


  //Wireup Input
  input.keydown(function(e)
  {
      var kc = e.keyCode;
      if(kc == 13)   //Enter
      {

      }
      else if(kc == 27)  //Esc
      {

      }
      else
      {
          //Resize TextArea To Input
          var width = 50 + (_txtArea.val().length*10);
          _txtArea.css("width", width+"px");    
      }
  });

  // PUBLIC METHODS

  this.setTimerID = function(id) {
    timerID = id;
  };

  this.getTimerID = function() {
    return timerID;
  };

};


//Attach this new method to jQuery   
$.fn.autoCompleteEx = function(options)
{    
    //Merge Given Options W/ Defaults, But Don't Alter Either
    var opts = $.extend({}, $.fn.autoCompleteEx.defaults, options);

    //Iterate over the current set of matched elements   
    return this.each(function() 
    {
        var acx = $(this); //Get JQuery Version Of Element (Should Be Div)

        // creating a new AutoCompleteEx object and attach to the element's data, if not already attached
        if (!acx.data('autoCompleteEx')) {
          acx.data('autoCompleteEx', new AutoCompleteEx(options, acx));
        }

    });   //End Each JQ Element

}; //End autoCompleteEx()

//Default Settings
$.fn.autoCompleteEx.defaults =
{
    minChars:  2,
    delay:     300,
    maxItems:  1
};

//End Of Closure
})(jQuery);

Вы можете вызвать методы следующим образом:

$("div#someDiv").autoCompleteEx();
$("div#someDiv").data('autoCompleteEx').setTimerID(123);
var timerId = $("div").data('autoCompleteEx').getTimerID();
console.log(timerId); // outputs '123'

А если вы создаете более одного экземпляра:

$("div.someDiv").autoCompleteEx();
$("div.someDiv").eq(0).data('autoCompleteEx').setTimerID(123);
$("div.someDiv").eq(1).data('autoCompleteEx').setTimerID(124);
var firstTimerId = $("div").eq(0).data('autoCompleteEx').getTimerID();
var secondTimerId = $("div").eq(1).data('autoCompleteEx').getTimerID();
console.log(firstTimerId); // outputs '123'
console.log(secondTimerId); // outputs '124'
1 голос
/ 08 октября 2009

Используйте что-то вроде этого:

acx.data("acx-somename", datavalue);

Затем вы можете впоследствии получить его с помощью:

var datavalue = acx.data("acx-somename");
1 голос
/ 08 октября 2009

Взгляните на .data функциональность jQuery. Позволяет хранить пары ключ / значение на любом объекте.

0 голосов
/ 31 октября 2009

Управление экземплярами! Это то, что вы хотите, верно? Старая добрая мода, манипулирование экземплярами в реальном времени. Это то, что я тоже хотел. Я гуглил тот же вопрос и нигде не смог найти хорошего ответа (как те, что указаны выше), поэтому я понял это. Мне не нравится мое решение, потому что оно выглядит как обходной способ получения доступа к методам экземпляров и странно для потребителя jquery, но, во-первых, jQuery странно, но прекрасно. Я написал простой плагин для затухания изображений, но затем, когда мне нужно было сделать с ним больше, я хотел предоставить методы живому экземпляру, чтобы получить этот результат здесь -> пример , я сделал следующее:

var instanceAccessor = {};
var pluginOptions = {'accessor':instanceAccessor}
$('div').myPlugin(pluginOptions);

Затем внутри плагина я добавляю методы к этому, переданному в объекте 'accessor', поскольку он является объектом. Я выставляю методы внутри плагина так:

if (pluginOptions.accessor != null && typeof(pluginOptions.accessor) === 'object') {
   pluginOptions.accessor.exposedMethod = function (someParam) {
     // call some private function here and access private data here
   };
}

Затем потребитель этого плагина может вызывать метод или методы экземпляра в любое время во время выполнения, как мы делали до того, как jquery сделал это странным:

instanceAccessor.exposedMethod('somevalue');

Вы можете искать "тупое перекрестное затухание" в поиске плагина jquery, чтобы найти мой тупой плагин и увидеть код для себя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...