jQuery插件也适用于动态创建的元素

时间:2013-05-14 12:00:21

标签: jquery jquery-plugins

我正在编写一个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 -->
  4. 第一个应该在新窗口中打开,第二个应该在模式对话框中打开,第三个应该以本机行为打开(无论在标签上设置了什么目标)。

    我想为这种行为创建一个尽可能通用的插件。我现在写了:

    (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. 我正在考虑修改我的插件以在插件中放置“on”,但我不喜欢这个想法,因为它在插件中引入了一些超出范围的行为

    3. 我也可以在每次创建动态节点时调用插件,但它也会将依赖项引入其他部分。我的插件不会像我想的那样自主。

    4. 有没有人有建议来处理我的要求,让我的插件尽可能隔离?

      [edit] 这适用于IE8及更高版本(理想情况下适用于其他浏览器)

      [edit] 这里有一个jsFiddle来说明问题(只需点击Add并尝试点击新创建的元素。)

5 个答案:

答案 0 :(得分: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元素启用插件所需的所有工作。

答案 1 :(得分:2)

我迟到了回答,但希望它有所帮助。我有同样的问题。

$.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

答案 2 :(得分:1)

我在插件中找到了依赖on()方法的solution

在这种情况下,插件等待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插件指南并不十分自信。

因此,如果有人有更好的解决方案,我会很高兴知道。

答案 3 :(得分: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

答案 4 :(得分:0)

您可以使用所谓的Mutation Observer方法来响应DOM中的更改。以下是有关它的更多信息:

MutationObserver

以下是一些很好的例子:

DOM manipulations