CodeMonkey CodeMonkey - 1 month ago 13
CSS Question

Twitter Bootstrap Tabs active class toggle not working

I am using Twitter bootstrap nav tabs and nav pills. This is my code:

<div class="Header2" style="background-color:#1E3848">
<div id="headerTab">
<ul class="nav nav-tabs">
<li class="active ans-tab"> <a href="http://myweb.com/">Home</a></li>
<li class="topTab"><a href="http://myweb.com/score/">Score</a></li>
<li class="topTab"><a href="http://myweb.com/leaderboards/">Leaderboards</a></li>
</ul>
</div>
<div id="subHeaderTabPlayer">
<ul class="nav nav-pills">
<li class="active"> <a href="http://myweb.com/">Top</a></li>
<li><a href="http://myweb.com/rules/" data-toggle="pill">Rules</a></li>
<li><a href="http://myweb.com/player/" data-toggle="pill">Player</a></li>
<li><a href="http://myweb.com/categories/" data-toggle="pill">Categories</a></li>
</ul>
</div>
</div>


Now if i add attribute
data-toggle="pill"
and
data-toggle="tab"
the tab with active class change to one which i have clicked. However, href is no longer working.
If i do not use these classes, href works but active class does not change and it always stays to the element that class was given at the load of page.

I even tried using jQuery to toggle class behavior and it does not work as well: jQuery code:

<script type="text/javascript" src="//code.jquery.com/jquery-1.9.1.js"></script>
<script type="text/javascript">
$(window).load(function(){
document.getElementById( 'subHeaderTabPlayer' ).style.display = 'none';
$('ul.nav-tabs li a').click(function (e) {
var activeTab= document.getElementByClass('active');
activeTab.removeAttribute("class");

$('ul.nav-tabs li.active').removeClass('active')
$(this).parent('li').addClass('active')
})
$('ul.nav-pills li a').click(function (e) {
$('ul.nav-pills li.active').removeClass('active')
$(this).parent('li').addClass('active')
})
$(".ans-tab").click(function() {
document.getElementById('subHeaderTabPlayer').style.visibility = 'visible';
document.getElementById('subHeaderTabPlayer' ).style.display = 'block';

});
$(".topTab").click(function() {
document.getElementById('subHeaderTabPlayer').style.visibility = 'collapse';
document.getElementById('subHeaderTabPlayer' ).style.display = 'none';
});
});



</script>

Answer

Bootstrap nav tabs and pills work with a JS plugin. The plugin is loaded on the page and takes care of the highlighting and the remembering of the currently active tab or pill. It disables the href attributes if they link to other pages, simply because when you click on a link to another page, a new page is loaded. The JS is then reloaded for this new page, and it does not know about which tab or pill is currently active. They are designed to work with single page apps or links to anchors on the same page, and they work fine for that purpose.

The jQuery approach fails for the same reason, but might be easier to explain.

$('ul.nav-pills li a').click(function (e) {
  $('ul.nav-pills li.active').removeClass('active')
  $(this).parent('li').addClass('active')
})

You click on a link, JS goes off and changes the class, only if it has enough time to do so before the next page is loaded, this next page is loaded because the href attribute on the link tells the browser to navigate to a new page. And the new page loads your JS again, it can't possibly remember the variables from the previous page, like which tab or pill is meant to be active.

If you are going to navigate to other pages, you might as well just set the active class on the right tab for the specific page that is being loaded.

If you really need to do this on the client side, you could do something like this, and load it on each page.

$(document).ready(function () {

  // Set BaseURL
  var baseURL = 'http://myweb.com/'

  // Get current URL and replace baseURL
  var href = window.location.href.replace(baseURL, '');

  // Remove trailing slash
  href = href.substr(-1) == '/' ? href.substr(0, href.length - 1) : href;

  // Get last part of current URL
  var page = href.substr(href.lastIndexOf('/') + 1);

  // Add trailing slash if not empty (empty means we're currently at baseURL)
  page = page != '' ? page + '/' : page;

  // Select link based on href attribute and set it's closest 'li' to 'active'. 
  // .siblings('.active').removeClass() is only needed if you have a default 'active li'.
  $('a[href="' + baseURL + page + '"]', '.nav li').closest('li').addClass('active').siblings('.active').removeClass();

});

It sets the active tab based on the last part of the URL, it's kind of specific to your scenario (and previously my scenario) though. You'd have to adapt it if there were file extensions or query parameters involved. Every page needs to at least have a link to itself on it. And it preferably has the exact same header with the same tabs and pills rendered on it as all the other pages.

Both of these last points again raise the thought in my mind "if you have to have this much control over what exactly is being rendered onto the page, you probably have access to the server side", and if that is the case, you might as well fix it there, as every link does a new request to the server anyway. And your solution wouldn't have to rely on some not so robust JavaScript to work.

So this is what I had going at one stage, you could improve it, or implement a solution where you post some data to the next page and read it with JavaScript there (which would probably be easier and more robust if you had file extensions or query strings to deal with).

Good luck with it! :)