Tabstrip Design

Giovedì 26 Aprile, 2007

Per realizzare delle Tabstrip davvero accattivanti esiste una tecnica particolarmente indicata che consente di mediare tra difficoltà grafiche e codice Javascript. Vediamo prima di tutto come costruire un templete in Photoshop per le nostre Tabstrip:

Costruita un tabstrip simile a quella mostrata qui sopra in figura, usiamo il tool per la selezione delle area di taglio per generare la forma che avrà la nostra tabella HTML. L'uso della tabella, in questo caso, è particolarmente indicato - come vedremo, in quanto semplifica sia il design che il codice Javascript. Ritagliamo dunque la nostra Tabstrip nel modo seguente:

Nel nostro file Photoshop dobbiamo considerare tutte le combinazioni delle schede (tab). Abbiamo quindi la scheda iniziale selezionata (taglio 3), i punti intermedi (taglio 5 e taglio 7) e la scheda finale non selezionata (taglio 9) con - eventuale - bordo di rifinitura (taglio 10 - opzionale a seconda del design). Bastano poi 3 combinazioni per risolvere tutti i casi visivi. Come mostrato qui sotto:

Graficamente parlando potete disporre le schede anche in sovrapposizione e sfalsate, come mostrato qui sotto:

Questione di gusto e di necessità... tornando all'immagine completa, con le tre strisce Tabstrip per le combinazioni, le ultime due servono per risolvere i casi limite; tagliatele come mostrato qui sotto:

Il taglio 17 e il taglio 23 risolvono i due casi limite: prima tab non selezionata, ultima selezionata. Il taglio 19 è l'opposto del taglio 5 e del taglio 7, visti in precedenza. L'immagine completa che otteniamo e che possiamo salvare per futuri cambiamenti di design è la seguente:

Per non confonderci quando affronteremo la parte Javascript, il nome dei tagli è il seguente:

  • taglio 3 = tabLeftFirstOn
  • taglio 4 = tabStripeOn
  • taglio 5 = tabMiddleOnOff
  • taglio 6 = tabStripeOff
  • taglio 7 = tabMiddleOffOff
  • taglio 9 = tabRightLastOff
  • taglio 10 = topBg
  • taglio 17 = tabLeftFirstOff
  • taglio 19 = tabMiddleOffOn
  • taglio 23 = tabRightLastOn

Per realizzare tutto ciò ci serve adesso un codice HTML, un codice CSS e un codice Javascript come gestore dei click sulle schede tab. Il codice HTML è banale ma articolato. Quindi propongo una classe PHP in grado di generarlo ogni volta che ci serve. In sostanza si tratta di creare una tabella HTML racchiusa in determinati DIV con ID particolari che ci serviranno poi per l'esecuzione del codice Javascript e per racchiudere il design tramite i CSS. Il codice HTML generato - tanto per farsi un'idea - di un Tabstrip con quattro schede è il seguente:

HTML:
  1. <div id="cscoTabStrip">
  2.     <div id="jscoTabStrip_info">
  3.         <table width="100%" cellspacing="0" cellpadding="0" border="0">
  4.             <tr>
  5.                 <td><div class="tabLeftFirstOn"></div></td>
  6.                 <td nowrap="" class="tabStripeOn"><a id="jscoTS_tab1" onclick="_onTabStrip( this );" class="tabsLink" title="Caratteristiche">Caratteristiche</a></td>
  7.                 <td><div class="tabMiddleOnOff"></div></td>
  8.                 <td nowrap="" class="tabStripeOff"><a id="jscoTS_tab2" onclick="_onTabStrip( this );" class="tabsLink" title="Come funziona">Come funziona</a></td>
  9.                 <td><div class="tabMiddleOffOff"></div></td>
  10.                 <td nowrap="" class="tabStripeOff"><a id="jscoTS_tab3" onclick="_onTabStrip( this );" class="tabsLink" title="Interfaccia">Interfaccia</a></td>
  11.                 <td><div class="tabMiddleOffOff"></div></td>
  12.                 <td nowrap="" class="tabStripeOff"><a id="jscoTS_tab4" onclick="_onTabStrip( this );" class="tabsLink" title="Richiesta Iscrizione">Richiesta Iscrizione</a></td>
  13.                 <td><div class="tabRightLastOff"></div></td>
  14.                 <td width="100%" class="topBG"><img src="css/tabstrip/cornerRight.png" alt=""/></td>
  15.             </tr>
  16.         </table>
  17.     </div>
  18.     <div id="jscoTSC_jscoTS_tab1" class="tabStripContent">
  19.         <p>contenuto SCHEDA TAB 1</p>
  20.     </div>
  21.     <div id="jscoTSC_jscoTS_tab2" class="tabStripContentHidden">
  22.         <p>contenuto SCHEDA TAB 2</p>
  23.     </div>
  24.     <div id="jscoTSC_jscoTS_tab3" class="tabStripContentHidden">
  25.         <p>contenuto SCHEDA TAB 3</p>
  26.     </div>
  27.     <div id="jscoTSC_jscoTS_tab4" class="tabStripContentHidden">
  28.         <p>contenuto SCHEDA TAB 4</p>
  29.     </div>
  30. </div>

Il codice PHP che genera questo HTML è il seguente:

PHP:
  1. // ***********************************************************
  2. //
  3. // file   : tabstrip.php
  4. // Author      : Giovambattista Fazioli (www.undolog.com)
  5. // Web      : http://www.undolog.com
  6. // E-mail      : info (at) undolog (dot) (com)
  7. // Created    : 03/10/2005 10.07
  8. // Modified  : 06/11/2005 07.17
  9. //
  10. // PROPERTIES
  11. //  name    - (STRING)    - Nome (ID) della Window
  12. //  style  - (STRING)   - Stile aggiuntivi in linea
  13. //  tabs    - (ARRAY) - Elenco delle schede (tab)
  14. //
  15. // METHODS
  16. //  addTab()  - Aggiunge una scheda al TabStrip
  17. //  toString()    - Restituisce l'output HTML per la Window
  18. //  show()        - Visualizza l'output HTML per la Window
  19. //
  20. // NOTE
  21. //
  22. // EXAMPLES
  23. //
  24. // ***********************************************************
  25.  
  26. class CSCO_UI_TABSTRIP {
  27.  
  28.     // #public properties
  29.     var $name;
  30.     var $style;
  31.     var $tabs;
  32.     var $jsListener;
  33.  
  34.     // #private properties
  35.     var $strHeader;
  36.     var $strBody;
  37.  
  38.     // =====================================================
  39.     // CONSTRUCTOR:
  40.     // =====================================================
  41.     function CSCO_UI_TABSTRIP ( $name, $jsListener="" ) {
  42.         $this->name         = $name;
  43.         $this->jsListener   = $jsListener;
  44.         $this->tabs   = array();
  45.     }
  46.  
  47.     // =====================================================
  48.     // METHOD: addTab()
  49.     //
  50.     // DESCRIPTION
  51.     //  Aggiunge una scheda al TabStrip.
  52.     //
  53.     // EXAMPLES
  54.     //
  55.     // =====================================================
  56.     function addTab( $name, $label, $tooltip, $content, $selected=false ) {
  57.         $tab = array( "name"        => $name,
  58.                       "label"      => $label,
  59.                       "tooltip"  => $tooltip,
  60.                       "content"  => $content,
  61.                       "selected"    => $selected );
  62.         $this->tabs[] = $tab;          
  63.     }
  64.  
  65.     // =====================================================
  66.     // METHOD: toString()
  67.     //
  68.     // DESCRIPTION
  69.     //  Restituisce l'output HTML per la TabStrip.
  70.     //
  71.     // EXAMPLES
  72.     //
  73.     // =====================================================
  74.     function toString() {
  75.         //
  76.         $this->strHeader    = '<div id="cscoTabStrip" '.
  77.                               ( ($this->style != "")?'style="'.$this->style.'"':'' ).'>'.
  78.                               '<div '.
  79.                               'id="jscoTabStrip_'.$this->name.'">'.
  80.                               '<table style="border-collapse: collapse;" width="100%" border="0" cellspacing="0" cellpadding="0">'.
  81.                              '<tr>';
  82.         $strBody = "";
  83.         // FirstLeft
  84.         $classLeft = ( $this->tabs[0]["selected"] )?"tabLeftFirstOn":"tabLeftFirstOff";
  85.         $this->strHeader .= '<td><div class="'.$classLeft.'"></div></td>';        
  86.         for($i=0; $i <sizeof($this->tabs); $i++) {
  87.             //
  88.             $classBck = ( $this->tabs[$i]["selected"] )?"tabStripeOn":"tabStripeOff";
  89.             //
  90.             if( $i == ( sizeof($this->tabs) -1) ) {
  91.                 $classMiddle = ( $this->tabs[$i]["selected"] )?"tabRightLastOn":"tabRightLastOff";
  92.             } else {
  93.                 if( $this->tabs[$i]["selected"] ) {
  94.                     $classMiddle = "tabMiddleOnOff";
  95.                 } else {
  96.                     $classMiddle = ( $this->tabs[$i+1]["selected"] )?"tabMiddleOffOn":"tabMiddleOffOff";
  97.                 }
  98.             }
  99.             //
  100.             //
  101.             $this->strHeader .= '<td nowrap="nowrap" class="'.$classBck.'">'.
  102.                                 '<a title="'.$this->tabs[$i]["tooltip"].'" class="tabsLink" onclick="_onTabStrip( this '.( ($this->jsListener!="")?(",'".$this->jsListener."'"):"" ).' );" id="jscoTS_'.$this->tabs[$i]["name"].'">'.$this->tabs[$i]["label"].'</a>'.
  103.                                 '</td>'.
  104.                                 '<td><div class="'.$classMiddle.'"></div></td>';
  105.             //             
  106.             $class = ($this->tabs[$i]["selected"])?"tabStripContent":"tabStripContentHidden";
  107.             $strBody .= '<div class="'.$class.'" id="jscoTSC_jscoTS_'.$this->tabs[$i]["name"].'">'.$this->tabs[$i]["content"].'</div>';
  108.             //
  109.         }
  110.         // align foo
  111.         $this->strHeader .= '<td class="topBG" width="100%"></td>'.
  112.                             '</tr></table>';
  113.        
  114.         $this->strBody   =    '<div>'.$strBody.'</div>';
  115.         //
  116.         return( $this->strHeader.$this->strBody.'</div></div>');
  117.     }
  118.  
  119.     // =====================================================
  120.     // METHOD: show()
  121.     //
  122.     // DESCRIPTION
  123.     //  Visualizza l'output HTML per la TabStrip.
  124.     //
  125.     // EXAMPLES
  126.     //
  127.     // =====================================================
  128.     function show() {
  129.         echo ( $this->toString() );
  130.     }
  131.    
  132. } // END OF SCO_UI_TABSTRIP.PHP FILE

Per usare la classe basta questo frammento:

PHP:
  1. // File con la definizione della class
  2. require "tabstrip.php";
  3.  
  4. // Creo oggetto Tabstrip
  5. $tnew CSCO_UI_TABSTRIP( "info" );
  6.  
  7. // Creo / leggo da file - i contenuti delle schede
  8. $s1 = "Contenuto 1";
  9. $s2 = "Contenuto 2";
  10. $s3 = "Contenuto 3";
  11. $s4 = "Contenuto 4";
  12.  
  13. // Aggiungo schede 
  14. $t->addTab("tab1","Caratteristiche","Caratteristiche", $s1, true);
  15. $t->addTab("tab2","Come funziona","Come funziona", $s2);
  16. $t->addTab("tab3","Interfaccia","Interfaccia", $s3);
  17. $t->addTab("tab4","Richiesta Iscrizione","Richiesta Iscrizione", $s4);
  18.  
  19. // Mostro tutto - in alternativa usare toString() method
  20. $t->show();

Definiamo ora gli stili:

CSS:
  1. div#cscoTabStrip    {font-size:13px}
  2.  
  3. div#cscoTabStrip .tabStripeOff a.tabsLink,
  4. div#cscoTabStrip .tabStripeOff a.tabsLink:link,
  5. div#cscoTabStrip .tabStripeOff a.tabsLink:visited   {display:block;cursor:pointer;white-space:nowrap;color:#888;margin:0 4px 0 4px}
  6. div#cscoTabStrip .tabStripeOff a.tabsLink:hover  {color:#f70}
  7.  
  8. div#cscoTabStrip .tabStripeOn a.tabsLink,
  9. div#cscoTabStrip .tabStripeOn a.tabsLink:link,
  10. div#cscoTabStrip .tabStripeOn a.tabsLink:visited    {display:block;cursor:pointer;white-space:nowrap;color:#000;font-weight:bold;margin:0 4px 0 4px}
  11. div#cscoTabStrip .tabStripeOn a.tabsLink:hover  {}
  12.  
  13.  
  14. div#cscoTabStrip .tabLeftFirstOff   {display:block;width:20px;height:35px;background:url(tabstrip/tabLeftFirstOff.png) no-repeat;}
  15. div#cscoTabStrip .tabLeftFirstOn    {display:block;width:20px;height:35px;background:url(tabstrip/tabLeftFirstOn.png) no-repeat;}
  16.  
  17. div#cscoTabStrip .tabMiddleOffOff   {display:block;width:20px;height:35px;background:url(tabstrip/tabMiddleOffOff.png) no-repeat;}
  18. div#cscoTabStrip .tabMiddleOffOn    {display:block;width:20px;height:35px;background:url(tabstrip/tabMiddleOffOn.png) no-repeat;}   
  19. div#cscoTabStrip .tabMiddleOnOff    {display:block;width:20px;height:35px;background:url(tabstrip/tabMiddleOnOff.png) no-repeat;}   
  20. div#cscoTabStrip .tabStripeOff    {background:url(tabstrip/tabStripeOff.png) repeat-x;}
  21. div#cscoTabStrip .tabStripeOn      {background:url(tabstrip/tabStripeOn.png) repeat-x;}
  22.  
  23. div#cscoTabStrip .tabRightLastOff   {display:block;width:20px;height:35px;background:url(tabstrip/tabRightLastOff.png) no-repeat;}
  24. div#cscoTabStrip .tabRightLastOn    {display:block;width:20px;height:35px;background:url(tabstrip/tabRightLastOn.png) no-repeat;}
  25. div#cscoTabStrip .topBG    {text-align:right;background:url(tabstrip/topBg.png) repeat-x;}
  26.  
  27. div#cscoTabStrip .tabStripContent      {width:100%;padding:0;}
  28. div#cscoTabStrip .tabStripContentHidden {display:none;}

Il codice Javascript è assai blando, scritto in un'epoca lontata quando Prototype.js non esisteva. Inoltre nell'esempio qui sotto faccio uso di una libreria di effetti (non esisteva nemmeno Scriptaculous) ancora valida, quindi la riga che usa la transizione Opacity la potete sostituire con quello che volete:

JavaScript:
  1. function _onTabStrip(t) {
  2.     if( arguments.length> 1 ) {
  3.         var evt = "onBeforeClick";
  4.         var n   = t.id.substr(7);
  5.         var e = "var res = "+arguments[1]+"('"+n+"','"+evt+"');";
  6.         eval(e);
  7.         if(!res) return(false);
  8.     }
  9.     var tr = t.parentNode.parentNode;
  10.     for(var i = 1; i <tr.childNodes.length-2; i++ ) {
  11.         var td = tr.childNodes[i];
  12.         var el = td.childNodes[0];
  13.         //
  14.         switch( el.tagName ) {
  15.             case "DIV":
  16.                 el.className = "tabMiddleOffOff";
  17.                 break;
  18.             case "A":
  19.                 td.className = "tabStripeOff";
  20.                 break
  21.         }
  22.     }
  23.     var td = t.parentNode;
  24.     td.previousSibling.childNodes[0].className = "tabMiddleOffOn";
  25.     td.nextSibling.childNodes[0].className = "tabMiddleOnOff";
  26.     tr.childNodes[0].childNodes[0].className = ( tr.childNodes[1].childNodes[0] === t )?"tabLeftFirstOn":"tabLeftFirstOff";
  27.     var last = Number( (tr.childNodes.length-2) );
  28.     tr.childNodes[last].childNodes[0].className = ( tr.childNodes[last-1].childNodes[0] === t )?"tabRightLastOn":"tabRightLastOff";
  29.     t.parentNode.className = "tabStripeOn";
  30.     var mc  = $G( "jscoTSC_"+t.id );
  31.     var d = mc.parentNode;
  32.     for (var i = 0; i <d.childNodes.length; i++) {
  33.         d.childNodes[i].className = "tabStripContentHidden";
  34.     }
  35.     mc.className = "tabStripContent";
  36.     var to = new OpacityTween(mc,Tween.regularEaseOut, 0, 100, 1);
  37.     to.t = t; to.args = arguments;
  38.     to.onMotionFinished = function() {
  39.         if( this.args.length> 1 ) {
  40.             var evt = "onAfterClick";
  41.             var n   = this.t.id.substr(7);
  42.             var e = "var res = "+this.args[1]+"('"+n+"','"+evt+"');";
  43.             eval(e);
  44.         }
  45.     };
  46.     to.start();  
  47. }

In pratica le righe dalla 36 alle 46 possono essere sostituite con quello che volete, anche un display="none". Il codice è antiquato, almeno rispetto le nuove tecniche di Javascript non intrusivo (oggi lo scriverei in modo diverso). Tuttavia la parte che rimane interessante ed attuale e la configurazione della tabella HTML che gestisce i Tabstrip. Essa permette una resa grafica di alto livello, a differenza di alcuni altri metodi che - in sostanza - trattano le schede Tab come semplici DIV uno accanto all'altro.
Un modo ancora migliore sarebbe quello di sfruttare lo z-index (ordine di visualizzazione) presente negli stili CSS, così da gestire la sovrapposizione di elementi grafici. Purtroppo, ad oggi, è una tecnica assai difficile da implementare a causa delle differenze tra i browser che la rendono di fatto instabile, salvo rare circostanze.
Presto ne farò una versione più moderna... se avete dubbi o domande sono qui!

Post correlati

Lascia un commento