views:

214

answers:

1

Context: HTML widgets generated using a Django ModelForm and template, jQuery 1.3.2, JavaScript on IE8, Firefox 3.5 or Safari 4. Procedures: An ordinary JavaScript function with some jQuery inside, or jQuery Enlightenment, Cody Lindley, "Adding new functions to the jQuery namespace," p. 116.

I have a jQuery construct that is repeated several times with different variables and so has been begging to be turned into a function. Basically some of the widgets need to be enabled or disabled based on the values of other widgets. I wrote this function two ways. The first way, with jQuery code inside an ordinary JavaScript function, is as follows:

function enable_or_disable_by_selection(master_id, master_id_value, dependent_ids) {
    /*
     * The master_id argument is the id attribute string of the master select element in the form
     * "#id_select_element_name".
     * The master_id_value argument is the selection value, a string, that causes the dependent
     * elements to be enabled when it is selected. In all other cases, they are disabled.
     * The dependent_ids argument is an array of dependent id attribute strings, such as
     * ["#id_element_1", "#id_element_2", "#id_element_3"]
     */

    /* ON CHANGE OF master_id SELECTION ELEMENT: */
    $(master_id).change(function() {
        /* If master_id_value is chosen, enable inputs for elements in dependent_ids: */
        if ($(master_id).val() == master_id_value) {
            for (var i = 0; i < dependent_ids.length; i++) {
                $(dependent_ids[i]).removeAttr("disabled");
            }
        }
        /* Otherwise disable inputs for elements in dependent_ids: */
        else {
            for (var i = 0; i < dependent_ids.length; i++) {
                $(dependent_ids[i]).attr("disabled", true);
            }
        }
    });
}

This works. The second way, recommended by the very able Mr. Lindley, puts my new function in the jQuery namespace. His basic recommended construct may be seen here. This helps me "avoid creating global code that could potentially create conflicts." Here's the code for my function following these recommendations:

(function($){
    $.enable_or_disable_by_selection = function(master_id, master_id_value, dependent_ids){
    /*
     * The master_id argument is the id attribute string of the master select element in the form
     * "#id_select_element_name".
     * The master_id_value argument is the selection value, a string, that causes the dependent
     * elements to be enabled when it is selected. In all other cases, they are disabled.
     * The dependent_ids argument is an array of dependent id attribute strings, such as
     * ["#id_element_1", "#id_element_2", "#id_element_3"]
     */

    /* CHANGE OF master_id SELECTION ELEMENT: */
        $(master_id).change(function() {
            /* If master_id_value is chosen, enable inputs for elements in dependent_ids: */
            if ($(master_id).val() == master_id_value) {
                for (var i = 0; i < dependent_ids.length; i++) {
                    $(dependent_ids[i]).removeAttr("disabled");
                }
            }
            /* Otherwise disable inputs for elements in dependent_ids: */
            else {
                for (var i = 0; i < dependent_ids.length; i++) {
                    $(dependent_ids[i]).attr("disabled", true);
                }
            }
        });
    };
})(jQuery);

This works too. It's the same logic wrapped up in jQuery.

I know that jQuery makes use of anonymous functions and closures in order to maintain a hermetic namespace. See "Using (function(){})()" by John Resig, here. But, as I study these two snippets, I am having a hard time seeing the risk in the first, simpler version. Also, am I wrong in thinking that this second method is a bit slower? Please help me see the advantages in the second version. I want to know why this is done.

Useful observations not pertinent to the question are always welcome.

+1  A: 

1 - The risk on your first version comes with the fact that your function is global, for example, some other random library is added to your page, and it overrides your function, it will have disastrous effects with the rest of the API of your widget.

I think you want to create redistributable widgets, and you must be defensive with your code, keeping as much your of your library code private as possible and you should be very selective and careful when you introduce global objects.

2 - The second example is not more slower, you are simply adding a member to the jQuery object, the time of name resolution of the property is really insignificant.

I would recommend you to read about namespacing and library design:

CMS
Christian, thanks for your useful response. I had not been thinking about the fact that the *function* is global in the first case. I am not actually writing a library of redistributable widgets, but at least now I understand the motivation to put functions in the jQuery namespace. Kind regards to you.
Thomas B. Higgins