locked
A gift to you all: An Atlas client-side tabstrip RRS feed

  • Question

  • User-2140226799 posted
    Well, Bertrand's Accordian example inspired me last night, so I came up with a client-side Atlas tabstrip.  It basically supplants the Microsoft tabstrip, which I have come to despise over the last few years :)  It works in both IE and Mozilla.  I don't really have anywhere to post this, so here it is:
     
    You can call this file something like TabSet.js

    Type.registerNamespace('Dice');

    // Tabset view
    Dice.Tabset = function(associatedElement) {
    	Dice.Tabset.initializeBase(this, [associatedElement]);
    
    	var _viewIndex = 0;
    	var _selectedTabStyle = '';
    
    	var _viewHeads = [];
    	var _viewPanes = [];
    
    	var _viewClickHandler;
    
    	this.get_viewIndex = function() {
    		return _viewIndex;
    	}
    
    	this.set_viewIndex = function(value) {
    		if (_viewIndex != value) {
    			_viewIndex = value;
    			_ShowCurrentPane.call(this);
    			this.raisePropertyChanged('viewIndex');
    		}
    	}
    
    	this.get_selectedTabStyle = function() {
    		return _selectedTabStyle;
    	}
    
    	this.set_selectedTabStyle = function(value) {
    		_selectedTabStyle = value;
    	}
    
    	this.dispose = function() {
    		if (_viewClickHandler) {
    			for (var i = _viewHeads.length - 1; i >= 0; i--) {
    				var head = _viewHeads[i];
    
    				if (head) {
    					head.detachEvent('onclick', _viewClickHandler);
    				}
    			}
    
    			delete _viewClickHandler;
    			delete _viewHeads;
    			delete _viewPanes;
    		}
    
    		Dice.Tabset.callBaseMethod(this, 'dispose');
    	}
    
    	Dice.Tabset.registerBaseMethod(this, 'dispose');
    
    	this.initialize = function() {
    		Dice.Tabset.callBaseMethod(this, 'initialize');
    
    		_viewClickHandler = Function.createDelegate(this, _onViewClick);
        
    		// Get tabstrip first.
    		var tabstrip;
    		var panes;
    		
    		var children = this.element.childNodes;
    		
    		for (var i = 0; i < children.length; i++) {
    			var child = children[i];
    			
    			if (child.nodeName == 'UL') {
    					tabstrip = child.childNodes;
    					break;
    			}
    		}
    		
    		for (var i = 0, p = 0; i < tabstrip.length; i++) {
    			var child = tabstrip[i];
    			
    			if (child.nodeName == 'LI') {
    					_viewHeads.add(child);
    					child.viewIndex = p++;
    					child.attachEvent('onclick', _viewClickHandler);
    			}
    		}
    		
    		for (var i = 0; i < children.length; i++) {
    			var child = children[i];
    
    			if (child.nodeName == 'DIV') {
    					_viewPanes.add(child);
    			}
    		}
    
    		_ShowCurrentPane.call(this);
    	}
    
    	Dice.Tabset.registerBaseMethod(this, 'initialize');
    
    	function _onViewClick() {
    		var pane = window.event.srcElement;
    		while (pane && (typeof(pane.viewIndex) == 'undefined')) pane = pane.parentNode;
    		this.set_viewIndex(pane.viewIndex);
    		return false;
    	}
    
    	function _ShowCurrentPane() {
    		for (var i = _viewPanes.length - 1; i >= 0; i--) {
    			var selectedTab = _viewHeads[i];
    			var pane = _viewPanes[i];
    			
    			if (i != _viewIndex) {
    					selectedTab.className = '';
    					pane.style.display = 'none';
    			} else {
    					selectedTab.className = _selectedTabStyle;
    					pane.style.display = 'block';
    			}
    		}
    	}
    
    	this.getDescriptor = function() {
    		var td = Dice.Tabset.callBaseMethod(this, 'getDescriptor');
        
    		td.addProperty('viewIndex', Number);
    		td.addProperty('selectedTabStyle', String);
    		return td;
    	}
    
    	Dice.Tabset.registerBaseMethod(this, 'getDescriptor');
    }
    
    Dice.Tabset.registerClass(Dice.Tabset', Sys.UI.Control);
    Sys.TypeDescriptor.addType(dice, 'tabset', Dice.Tabset);
    Make sure you add a reference to the file in your <atlas:scriptmanager> tag.
    Then, on the page where you want to use the control, add this:
    <script type="text/xml-script">
    <page xmlns:script="http://schemas.microsoft.com/xml-script/2005" xmlns:dice="dice">
    <components>
    	<dice:tabset id="productTabset" selectedTabStyle="selected"/>
    </components>
    </page>
    </script>
    
    Then, add your HTML for the tabset:
    <div id="productTabset">
    <ul>
    <li>Tab 1</li>
    <li>Tab 2</li>
    <li>Tab 3</li>
    <li>Tab 4</li>
    </ul>
    <div class="pane">
    First Tab
    </div>
    <div class="pane">
    Second Tab
    </div>
    <div class="pane">
    Third Tab
    </div>
    <div class="pane">
    Fourth Tab
    </div>
    </div> 
    

     The unordered list will become the tabs, and the divs will become the panes.

    You can style the tabs any way you want.  Here's a sample style sheet you can use:

    /* Tabset */
    
    div#productTabset ul
    {
    	display: block;
    	margin: 0;
    	padding: 0;
    	text-align: left;
    	width: 600px;
    }
    
    div#productTabset li
    {
    	background-color: #ece9d8;
    	border: solid 1px black;
    	border-bottom: none;
    	cursor: pointer;
    	display: block;
    	float: left;
    	font-weight: bold;
    	height: 15px;
    	list-style: none;
    	margin: 0 -1px 0 0;
    	padding: 3px;
    	position: relative;
    	width: 100px;
    	z-index: 97;
    }
    
    div#productTabset li.selected 
    {
    	background-color: White;
    	border-bottom: none;
    	position: relative;
    	z-index: 100;
    }
    
    div#productTabset .pane
    {
    	border: solid 1px black;
    	clear: both;
    	display: none;
    	overflow: auto;
    	padding: 5px;
    	position: relative;
    	width: 588px;
    	height: 300px;
    	top: -1px;
    	z-index: 99;
    }
    

     

    I'm not giving any kind of support for this control, so you're on your own.  It's a foundation you can customize to your own needs, and a nice way to learn the client-side stuff in Atlas.

    Have fun!

    Wednesday, March 29, 2006 5:36 PM

All replies

  • User-1880235900 posted

    I know there isn't any support but I have a little problem that someone might solve quickly. When I run the code I get the message: "Assertion Failed: Unrecognized tag dice:tabset".

    Maybe the tabset.js is inserted wrong. My scriptmanager has the code:

    <atlas:ScriptManager ID="ScriptManager1" runat="server" EnableScriptComponents=true >

    <Scripts><atlas:ScriptReference Path ="tabset.js" /></Scripts>

    </atlas:ScriptManager>

    The aspx-file and tabset.js is placed at the same directory.

    Sunday, June 25, 2006 7:14 AM
  • User-546811521 posted

    Try this,

    Dice.Tabset.registerClass('Dice.Tabset', Sys.UI.Control);

    Sys.TypeDescriptor.addType('dice', 'tabset', Dice.Tabset);

    Wednesday, July 5, 2006 7:57 AM
  • User186443736 posted

    OK I got it all working and it looks good, however can someone tell me how I can set the tab index on the page?  I can't seem to find a way to set that at runtime...

    Monday, October 2, 2006 11:25 AM
  • User-602078970 posted

    I tried your example in the latest AJAX V1.0 Beta and the code returns the following Javascript error:

    Sys.TypeDescriptor is null or not an object

    Friday, November 3, 2006 5:57 PM
  • User-602078970 posted

    I did the following modifications to the code:

    1) took out the closures and followed added prototype members.

    2) remove the TypeDescriptor and getDescriptor functions.

    3) In the xml-script added the reference... but it is still not working.

    I am posting the modified broken code, hopefull the community can fix it again to the new Ajax 1.0

    _________________________________

    Type.registerNamespace('Dice');// Tabset view

    Dice.Tabset = function(element) {

    // Ctor creates private fields and calls base.

    Dice.Tabset.initializeBase(this, [element]);

    this._viewIndex = 0;

    this._selectedTabStyle = '';

    this._viewHeads = [];

    this._viewPanes = [];

    this._viewClickHandler = null;

    }

    // Prototype: members

    Dice.Tabset.prototype = {

    initialize : function() {

    Dice.Tabset.callBaseMethod(this, 'initialize');

    // Create the OnClick handler

    this._viewClickHandler = Function.createDelegate(this, this._onViewClick);

     

    // Get tabstrip first.

    var tabstrip;

    var panes;

     

    var children = this.element.childNodes;

     

    for (var i = 0; i < children.length; i++) {

    var child = children[i];

     

    if (child.nodeName == 'UL') {

    tabstrip = child.childNodes;

    break;

    }

    }

     

    for (var i = 0, p = 0; i < tabstrip.length; i++) {

    var child = tabstrip[i];

     

    if (child.nodeName == 'LI') {

    this._viewHeads.add(child);

    child.SelectedIndex = p++;

    $addHandler(child, "click", this._viewClickHandler);

    }

    }

     

     

    for (var i = 0; i < children.length; i++) {

    var child = children[i];

    if (child.nodeName == 'DIV') {

    this._viewPanes.add(child);

    }

    }

    this._ShowCurrentPane.call(this);

    },

     

    dispose : function() {

    // Remove all the event handlers

    if (this._viewClickHandler) {

    for (var i = this._viewHeads.length - 1; i >= 0; i--) {

    var header = this._viewHeads[i];

    if (header) {

    $removeHandler(header, "click", this._viewClickHandler);

    }

    }

    this._viewClickHandler = null;

    this._viewHeads = null;

    this._viewPanes = null;

    }

    Dice.Tabset.callBaseMethod(this, 'dispose');

     

    },

     

    // SelectedIndex property accessors

    get_SelectedIndex: function() {

    return this._viewIndex;

    },

     

    set_SelectedIndex: function(value) {

    if (this._viewIndex != value) {

    this._viewIndex = value;

    this._ShowCurrentPane.call(this);

    this.raisePropertyChanged('SelectedIndex');

    }

    },

    get_selectedTabStyle: function() {

    return this._selectedTabStyle;

    },

    set_selectedTabStyle: function(value) {

    this._selectedTabStyle = value;

    },

     

    _onViewClick : function() {

    var pane = window.event.srcElement;

    while (pane && (typeof(pane.SelectedIndex) == 'undefined')) pane = pane.parentNode;

    this.set_SelectedIndex(pane.SelectedIndex);

    return false;

    },

     

    _ShowCurrentPane : function() {

    for (var i = _viewPanes.length - 1; i >= 0; i--) {

    var selectedTab = _viewHeads[i];

    var pane = _viewPanes[i];

     

    if (i != _viewIndex) {

    selectedTab.className = '';

    pane.style.display = 'none';

    } else {

    selectedTab.className = _selectedTabStyle;

    pane.style.display = 'block';

    }

    }

    }

    }

    Dice.Tabset.registerClass('Dice.Tabset', Sys.UI.Control);

    ____________________

    Here is the XML-script on the asp.net page:

    <script type="text/xml-script">

    <page xmlns:script="http://schemas.microsoft.com/xml-script/2005"

    xmlns:"JavaScript: Sys.UI,Sys,Dice">

    <components>

    <Dice:Tabset id="productTabset" selectedTabStyle="selected"/>

    </components>

    </page>

    </script>

    Monday, November 6, 2006 6:05 PM
  • User1145441399 posted

    I made it working under AJAX 1.0 Beta 2.

    The steps 1 (took out the closures and followed added prototype members) and 2 (remove the TypeDescriptor and getDescriptor functions) are OK. The step 3 (xml-script) is wrong, because xml-script So, I had to rewrite it to JavaScript . Additionally the Array class extension was changed and "Array.add" method call had to be changed.

    notifyScriptLoaded is added at the end of file to be loaded by ScriptManager. Not necessary if linked by standard <script ...> tag.

    ------------------------------------------------------------------------------------------------------------ 

    // JScript File

     

    // Tabset view

    Type.registerNamespace('Dice');

    Dice.Tabset = function(associatedElement) {

    Dice.Tabset.initializeBase(this, [associatedElement]);

    this._viewIndex = 0;

    this._selectedTabStyle = '';

    this._viewHeads = [];

    this._viewPanes = [];

    this._viewClickHandler;

    }

    Dice.Tabset.prototype = {

    get_viewIndex: function() {

    return this._viewIndex;

    },

    set_viewIndex: function(value) {

    if (this._viewIndex != value) {

    this._viewIndex = value;

    this._ShowCurrentPane.call(this);

    this.raisePropertyChanged('viewIndex');

    }

    },

    get_selectedTabStyle: function() {

    return this._selectedTabStyle;

    } ,

    set_selectedTabStyle: function(value) {

    this._selectedTabStyle = value;

    } ,

    dispose: function() {

    if (this._viewClickHandler) {

    for (var i = this._viewHeads.length - 1; i >= 0; i--) {

    var head = this._viewHeads[i];

     

    if (head) {

    head.detachEvent('onclick', this._viewClickHandler);

    }

    }

    this._viewClickHandler = null;

    this._viewHeads = null;

    this._viewPanes = null;

    }

    Dice.Tabset.callBaseMethod(this, 'dispose');

    },

    initialize: function() {

    Dice.Tabset.callBaseMethod(this, 'initialize');

    this._viewClickHandler = Function.createDelegate(this, this._onViewClick);

    // Get tabstrip first.

    var tabstrip;

    var panes;

    var children = this.get_element().childNodes;

    for (var i = 0; i < children.length; i++) {

    var child = children[i];

    if (child.nodeName == 'UL') {

    tabstrip = child.childNodes;

    break;

    }

    }

    for (var i = 0, p = 0; i < tabstrip.length; i++) {

    var child = tabstrip[i];

    if (child.nodeName == 'LI') {

    //this._viewHeads.add(child);

    Array.add(this._viewHeads,child);

    child.viewIndex = p++;

    child.attachEvent('onclick', this._viewClickHandler);

    }

    }

    for (var i = 0; i < children.length; i++) {

    var child = children[i];

    if (child.nodeName == 'DIV') {

    Array.add(this._viewPanes,child);

    }

    }

    this._ShowCurrentPane.call(this);

    } ,

    _onViewClick: function() {

    var pane = window.event.srcElement;

    while (pane && (typeof(pane.viewIndex) == 'undefined')) {

    pane = pane.parentNode;

    }

    this.set_viewIndex(pane.viewIndex);

    return false;

    } ,

    _ShowCurrentPane: function() {

    for (var i = this._viewPanes.length - 1; i >= 0; i--) {

    var selectedTab = this._viewHeads[i];

    var pane = this._viewPanes[i];

    if (i != this._viewIndex) {

    selectedTab.className = '';

    pane.style.display = 'none';

    } else {

    selectedTab.className = this._selectedTabStyle;

    pane.style.display = 'block';

    }

    }

    } ,

    getDescriptor: function() {

    var td = Dice.Tabset.callBaseMethod(this, 'getDescriptor');

    td.addProperty('viewIndex', Number);

    td.addProperty('selectedTabStyle', String);

    return td;

    }

    }

    Dice.Tabset.inheritsFrom(Sys.UI.Control);

    Dice.Tabset.registerClass('Dice.Tabset', Sys.UI.Control);

    if (Sys && Sys.Application) { Sys.Application.notifyScriptLoaded(); }

     

    -----------------------------------------------------------------------------------------

    Replacement of xml-script in asp.net page:

    <script type="text/javascript">

    function pageLoad() {

    $create(Dice.Tabset, {"selectedTabStyle":"selected"}, null, null, document.getElementById('productTabset'));

    }

    </script>

     

    Thursday, November 16, 2006 7:45 AM
  • User791036584 posted

    Great job valasekj!  You saved me a bunch of conversion work.  Thanks.

    Nice client-side TabStrip from Shazam999

    It is beyond me why this, or something similar, is not in the Control Toolkit...

    Friday, November 24, 2006 5:44 PM
  • User-645803374 posted

    Hey everyone. Here's my small contribution to this great control! I noticed that this control didn't work correctly in Firefox - now it should (only tested in Firefox 2 though). Thanks to Shazam999 & valasekj for all of their previous work! I left the original code, but commented out the line to notate the change that was required for it to work in Firefox.

     

    // ASP.NET AJAX Tabset View
    // Created by Shazam999
    // Beta 2 compatibility by valasekj

    // Last Modified: 12/13/06 - Firefox compatibility
    Type.registerNamespace('Dice');
    Dice.Tabset = function(associatedElement) {
    Dice.Tabset.initializeBase(this, [associatedElement]);
    this._viewIndex = 0;
    this._selectedTabStyle = '';
    this._viewHeads = [];
    this._viewPanes = [];
    this._viewClickHandler;
    }

    Dice.Tabset.prototype = {
    get_viewIndex:function() {
    return this._viewIndex;
    },

    set_viewIndex:function(value) {
    if (this._viewIndex != value) {
    this._viewIndex = value;
    this._ShowCurrentPane.call(this);
    this.raisePropertyChanged('viewIndex');
    }
    },

    get_selectedTabStyle:function() {
    return this._selectedTabStyle;
    } ,

    set_selectedTabStyle:function(value) {
    this._selectedTabStyle = value;
    } ,

    dispose:function() {
    if (this._viewClickHandler) {
    for (var i = this._viewHeads.length - 1; i >= 0; i--) {
    var head = this._viewHeads[i];
    if (head) {
    //head.detachEvent('onclick', this._viewClickHandler); //Changed to below for Firefox compatibility $removeHandler(head,'click', this._viewClickHandler);
    }
    }

    this._viewClickHandler = null;
    this._viewHeads = null;
    this._viewPanes = null;
    }

    Dice.Tabset.callBaseMethod(this, 'dispose');
    },

    initialize:function() {
    Dice.Tabset.callBaseMethod(this, 'initialize');
    this._viewClickHandler = Function.createDelegate(this, this._onViewClick);

    // Get tabstrip first. var tabstrip; var panes; var children = this.get_element().childNodes;
    for (var i = 0; i < children.length; i++) {
    var child = children[i];
    if (child.nodeName == 'UL') {
    tabstrip = child.childNodes;
    break;
    }
    }

    for (var i = 0, p = 0; i < tabstrip.length; i++) {
    var child = tabstrip[i];
    if (child.nodeName == 'LI') {
    //this._viewHeads.add(child); Array.add(this._viewHeads,child);
    child.viewIndex = p++;
    //child.attachEvent('onclick', this._viewClickHandler); //Changed to below for Firefox compatibility $addHandler(child,'click', this._viewClickHandler);
    }
    }

    for (var i = 0; i < children.length; i++) {
    var child = children[i];
    if (child.nodeName == 'DIV') {
    Array.add(this._viewPanes,child);
    }
    }

    this._ShowCurrentPane.call(this);
    } ,

    _onViewClick:function(e) {
    //var pane = window.event.srcElement; //Changed to below for Firefox compatibility var pane = window.event ? window.event.srcElement : e ? e.target : null;
    while (pane && (typeof(pane.viewIndex) == 'undefined')) {
    pane = pane.parentNode;
    }

    this.set_viewIndex(pane.viewIndex);
    return false;
    } ,

    _ShowCurrentPane:function() {
    for (var i = this._viewPanes.length - 1; i >= 0; i--) {
    var selectedTab = this._viewHeads[i];
    var pane = this._viewPanes[i];
    if (i != this._viewIndex) {
    selectedTab.className = '';
    pane.style.display = 'none';
    }else{
    selectedTab.className = this._selectedTabStyle;
    pane.style.display = 'block';
    }
    }
    } ,

    getDescriptor:function() {
    var td = Dice.Tabset.callBaseMethod(this, 'getDescriptor');
    td.addProperty('viewIndex', Number);
    td.addProperty('selectedTabStyle', String);
    return td;
    }
    }

    Dice.Tabset.inheritsFrom(Sys.UI.Control);
    Dice.Tabset.registerClass('Dice.Tabset', Sys.UI.Control);
    if (Sys && Sys.Application) { Sys.Application.notifyScriptLoaded(); }
      
    Wednesday, December 13, 2006 10:49 AM