views:

134

answers:

4

I have a page containing a set of jQuery tabs. All the tabs point at the same target div, but load it with different content via ajax. When I perform the initial full page load I need to set the active tab differently depending upon various factors. The content in the target div is already set on the server for this initial load, so I don't need to click the tab, I just need to set the correct tab to 'selected'. I have tried setting the class of the relevant "li" html element to "ui-tabs-selected". This has the initial desired effect, but once the page is loaded then on selecting another tab the preselected one does not switch off, leaving two tabs selected.

So, does anyone know an alternative way to preselect a tab (without causing it to be clicked), or a solution to the strange "ui-tabs-selected" behaviour that I am seeing.

Thanks.

<script type="text/javascript">
    $(function() {
        $("#panelTabs").tabs({
            ajaxOptions: {
                error: function(xhr, status, index, anchor) {
                    $(anchor.hash).html("Couldn't load this tab. We'll try to fix this as soon as possible.");
                }
            }
        });

        $("#panelTabs").tabs({
            select: function(event, ui) {
                getPage('hps.aspx?cmd=zz_' + ui.tab.id, 'panelA');
            }
        });
    });
    </script>

And the C# fragment that builds the UL:

StringBuilder tabsLiteral = new StringBuilder();
            tabsLiteral.Append("<ul>");
            foreach (KeyValuePair<string, Tab> kvp in tabs)
            {
                tabsLiteral.Append("<li>");
                // Note - the kvp.Value.URI determines what should happen when the Tab is clicked
                tabsLiteral.Append("<a id=\"" + kvp.Value.URI + "\" href=\"#panelTabs\">" + kvp.Value.Caption + "</a>");
                tabsLiteral.Append("</li>");
            }
            tabsLiteral.Append("</ul>");
            _panelTabs.Controls.Add(new LiteralControl(tabsLiteral.ToString()));

            HtmlGenericControl ctl = new HtmlGenericControl();
            StringBuilder html = new StringBuilder();
            html.Append("<script type=\"text/javascript\">");
            html.Append("$(\"#panelTabs\").tabs({selected: 2});");
            html.Append("</script>");
            ctl.InnerHtml = html.ToString();
            _panelTabs.Controls.Add(ctl);

Another version:

StringBuilder tabsLiteral = new StringBuilder();
            tabsLiteral.Append("<ul>");
            foreach (KeyValuePair<string, Tab> kvp in tabs)
            {
                string active = "";
                if (currentTabCaption == kvp.Value.Caption)
                {
                    //active = " class=\"ui-tabs-selected\"";
                }
                tabsLiteral.Append("<li" + active + ">");
                // Note - the kvp.Value.URI determines what should happen when the Tab is clicked
                tabsLiteral.Append("<a id=\"" + kvp.Value.URI + "\" href=\"#panelTabs\">" + kvp.Value.Caption + "</a>");
                tabsLiteral.Append("</li>");
            }
            tabsLiteral.Append("</ul>");
            _panelTabs.Controls.Add(new LiteralControl(tabsLiteral.ToString()));

            HtmlGenericControl ctl = new HtmlGenericControl();
            StringBuilder html = new StringBuilder();
            html.Append("<script type=\"text/javascript\">");
            //html.Append("$(\"#panelTabs\").tabs('option','selected', 2);"); 
            html.Append(@"$(function() {
                alert('initialising tabs');
                $(""#panelTabs"").tabs({
                    ajaxOptions: {
                        error: function(xhr, status, index, anchor) {
                            $(anchor.hash).html(""Couldn't load this tab. We'll try to fix this as soon as possible."");
                        }
                    },
                    select: function(event, ui) {
                        getPage('hps.aspx?cmd=zz_' + ui.tab.id, 'panelA');
                    }
                });
            });");
            html.Append("$(\"#panelTabs\").tabs({selected: 2});");
            html.Append("</script>");
            ctl.InnerHtml = html.ToString();
            //_panelTabs.Controls.Add(ctl);
            Page.Controls.Add(ctl);
+1  A: 

You could have the first line inside of your tab toggle function remove the selected class:

var ui-tabs-selected = .ui-tabs-selected;

$(ui-tabs-selected).removeClass(ui-tabs-selected);

ethagnawl
@ethagnawl thanks, sounds like a good idea - I tried pasting your two lines into the function but js is complaining about the first line (expecting ; at the first '-' char) - any ideas why?
DEH
@ethagnawl - thanks for pointing me in the right direction
DEH
+2  A: 

What you're looking for is the selected option. E.g.

$("#MyTabs").tabs({
  selected: 2 
});
Gert G
@Gert G - thanks, that works initially but then exhibits exactly the same behaviour as the "ui-tabs-selected" class being added to the li element i.e. on clicking another tab I end up with two tabs selected. On clicking again it sorts itself out and only one tab is shown as selected.
DEH
@DEH - I've never had problems with it. Can you post some code up in your question? Thanks.
Gert G
This should work. @DEH Post your markup and any relevant JS in your question.
TheJuice
@Gert G - I've just added a couple of code fragments to the initial post. The entire system is rather complex so I can't easily post an entire working example, but hopefully these fragments will allow you to see what I'm doing. I'm using a common div for the tabs called panelTabs, and this is dynamically added to the page by earlier C#. The javascript is just sitting at the top of the main page.
DEH
Thanks. It gives you some clues of how it's built. What happens if you add `$("#panelTabs").tabs({selected: ui.tab.id });` right after `getPage('hps.aspx?cmd=zz_' + ui.tab.id, 'panelA');` and remove `html.Append("$(\"#panelTabs\").tabs({selected: 2});");` from the C# code? A shot in the dark, but perhaps worth a try.
Gert G
@Gert G - no luck. It doesn't seem to do anything at all i.e. the first tab is selected as opposed to the one I want. Thanks for all your help though
DEH
@DEH - No problem. I'll see if I can recreate your problem locally. I'll be back later.
Gert G
@Gert G - ok, thanks a lot. I'm in the UK and it's 01:00am here so I'm off for some kip, but I'll check back in the morning.
DEH
@Gert - I've just posted another attempt in the initial post. I moved the main tabs function to the method that builds the UL, and I also tweaked the code to add the script to the main page, not the tabs control. Noticed something strange in testing - the C# can deliver the UL and script as part of a full page refresh, or it can operate in ajax mode where the delivered html is used to update a div rather than the whole page. It all works perfectly when the html and script is served up via ajax and is used to populate a div - the problem only appears when a full pge refresh occurs. Kip now!
DEH
I think what's happening is that you rewrite the tabs and it loses the event handling. Why not go for a more straight forward approach of having different panels and load the content into those panels?
Gert G
@Gert The single target div is very central to the UI architecture of the system. There are lots of different sets of tabs and each tab always loads content into the same target div. I'll have a fiddle with the event wireup and see if I can make any progress. Thanks for the advice
DEH
@Gert - solved it this morning, and posted the solution as another answer. I consider the solution to be a workaround because I think it should handle this situation. I hope you don't mind, but I have given the answer to ethnagawl as he pointed me in the right direction. Thanks very much for your assistance with it - it is a shame I can't accept two answers.
DEH
@DEH - No problem. I'm glad you found the solution.
Gert G
+1  A: 

Change this:

html.Append("$(\"#panelTabs\").tabs({selected: 2});");

To this:

html.Append("$(\"#panelTabs\").tabs('option','selected', 2);");

Edit: and...

$(function() {
        $("#panelTabs").tabs({
            ajaxOptions: {
                error: function(xhr, status, index, anchor) {
                    $(anchor.hash).html("Couldn't load this tab. We'll try to fix this as soon as possible.");
                }},
            select: function(event, ui) {
                getPage('hps.aspx?cmd=zz_' + ui.tab.id, 'panelA');
            }
        });    

    });
TheJuice
@TheJuice - in my test that does not select a tab at all, whereas the earlier version did select a tab but didn't 'release' it when another tab is clicked.
DEH
@DEH get rid of the html.append... line, add a hidden field to hold the value of your selected tab, then add:" ,selected: $('[id*="TabToSelect"]').val()" to your initialization script, set the value of TabToSelect in codebehind
TheJuice
@DEH I left off a } earlier. Also, if you try to select the tab after you initialize your tabs it will call your select event. here is a simple example: http://jsbin.com/anitu3
TheJuice
@TheJuice - thanks for your help - have to leave it now but will check it out again tomorrow
DEH
A: 

The solution was to add and remove classes, as ethagnawl suggested. The following C# shows both the UL being built, plus the required jQuery script injection. The important bit is in the show: function where the classes are being manipulated. It is this code that works around the problem.

StringBuilder tabsLiteral = new StringBuilder();
            tabsLiteral.Append("<ul>");
            foreach (KeyValuePair<string, Tab> kvp in tabs)
            {
                //string tabClass = " class=\"ui-state-default\"";
                string tabClass = " class=\"ui-state-default\"";
                if (currentTabCaption == kvp.Value.Caption)
                {
                    tabClass = " class=\"ui-tabs-selected\"";
                }
                tabsLiteral.Append("<li" + tabClass + ">");
                //tabsLiteral.Append("<li>");
                // Note - the kvp.Value.URI determines what should happen when the Tab is clicked
                tabsLiteral.Append("<a id=\"" + kvp.Value.URI + "\" href=\"#panelTabs\">" + kvp.Value.Caption + "</a>");
                tabsLiteral.Append("</li>");
            }
            tabsLiteral.Append("</ul>");
            _panelTabs.Controls.Add(new LiteralControl(tabsLiteral.ToString()));

            HtmlGenericControl ctl = new HtmlGenericControl();
            StringBuilder html = new StringBuilder();
            html.Append("<script type=\"text/javascript\">");
            //html.Append("$(\"#panelTabs\").tabs('option','selected', 2);"); 
            html.Append(@"$(function() {
                $('#panelTabs').tabs({
                    select: function(event, ui) {
                        getPage('hps.aspx?cmd=zz_' + ui.tab.id, 'panelA');
                    },
                    show: function(event, ui) {
                        // You MUST set both 'ui-state-active' AND 'ui-tabs-selected' for tab UI to operate properly - if either are missing it doesn't work
                        $('.ui-tabs-selected').removeClass('ui-state-active').removeClass('ui-tabs-selected'); 
                        $(ui.tab).addClass('ui-state-active').addClass('ui-tabs-selected'); 

                        //$('#panelTabs').tabs('selected',idx); // WOULD have been nice if this had worked, but UI does not keep up
                     }
                });
            });");
            html.Append("</script>");
            ctl.InnerHtml = html.ToString();
            //Page.Header.Controls.Add(ctl); // NEVER place script in the page header when the script must survive an AJAX delivery - it won't run
            Page.Controls.Add(ctl); // Acceptable to place script on the page, as it WILL survive AJAX delivery
DEH