11

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

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

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

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

[编辑]这是一个说明问题的jsFiddle(只需单击Add并尝试单击新创建的元素)。

4

5 回答 5

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将完成为每个当前(和未来)元素启用插件所需的所有工作。

于 2013-05-14T13:03:53.567 回答
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
于 2016-07-06T22:12:57.493 回答
1

您仍然可以实现自己的观察者方法,在这里扩展 append 方法:

;(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);

应该也可以在旧浏览器中工作,但是,观察者遇到了一些性能问题,即使你可以优化它。

看演示

于 2013-05-14T13:11:01.493 回答
1

我在插件中找到了一个依赖于该方法的解决方案。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 插件指南不是很有信心。

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

于 2013-05-14T12:38:46.290 回答
0

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

突变观察者

这里有一些很好的例子:

DOM 操作

于 2013-05-14T12:05:41.847 回答