Tutorial # 1: jQuery-style Tabs and Nested Tabs

Table of contents

Tutorial # 1: jQuery-style Tabs and Nested Tabs
1: CSS for a HTML table, including colouring alternate lines
2: JS for jQuery, jQuery UI and DataTable
3: HTML for nested tabs
4: Perl code to populate the text on a tab
5: An extract from the complete web page
Traps
Making a (new) tab 'current', i.e. visible
Calling focus() on a CGI form field on the 'current' tab
Links

Tutorial # 1: jQuery-style Tabs and Nested Tabs

1: CSS for a HTML table, including colouring alternate lines

        <style type="text/css" title="currentStyle">
                @import "<: $demo_page_css_url :>";
                @import "<: $demo_table_css_url :>";
        </style>

        <link rel="stylesheet" type="text/css" href="<: $jquery_ui_css_url :>">

Notes:

# 1: This is part of web.page.tx. See below for details.

# 2: The syntax '<: $x :>' is a placeholder used by Text::Xslate. See below for the Perl code. Also see below for the output of the templating engine.

2: JS for jQuery, jQuery UI and DataTable

        <script type="text/javascript" src="<: $jquery_js_url :>"></script>
        <script type="text/javascript" src="<: $jquery_ui_js_url :>"></script>
        <script type="text/javascript" src="<: $datatable_js_url :>"></script>

Notes:

# 1: This is part of web.page.tx. See below for details.

3: HTML for nested tabs

        <div id="global_tabs">
            <ol>
                <li><a href="#global_search_tab"><span>Search</span></a></li>
                <li><a href="#global_add_tab"><span>Add</span></a></li>
                <li><a href="#global_report_tab"><span>Reports</span></a></li>
                <li><a href="#global_about_tab"><span>About</span></a></li>
            </ol>
            <div id="global_search_tab"><: $html4search :></div>
            <div id="global_add_tab">
                        <div id="add_tabs">
                            <ol>
                                <li><a href="#add_person_tab"><span>Add person</span></a></li>
                                <li><a href="#add_org_tab"><span>Add organization</span></a></li>
                            </ol>
                            <div id="add_person_tab"><: $html4add_person :></div>
                            <div id="add_org_tab"><: $html4add_organization :></div>
                        </div>
                </div>
            <div id="global_report_tab"><: $html4report :></div>
            <div id="global_about_tab"><: $html4about :></div>
        </div>

Notes:

# 1: This is part of web.page.tx. See below for details.

# 2: This gives us 4 tabs at the outer ('global') level, and 2 tabs within the outer 'Add' tab.

# 3: This code is all taken from App::Office::Contacts V 2.00, which I'm afraid is not yet released.

4: Perl code to populate the text on a tab

Step 1: Create a templating object:

        my($config) = $self -> param('config');

        $self -> param
        (
                templater => Text::Xslate -> new
                (
                        input_layer => '',
                        path        => $$config{template_path},
                )
        );

Step 2: Build the web page in your controller:

        my($config) = $self -> param('config');
        my($param)  =
        {
                datatable_js_url      => $$config{datatable_js_url},
                demo_page_css_url     => $$config{demo_page_css_url},
                demo_table_css_url    => $$config{demo_table_css_url},
                html4about            => mark_raw($self -> build_about_html),
                html4add_organization => mark_raw($self -> param('view') -> organization -> build_add_html),
                html4add_person       => mark_raw($self -> param('view') -> person -> build_add_html),
                html4report           => mark_raw($self -> param('view') -> report -> build_report_html),
                html4search           => mark_raw($self -> param('view') -> search -> build_search_html),
                jquery_js_url         => $$config{jquery_js_url},
                jquery_ui_css_url     => $$config{jquery_ui_css_url},
                jquery_ui_js_url      => $$config{jquery_ui_js_url},
                self_url              => $self -> query -> url,
                web_page_css_url      => $$config{web_page_css_url},
        };

        return $self -> param('templater') -> render('web.page.tx', $param);

5: An extract from the complete web page

        <html>

        <head>

        <title>Contacts</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

        <style type="text/css" title="currentStyle">
                @import "/assets/js/DataTables-1.9.4/media/css/demo_page.css";
                @import "/assets/js/DataTables-1.9.4/media/css/demo_table.css";
        </style>

        <link rel="stylesheet" type="text/css" href="/assets/js/jquery-ui-1.9.2.custom/css/smoothness/jquery-ui-1.9.2.custom.min.css">
        <link rel="stylesheet" type="text/css" href="/assets/css/app/office/contacts/web.page.css">

        </head>
        <body>

        <h3 class="blue_center">Contacts</h3>

        <div id="global_tabs">
            <ol>
                <li><a href="#global_search_tab"><span>Search</span></a></li>
                <li><a href="#global_add_tab"><span>Add</span></a></li>
                <li><a href="#global_about_tab"><span>About</span></a></li>
            </ol>
            <div id="global_search_tab"><form name="search_form" id="search_form">...</form><div id="search_result_div"></div></div>
            <div id="global_add_tab">
                        <div id="add_tabs">
                            <ol>
                                <li><a href="#add_person_tab"><span>Add person</span></a></li>
                                <li><a href="#add_org_tab"><span>Add organization</span></a></li>
                            </ol>
                            <div id="add_person_tab"><form id="add_person_form">...</form><div id="add_person_result_div"></div></div>
                            <div id="add_org_tab"><form id="add_org_form">...</form><div id="add_org_result_div"></div></div>
                        </div>
                </div>
            <div id="global_about_tab"><table align="center">
        <tr class="odd gradeC">
                <td>Program</td><td>App::Office::Contacts V 1.00</td>
        </tr>
        <tr class="even gradeC">
                <td>Author</td><td>Ron Savage</td>
        </tr>
        ...
        </table>
        </div>
        </div>

        </body>
        <script type="text/javascript" src="/assets/js/DataTables-1.9.4/media/js/jquery.js"></script>
        <script type="text/javascript" src="/assets/js/jquery-ui-1.9.2.custom/js/jquery-ui-1.9.2.custom.min.js"></script>
        <script type="text/javascript" src="/assets/js/DataTables-1.9.4/media/js/jquery.dataTables.min.js"></script>

        <script type="text/javascript">

        ...

        $(document).ready(function()
        {
                $("#global_tabs").tabs();
                $("#global_tabs").bind("tabsselect", function(event, ui)
                {
                        return true;
                });

                ...

        });

        </script>
        </html>

Notes:

I've used these to color alternate lines within a HTML table above:

        <tr class="odd gradeC">
        <tr class="even gradeC">

This is discussed in more depth in the 4th tutorial [HTML Tables](http://savage.net.au/jQuery-tutorials/html/html.tables.html).

Traps

As you know, jQuery is full of astonishing and infuriating traps.

Here are some connected with tabs.

Making a (new) tab 'current', i.e. visible

This works:

        var org_tab_index = 1;

        $("#global_tabs")
        .tabs("add", "#occ_detail_div", "Update occupations", org_tab_index)
        .tabs("select", org_tab_index);

This does not:

        $("#global_tabs")
        .tabs("add", "#occ_detail_div", "Update occupations", org_tab_index)
        .tabs("enable", org_tab_index);

'disable' and 'enable' make a tab (un)selectable.

After 'disable', the [UI docs](http://api.jqueryui.com/tabs/#method-disable) say 'The selected tab cannot be disabled', but what I think they mean is 'The disabled tab cannot be selected'.

Calling focus() on a CGI form field on the 'current' tab

When switching tabs, it's user-friendly to set the focus to the first CGI form field.

The problem is, when switching back and forth between tabs, the most obvious way of calling focus() does not work.

The solution is to call setTimeout(...), to cause a delay before calling focus().

The wrong way:

        $("#global_tabs").tabs("add", "#org_detail_div", "Update organization", org_tab_index);
        $("#org_detail_div").append(html); // This html was returned from an Ajax call.
        $("#global_tabs").tabs("select", org_tab_index);
        $("#update_org_name").focus();

The right way:

        $("#global_tabs")
        .tabs("add", "#org_detail_div", "Update organization", org_tab_index)
        .bind("tabsselect", function(){setTimeout(function(){$("#update_org_name").focus();}, 10)});
        $("#org_detail_div").append(html);
        $("#global_tabs").tabs("select", org_tab_index);

Links

My home page

All tutorials (Includes references)

POD source for this tutorial