Steve B Steve B - 4 months ago 5
Javascript Question

jQuery plugin to apply also on dynamically created elements

I'm writing a jquery plugin that should handle extra information on links to specify open behavior.

For example, I want to supports markup like :


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



The first should open in a new window, the second in a modal dialog, the third should open with the native behavior (whatever target has been set on the tag).

I would like to create a plugin for this behavior as generic as possible. I've written by now:

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


This code allows me to call from any page something like:

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


This is working for statically generated tags. However, my applications may generate markup dynamically (most of time using jsRender, but this does not matters).

But because this behavior is set up once when the javascript file is loaded, it won't take dynamically generated objects.

What should I do to handle my requirement?


  1. I tried to use the
    on
    method to monitor load events, but this does not works :

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


    I understand this does not works because the "load" event does not bubbles

  2. I was thinking about modifying my plugin to put the "on" inside the plugin, but I don't like this idea because it introduces some out of scope behavior in the plugin

  3. I can also call the plugin each time a dynamic node is created, but it will also introduce dependencies into other parts. My plugin won't be as autonomous as I would like.



Does anyone has a suggestion to handle my requirement, keeping my plugin as isolated as possible?

[edit] This should works with IE8 and later (and ideally with other browsers)

[edit] here is a jsFiddle that illustrate the issue (just click on
Add
and try to click on the newly created element).

Answer

Plugins added to $.fn should only apply to the listed elements, and not any future elements.

You should concentrate on having your plugin provide the mechanism, e.g.:

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

and then allow the plugin user to register the event handler which triggers that mechanism, using event delegation if necessary:

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

The latter code could be put into the jQuery namespace too, as a utility function:

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

Such that just calling:

$.openmode();

will do all the work required to enable the plugin for every current (and future) .openmode element.

Comments