Pixelastic

You can cut our wings but we will always remember what it was like to fly.

Posts tagged with "tabs"

The multi-tab admin problem

Today I had to update an important zone file at my registrar. Because it was quite important, and because I already did an almost identical job a few months ago, I decided to copy-paste the old work.

So I started by opening the domain A zone edit page. The  I opened a new tab, and loaded the domain B zone edit page.

I selected the domain B content and copied it in the domain A page. I had to update some values, mostly IP addresses, and then validate the page.

Instead of updating the domain A zones as I expected, it updated the domain B zones and left the domain A unchanged. Can you guess why ?

Session handling

My guess is that everytime I opened a new page, a Session was tracking my "current" domain, the one I was editing. So opening a new tab to a new domain changed my current domain from domain A to domain B.

Therefore, when I updated the domain A zone, the next page was assuming I was editing the domain B page. If I had opened the tabs in reverse order, it would have worked seamlessly.

Why doing that ?

I have encountered a similar behavior in my own apps. By using the cakePHP Security behavior, a special security token is generated for each form. Its integrity is checked whenever a form is posted, and verify that no field has been tampered (no mandatory field has been deleted, no new field has been added).

This is a great security component, but because a special key is stored on the Session side, it also means that whenever you open several tabs, only the latest can be correctly checked and all other one will get rejected.

It bugs me at first, because I used to open several tabs whenever I need to "batch add" a lot of items. In real life, I found that I was almost the only person to do that... So I finally accepted the multi-tab restriction.

Getting back to my registrar : I guess they did it that way because of security reasons too, but I found it particularly counter-intuitive in that case and that could have led to serious damages.

How to prevent a FOUC but still gracefully degrade the jQuery UI tabs

I finally managed to fix something that was bugging me for a very long time. I'm talking about the jQuery UI tabs.

They are pretty useful actually, but I always hated that FOUC they produce. For one split second, you'll see all your tabs content, then they'll be neatly re-arranged in tabs.

What I want are my tabs displayed correctly right away, on first load.

First, the markup

Here's a typical tabs markup :

<div class="tabs">
    <ul>
        <li><a href="#firstTab">First tab</a></li>
        <li><a href="#secondTab">Second tab</a></li>
    </ul>

    <div class="tabPanel" id="firstTab">
        First tab content
    </div>
   
    <div class="tabPanel" id="secondTab">
        Second tab content
    </div>
</div>

CSS to hide all tabs if Javascript is disabled

If your Javascript is disabled, so will jQuery UI. We will then hide the <ul> because it serves no purpose here. We will only show it if js is enabled

.tabs ul { display:none; }
.js .tabs ul { display:block; }

Applying jQuery UI tabs

By doing a $('.tabs').tabs(); jQuery UI will treat your <ul> as your tab menu and all your .tabPanel as their corresponding contents. It will hide all your panels, except for the first one. It does so by adding a .ui-tabs-panel class to every .tabPanel as well as a .ui-tabs-hide to every panel it hides.

Right now, you should add another CSS rule to hide the unused panels :

.ui-tabs-hide { display: none; }

But if you look at your page now, you'll see all your tabs content before they get hidden. That is the FOUC I was talking about. The jQuery UI documentation indicate that to remove it, you should directly add the .ui-tabs-hide class to panels you'll want to hide.

As also pointed in the doc, it will not gracefully degrade because users without Javascript won't even be able to see your other tabs. Also, it asks you to add server-side logic (HTML markup with jQuery specific classes) for something that should be handled entirely client-side.

Removing the FOUC while gracefully degrade

Ok, so what I did was writing two simple rules that will directly hide all unused panels while still displaying the active one, even before jQuery UI takes action.

.js .tabPanel + .tabPanel { display:none;}

That way, no FOUC, and users without Javascript still see all the content. Unfortunatly, if you now try clicking on your tabs, you'll see that nothing happens and you get stuck with your default panel always visible.

Fixing the jQuery UI tabs

As jQuery will add new classes to my elements, I'll just have to write more specific rules that use those classes. Here's the little piece of logic I came up with :

.js .tabPanel.ui-tabs-panel { display:block;}
.js .tabPanel.ui-tabs-panel.ui-tabs-hide { display:none;}

All the jQuery panels are shown, except for the one hidden by jQuery. All those rules being more and more specific, they will get applied once the tabs are activated but still override the previous display: declarations.

Conclusion

This is how I fixed an issue that was bugging me for years. Unfortunatly the CSS rules being dependent on the markup used, I haven't yet been able to write them in a global form that could be added to the main jQuery UI css files.