Направить события на объекте на другой объект - PullRequest
5 голосов
/ 19 сентября 2009

Я хочу направить / перенаправить все события, которые происходят на цели origTarget, на другой объект otherObject. Я хочу сделать что-то вроде этого:

/* Magic binding (doesn't work!). */
$(origTarget).bind('*', function(e) {
    $(otherObject).trigger(e);
});

Возможно ли это с помощью jQuery, не просматривая все возможные типы событий?

В этом примере должно появиться предупреждение «hello»:

var origTarget = { };
var otherObject = { };

/* Do magic binding here. */

$(otherObject).bind('alert', function() {
    alert('hello');
});

$(origTarget).trigger('alert');

1 Ответ

6 голосов
/ 19 сентября 2009

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

Сказав это, давайте попробуем найти вам решение

Немного покопавшись в вещах jQuery.event, я не вижу никакого "легкого" способа сделать это. Однако кажется, что события кэшируются в объекте с использованием jQuery.data(elem,'events'). Есть несколько вариантов для изучения

Копирование связанных событий из одного объекта в другой

Решение для копирования потенциальных событий, написанное и протестированное в jQuery 1.3.2:

(function($){ 
  $.fn.proxyEventsFrom = function(target) {
    // cache this for later
    var $target = $(target);

    return this.each(function() {
      // store a copy of the element to use in the event handler callback
      var elem = this; 

      // get a list of events that I subscribe to
      var events = $(this).data("events") || {};

      $.each(events, function(type, handlers) {
        // bind onto this event on the target
        $target.bind(type, function(event) {
          var evArgs = arguments;
          // store the element that event originally fired on in the event object.
          event.proxyFrom = this; 
          $.each(handlers, function(guid, handler) {
            var ret = handler.apply(elem, evArgs);
            // to behave like other event handlers.
            if( ret !== undefined ){
              event.result = ret;
              if ( ret === false ) {
                event.preventDefault();
                event.stopPropagation();
                return false; // stop our each loop too
              }
            }
          }); // end $.each(handlers, function(guid, handler) 
          return event.result;
        }); // end $target.bind(type, function(event) 
      });// end $.each(events, function(type, handlers) 
    });// end this.each(function() {});
  }; // end $.fn.proxyEventsFrom
})(jQuery);

$(someObject).proxyEventsFrom(targetObject);

Это работает путем чтения всех событий, которые someObject ожидает, и последующей привязки нового события к targetObject того же типа события, которое вызовет все обработчики событий на someObject. Обработчик события в someObject может выглядеть так:

$(someObject).bind('alert', function(e) {
  if (e.proxyFrom) { alert('Proxied Succesfully!'); }
  alert('test');
});
$(someObject).proxyEventsFrom(targetObject);
$(targetObject).trigger('alert');

Этот подход может легко сгенерировать рекурсивный цикл обработчиков событий, если вы перенаправили события чему-то, что проксируется обратно. Будьте осторожны.

Переопределение jQuery.event.trigger

Можно переопределить функцию запуска событий, чтобы выполнить то, что вы хотите, хотя я предлагаю против этого по нескольким причинам: переопределять вещи в библиотеках опасно. и эта функция будет вызываться только для использования .trigger(), очевидно - поэтому не применяется к стандартным событиям DOM.

  (function() { // wrap this in a closure so origTrigger can't be overwritten.
    var origTrigger = jQuery.event.trigger;
    jQuery.event.trigger = function(event, data, elem, bubbling)
    {
      if (elem === origTarget) {
        if (origTrigger.call(this, event, data, otherObject, bubbling) === false) return;
      } 
      return origTrigger.apply(this,arguments);
    }
  })();

Сделать события цели === событиями другого объекта

Если у origTarget никогда не будет собственных событий, вы можете скопировать объект событий по ссылке.

  // copy the events table by reference from otherObject "events" data 
  // (create if neccesary)
  $.data(origTarget, 'events', 
    $.data(otherObject,'events') || $.data(otherObject,'events',{}));

  // need to setup handler since jQuery isn't creating it for us
  // function source found on line 2465 of jquery-1.3.2.js

  var handle = $.data(origTarget, 'handle', function(){
    return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
      jQuery.event.handle.apply(arguments.callee.elem, arguments) :
      undefined;
  });
  handle.elem = origTarget;

Пользовательская функция 'handle'

Это было долгий путь к этому решению, внутренне все события jQuery передаются через функцию handle, которая хранится в $.data(elem, 'handle'). Поэтому, если вы переопределите эту функцию на своей цели, вы сможете передавать события другому объекту. Это решение предполагает, что у вас когда-либо есть только один дополнительный слушатель, однако его можно легко адаптировать, чтобы иметь более одного слушателя. Я думаю, что это лучший подход, который я придумала:

  jQuery.event.proxyEvents = function(fromObject, toObject) {
    // create an new handle function
    var newHandle = function(event) {
      // the test in the original handler returns undefined if jQuery.event.triggered
      if (jQuery.event.triggered) return undefined;
      // event should be safe - but just in case lets take a trick from
      // jQuery.event.handle
      event = arguments[0] = jQuery.event.fix(event || window.event);
      // if we have a "listener"
      if (arguments.callee.listener) {
        // set proxyFrom for access in bound event handlers
        event.proxyFrom = arguments.callee.elem;
        // call the root "handle" function on the listener
        jQuery.event.handle.apply(arguments.callee.listener, arguments);
        // if we got a result of false from the event callbacks - exit early
        if (event.result === false) return;
        // clean that proxy property out
        delete event.proxyFrom;
      }
      // this is all the basic 'handle' function does:
      jQuery.event.handle.apply(arguments.callee.elem, arguments);
    };
    // properties of the handler function
    newHandle.elem = fromObject;
    newHandle.listener = toObject;
    // store it
    jQuery.data(fromObject, 'handle', newHandle);
  };
  jQuery.event.proxyEvents(origTarget, otherObject);

Он даже работает с DOM Elements (хотя вам нужно передать элемент, а не объект jQuery)

  jQuery.event.proxyEvents($('span')[0], $('input')[0]);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...