Добавление функции обратного вызова jQuery при расширении библиотеки - PullRequest
3 голосов
/ 01 июля 2010

Короткая версия: Я расширяю объектную функцию jQuery, которая должна принимать 0-3 аргумента и передавать их как аргументы со второго по четвертый в jQuery.animate. За исключением того, что я хочу изолировать обратный вызов и добавить в конец другой вызов функции.


Я расширяю библиотеку jQuery обычным способом, через jQuery.fn.newfunction = .... Это пользовательская анимационная функция, в частности, расширяющая объект, чтобы его внешняя ширина, включая поля, равнялась внутренней ширине его родительского узла.

jQuery.fn.expand_to_parent_container = function() {
  var args = Array.prototype.slice.call(arguments);

  return this.each(function(){
    var parent = $(this).parent();
    parent.inner_width = parent.innerWidth();

    var inner_outer_differential = $(this).outerWidth(true) - $(this).width();

    // store original width for reversion later
    $(this).data('original_width', $(this).width());

    args.unshift({ width: parent.inner_width - inner_outer_differential });

    $.fn.animate.apply($(this), args );
  });
}

Достаточно просто. Я использую локальную переменную arguments, «приводя» ее к массиву и отменяя смещение первого аргумента, который будет помещен в слот properties .animate ( API здесь ). Следовательно, намерение состоит в том, чтобы позволить .expand_to_parent_container принимать аргументы duration, easing, callback, с обычным соглашением jQuery, делающим их необязательными в интуитивных шаблонах (например, если передается только функция, она становится обратным вызовом). Похоже, что jQuery.speed отвечает за выяснение этого беспорядка, но я бы предпочел не использовать его / вмешиваться в него напрямую, потому что он не выглядит таким же публичным / устаревшим, как другие функции jQuery, и потому что я Я бы предпочел просто делегировать задачу на .animate.

Вот моя функция возврата.

jQuery.fn.contract_to_original_size = function() {
  var args = Array.prototype.slice.call(arguments);

  var callback_with_width_reset = function(){
    callback();
    $(this).width('auto');
  }

  args.unshift({ width: $(this).data('original_width') });

  if($(this).data('original_width')) {
    $.fn.animate.apply($(this), args );
  } else {
    $(this).width('auto');
  }
}

Я четко обозначил желаемое поведение в присваивании var callback_with_width_reset ..., так как мне бы хотелось, чтобы свойство width было сброшено на auto, чтобы восстановить его ближе к исходному состоянию и восстановить после любого сумасшествия, которое может произойти в течение Ход множества анимаций. Однако я не знаю, как выделить функцию обратного вызова из arguments или args.

Ответы [ 2 ]

1 голос
/ 01 июля 2010

Есть несколько вещей, которые я бы хотел отметить очень быстро:

  • Если вы вызываете expand_to_parent_container для нескольких объектов одновременно, вы продолжаете отменять смещение {width: ...} объектов в argsи никогда не сдвигайте их обратно.
  • contract_to_original_size не использует this.each() и не return this, и будет страдать от одних и тех же проблем с объектами ширины без смещения снова и снова.
  • Вы можете убедиться, что ваш код устанавливает только original-width, если он еще не установлен, остановив одну из ваших проблем здесь (запуск раскрытия при расширении / что бы с ним не связывалось!)
  • Вы можете использовать $.map() в вашем массиве аргументов, чтобы довольно легко обнаружить и заменить функцию обратного вызова, если вы все еще чувствуете, что вам нужно.

Вот код, откорректированный aнемного:

jQuery.fn.expand_to_parent_container = function() {
  // instead of Array.prototype, we can grab slice from an empty array []
  var args = [].slice.call(arguments);

  return this.each(function(){
    var parent = $(this).parent();
    parent.inner_width = parent.innerWidth();

    var inner_outer_differential = $(this).outerWidth(true) - $(this).width();
    // store original width for reversion later
    if (!$(this).data('original_width'))
    {
      $(this).data('original_width', $(this).width());      
    }

    args.unshift({ width: parent.inner_width - inner_outer_differential });
    $.fn.animate.apply($(this), args );
    args.shift(); // push the width object back off in case we have multiples
  });
}

jQuery.fn.contract_to_original_size = function() {
  var args = Array.prototype.slice.call(arguments);
  var callback;

  var callback_with_width_reset = function(){
    if (callback) callback.apply(this,arguments);
    // do something else like:
    // $(this).width('auto');
    // you may want to $(this).data('original-width', null); here as well?
  }

  args = $.map(args, function(param) {
    // if the parameter is a function, replace it with our callback
    if ($.isFunction(param)) {
      // and save the original callback to be called.
      callback = param;
      return callback_with_width_reset;
    }
    // don't change the other params
    return param;
  });

  // we didn't find a callback already in the arguments
  if (!callback) { 
    args.push(callback_with_width_reset);
  }


  return this.each(function() {
    // rather than shift() the new value off after each loop
    // this is another technique you could use:

    // create another copy so we can mess with it and keep our template
    var localArgs = [].slice.call(args); 
    // also - storing $(this) is going to save a few function calls here:
    var $this = $(this);

    if($this.data('original_width')) {
      localArgs.unshift({ width: $this.data('original_width') });
      $.fn.animate.apply($this, localArgs );
    } else {
      $this.width('auto');
    }    
  });
}

Проверьте это в действии на jsFiddle

0 голосов
/ 01 июля 2010

Мое текущее решение, которое, я полагаю, я выберу, если не появится что-то лучшее, следующее:

var arguments_length = arguments.length;
for(var i = 0; i < arguments_length; i++) {
  if(typeof arguments[i] == 'function') {
    var temp = arguments[i];
    arguments[i] = function(){
      temp();
      $(this).width('auto');
    }
  }
}

В связанный момент возвращаемое значение Array.prototype.slice.call(arguments); не работает должным образом. Первоначально, вместо итерации по arguments, я установил для вызова этого прототипа присвоение args, а затем итерации по for(arg in args). Но было много неожиданного поведения. Думаю, пока что, я думаю.

Кажется, это работает, но есть два недостатка, которые не кажутся важными, но все же чувствуют неправильными.

  1. Относительно «глуп» в том, как он определяет, какой из аргументов является функцией обратного вызова.
  2. Мой сброс arguments[i] в решающей степени зависит от функции, не принимающей аргументов. К счастью, функция обратного вызова в .animate действительно не принимает аргументов, так что теперь это не так уж и сложно, но я подозреваю, что более гибкое расширение будет более разумным при обработке этого.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...