2013-05-14 4 views
11

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

Например, я хочу поддерживает разметку, как:

  1. <a href="somewhere" data-openmode="NewWindow" class="openmode" />
  2. <a href="somewhere" data-openmode="Modal" class="openmode" />
  3. <a href="somewhere" class="openmode" /> <!-- Not specified -->

Первый должен открываться в новом окне, то второй в модальном диалоге , третье должно открыться с помощью собственного поведения (любая цель была установлена ​​в теге).

Я хотел бы создать плагин для такого поведения как можно более общего. Я написал сейчас:

(function ($) { 
    $.fn.myOpenMode = function() { 
     return this.mousedown(function() { 
      var $this = $(this); 

      var mode = $this.data("openmode"); 
      var href = this.href; 
      var handled = true; 
      if (mode) { 
       switch (mode) { 
        case "NewWindow": 
        case "1": 
         window.open(href, "_blank"); 
         break; 
        case "Dialog": 
        case "2": 
         openAsDialog(href); 
         break; 

        // Actually, there are other options, but I removed them for clarity 

        default: 
         handled = false; 
       } 
      } 
      else { 
       handled = false; 
      } 
      return !handled; 
     }); 
    }; 
})(jQuery); 

Этот код позволяет мне звонить из любой страницы что-то вроде:

$(function(){ 
    $(".openmode").myOpenMode(); 
}); 

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

Но поскольку это поведение настраивается один раз, когда загружается файл javascript, он не будет принимать динамически генерируемые объекты.

Что мне делать, чтобы удовлетворить мои требования?

  1. Я пытался использовать метод on контролировать событие загрузки, но это не работает:

    $(function(){ 
        $(document).on("load",".openmode", function() { $(this).myOpenMode(); }); 
    }); 
    

    Я понимаю, это не работает, потому что это событие «нагрузка» не пузырь

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

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

Есть ли у кого-нибудь предложения по работе с моим требованием, сохраняя мой плагин как можно более изолированным?

[править] Это должно работать с IE8, а затем (и в идеале с другими браузерами)

[править] вот jsFiddle, иллюстрирующая проблему (просто нажмите на Add и попробуйте нажать на вновь созданный элемент).

+3

.on() для обработчиков событий, а не плагин.Вы должны вызывать плагин каждый раз, когда создается динамический узел, или с помощью наблюдателей, чтобы проверить, когда в DOM добавлен новый конкретный элемент (.openmode) и закодируйте свою логику в соответствии с этим. Конечно, наблюдатели могут получить некоторые проблемы с производительностью. –

+0

внутри плагина вы не используете .on(). –

+0

@roasted: Я не поклонник использования 'on()' внутри плагина. Однако, насколько я понимаю мои поисковые запросы Google, наблюдатели не существуют с IE и являются убийцами производительности. –

ответ

8

Плагины, добавленные в $.fn, должны применяться только к перечисленным элементам, а не к будущим элементам.

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

(function($) { 

    $.fn.openmode = function(cmd) { 
     cmd = cmd || 'on'; 

     switch (cmd) { 

      case 'click': 
       // read props, open windows, etc 
       break; 

      case 'on': 
       this.addClass('openmode'); 
       break; 

      case 'off': 
       this.removeClass('openmode'); 
       break; 
     } 
    }); 

})(jQuery); 

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

$(document).on('click', 'a.openmode', function() { 
    $(this).openmode('click'); 
}); 

последний код можно поместить в пространство имен JQuery тоже, как функции полезности:

(function($) { 
    $.openmode = function(cmd) { 
     cmd = cmd || 'on'; 

     switch (cmd) { 

      case 'on': 
       $(document).on('click.openmode', 'a.openmode', function() { 
        $(this).openmode('click'); 
       }); 
       break; 

      case 'off': 
       $(document).off('click.openmode', 'a.openmode'); 
       break; 
     } 
    }; 
})(jQuery); 

Такие, что просто звонит:

$.openmode(); 

будет делать всю работу, необходимую для того, чтобы плагин для каждых текущего (и будущего) .openmode элемента.

+0

интересным подходом. Я дам ему попробовать –

+0

Это приятное решение IMO –

+0

Maye опечатка в коде (два 'case 'on':' clause) –

0

Вы можете использовать так называемый подход Mutation Observer, чтобы реагировать на изменения в DOM.Вот подробнее об этом:

MutationObserver

и вот некоторые хорошие примеры:

DOM manipulations

+0

Я не указал его изначально, но мне нужно настроить IE8 и позже. согласно предоставленной вами ссылке, это еще не поддерживается. –

1

Я нашел solution, которые полагаются на метод on(), в плагине.

Плагин в этом случае ожидания в selector аргумент по-прежнему держать селектор на уровне приложений (не уровень плагин):

(function ($) { 
    $.fn.myOpenMode = function (selector) { 
     return $(document).on("mousedown", selector ,function() { 
      var $this = $(this); 

      var mode = $this.data("openmode"); 
      var href = this.href; 
      var handled = true; 
      if (mode) { 
       switch (mode) { 
        case "NewWindow": 
         alert("NewWindow"); 
         break; 
        case "Dialog": 
         alert("Dialog"); 
         break; 
        default: 
         handled = false; 
       } 
      } else { 
       handled = false; 
      } 
      return !handled; 
     }); 
    }; 
})(jQuery); 

Тогда я изменить вызов в мой плагин, как это :

$(function(){ 
    $.fn.myOpenMode(".openmode"); 
}); 

Это является работает, как ожидалось. Однако я не очень уверен в соблюдении правил плагина jQuery.

Итак, если у кого-то есть лучшее решение, я буду рад узнать.

+0

вы никогда не должны вызывать функцию $ .fn прямо так. '$ .fn' - это свойства коллекций jQuery, а не автономные функции (которые должны быть в' $ ') – Alnitak

1

Вы все еще могли бы реализовать свой собственный метод наблюдателя, здесь простирающийся добавить метод:

;(function ($) { 
    var fctsToObserve = { 
     append: [$.fn.append, 'self'] 
    }, fctsObserveKeys = ''; 
    $.each(fctsToObserve, function (key, element) { 
     fctsObserveKeys += "hasChanged." + key + " "; 
    }); 
    var oOn = $.fn.on; 
    $.fn.on = function() { 
     if (arguments[0].indexOf('hasChanged') != -1) arguments[0] += " " + fctsObserveKeys; 
     return oOn.apply(this, arguments); 
    }; 
    $.fn.hasChanged = function (types, data, fn) { 
     return this.on(fctsObserveKeys, types, null, data, fn); 
    }; 
    $.extend($, { 
     observeMethods: function (namespace) { 
      var namespace = namespace ? "." + namespace : ""; 
      var _len = $.fn.length; 
      delete $.fn.length; 
      $.each(fctsToObserve, function (key) { 
       var _pre = this; 
       $.fn[key] = function() { 
        var target = _pre[1] === 'self' ? this : this.parent(), 
         ret = _pre[0].apply(this, arguments); 
        target.trigger("hasChanged." + key + namespace, arguments); 
        return ret; 
       }; 
      }); 
      $.fn.length = _len; 
     } 
    }); 
    $.observeMethods() 
})(jQuery); 

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

SEE DEMO

+0

IMHO нет необходимости в методе наблюдателя. Плагин OP только когда-либо делает что-либо _useful_ в ответ на пользовательское событие, а jQuery уже имеет превосходный механизм обработки событий на вновь добавленных узлах DOM. – Alnitak

+0

@Alnitak Im полностью согласен –

1

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

$.fn.plugin = function(){ 
    //some code 
} 
$(".element").plugin();   //works fine 
$(".elementParent").append(".newElement"); 
$(".newElement").plugin();   // doesn´t work 

Это не работает, потому что вам нужно вызвать плагин снова после того, как вы добавили элемент, как этот

function runPlugin(){ 
    $.fn.plugin = function(){ 
     //some code 
    } 
} 
runPlugin();      //call plugin here 
$(".element").plugin();   //works fine 
$(".elementParent").append(".newElement"); 

runPlugin();      // call plugin here 
$(".newElement").plugin();  // works fine 
+0

Я столкнулся с этой проблемой. Из вашего кода это означает, что вы добавили новую функцию «runPlugin» в JS-файл плагина? Я попробовал это, а затем, когда я вызываю runPlugin(); как вы это сделали, я получаю ошибку - runPlugin не определен. –

+0

Правильно. Подумайте об обертке функции Asi переменной. Каждый раз, когда вы вызываете его, вы вызываете весь его скрипт. Что касается вашей проблемы, я не уверен, работает ли она при использовании внешнего ja-файла для плагина. Тем не менее, вы можете попробовать: a) скопировать файл в документ или b) добавить $ (document) .append ("") перед вызовом плагина – edvilme

+0

Однако, если бы я был чтобы выбрать, я бы выбрал первый вариант, поскольку он более эффективен и не требует загрузки внешних файлов, что делает скрипт более быстрым и надежным. – edvilme