Unobtrusive jQuery UI with AJAX support

Recently I came across a post by Joe Stagner that talked about the practice of “Unobtrusive JavaScript” in web pages.  Assuming that you understand the separation of concerns therein, I’m going to jump right to a jQuery plug-in authored by Damian Edwards that is available on NuGet, Unobtrusive jQuery UI.

The main idea is this: you can add markup to your html content that wires up jQuery UI without the need to write any additional javascript; just by virtue of including the plug-in script in your html, you can wire up jQuery UI widgets.

Normally, to wire up the following:

jQuery DatePicker

jQuery DatePicker

You would need to write this code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link href="Content/themes/base/minified/jquery-ui.min.css" rel="stylesheet" type="text/css" />
    <script src="Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery-ui-1.8.16.min.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            $('#date').datepicker({});
        });
    </script>
</head>
<body>
    <input type="text" name="date" id="date" />
</body>
</html>

The plug-in he wrote is fantastic in that it does the job with a minimal amount of effort.  But, it doesn’t address any content that was added AFTER the page has loaded via AJAX.  This is because his plugin wires everything up in jQuery’s document.ready event.  The other limitation to his code is that it did not correctly parse options for jQuery UI widgets that were camelCased.

So if there was an option like changeMonth, the attribute should be data-ui-datepicker-changeMonth.  However, the browser will render this as data-ui-datepicker-changemonth without the uppercased M.  This poses a problem in that JavaScript is a case-sensitive language and that option will be passed to the jQuery UI widget function like:

    <input type="text" name="date" id="date" data-ui-fn="datepicker" data-ui-changeMonth="true" />

will functionally get evaluated as:

    $('#date').datepicker({
        changemonth: true
    });

When in fact what we need is:

    $('#date').datepicker({
        changeMonth: true
    });

Check out his source:

/*!
 * Unobtrusive jQuery UI 0.1
 * Copyright 2011, Damian Edwards http://damianedwards.com
 * Licensed under Ms-PL
 * http://www.opensource.org/licenses/MS-PL
 */
(function ($) {
	"use strict";

    $(function () {
        var prefix = "data-ui-";

        // Wire-up jQuery UI unobtrusively
        $("*[" + prefix + "fn]").each(function () {
            var el = this,
                $el = $(el);

            // Loop through functions in data-ui-fn attribute
            $.each($el.attr(prefix + "fn").split(" "), function () {
                var fn = this,
                    options = {},
                    optionPrefix = prefix + fn + "-";

                // Build options parameter from data-ui-[fn]-* attributes
                $.each(el.attributes, function () {
                    var attr = this;
                    if (!attr.specified) return true;

                    if (attr.name.indexOf(optionPrefix) === 0) {
                        options[attr.name.substr(optionPrefix.length)] = attr.value;
                    }
                });

                // Call jQuery UI fn if it exists
                ($el[fn] || $.noop).call($el, options);
            });
        });
    });

}(window.jQuery));

Yesterday I proposed an update that would do the following:

  1. Add a method that allowed to parse the an element an all of it’s children in the hierarchy of the DOM
  2. Add a method that parses a specific element in the DOM
  3. Correct the parsing of the options for the jQuery UI widget function.  Fix camel casing.

Here is what I came up.  You should note, that I modeled this after the default jquery.validate.unobtrusive.js code that is in any default MVC project.

/*!
* Unobtrusive jQuery UI 0.1
* Copyright 2011, Damian Edwards http://damianedwards.com
* Licensed under Ms-PL
* http://www.opensource.org/licenses/MS-PL
*/

/*!
* Modified by Andrew Cohen, 11/30/2011
* TempWorks Software, Inc.
*/

(function ($) {
    "use strict";

    var $jQui = $.ui,
        prefix = "data-ui-";

    $jQui.unobtrusive = {
        parse: function (element) {
            // Wire-up jQuery UI unobtrusively
            $("*[" + prefix + "fn]", element).each(function () {
                $jQui.unobtrusive.parseElement(this);
            });
        },
        parseElement: function (el) {
            var $el = $(el),
                el = $el[0];

            // Loop through functions in data-ui-fn attribute
            $.each($el.attr(prefix + "fn").split(" "), function () {
                var fn = this,
                    options = {},
                    optionPrefix = prefix + fn + "-";

                // Build options parameter from data-ui-[fn]-* attributes
                $.each(el.attributes, function () {
                    var attr = this;
                    if (!attr.specified) return true;

                    if (attr.name.indexOf(optionPrefix) === 0) {
                        // camelCase the name
                        var attrName = $.camelCase(attr.name.substr(optionPrefix.length));
                        options[attrName] = attr.value;
                    }
                });

                // get UI fn if it exists
                var uiFn = ($el[fn] || $.noop);
                // call destroy to remove the ui widget
                uiFn.call($el, 'destroy');
                // call fn with options
                uiFn.call($el, options);
            });
        }
    }

    $(function () {
        $jQui.unobtrusive.parse(document);
    });

} (window.jQuery));

It also has 2 new methods:

$.ui.unobtrusive.parse

and

$.ui.unobtrusive.parseElement

The two new methods allow you to re-parse the DOM after content has been added to your page via AJAX.

I have submitted this change to Damian’s GitHub repository.

4 thoughts on “Unobtrusive jQuery UI with AJAX support

  1. Lionel

    Hello,
    very nice for a start :p. You saved me a huge amount of time.

    In fact i went into troubles when i wanted to add an autoopen option for the dialog box. if you try to camelcase autoopen it gives you autoopen and not autoOpen as stated in jquery ui documentation (http://jqueryui.com/demos/dialog/). It works fine with IE but both firefox and chrome minimizes the name of the attributes it finds. Do you have any work around? Otherwise, i think the only solution is to build a kind of a dictionnary.
    Thanks very much for you help.

    Reply
    1. Andy Cohen

      If I’m not mistaken, Damian pulled in my source code to his NuGet package and this is working. Try grabbing the latest from NuGet or GitHub and let me know if that works.

      Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s