When screen size made smaller bootstrap tabs are overlapping
Asked Answered
H

7

13

When screen size made smaller bootstrap tabs are overlapping like image. enter image description here

I used col-sm and my angular HTML is like this (but any non-angular HTML solution is fine too):

<div class="col-sm-6"> <tabset> <tab>
    <tab-heading active="true">
        Tab1
        <toggle-switch ></toggle-switch>
    </tab-heading>
...
</tab>
<tab>
    <tab-heading>Tab2
        <toggle-switch ></toggle-switch></tab-heading>
    ...
</tab>
<tab>
    <tab-heading>Tab3
        <toggle-switch ></toggle-switch></tab-heading>
    ...
</tab></tabset></div>

I would like for the other tabs to stack in the background instead of the foreground.

desired screenshot

Please forgive my terrible gimp skills, but the active tab should always be up front, so if tab 3 is selected, then the other row should shift to the back.

desired active

It doesn't have to be arranged specifically like this, but the point is that the active tab should not have anything in between it and its content pane, so something like this is also fine:

enter image description here

Hamrick answered 17/5, 2016 at 11:7 Comment(12)
ok, so... they wrap... what did you want them to do instead?Threesome
Hi @KevinB I added a screenshot with one possible solutionDice
toggle-switch......Are you using bootstrap-switch.js?Vaso
@ncd275 it looks like it in addition to angular-js, but I'd be happy with an answer just for the bootstrap tabs.Dice
Look at this jsfiddle.net/obliviga/gpavt9h0 this turns the content into a collapse componentEvvie
@Evvie that's an interesting workaround, thanks! not exactly what I was looking for though.Dice
Not sure what you mean by having the active tab 'up front'? Can you clarify?Underthecounter
have you tried nav justified ? getbootstrap.com/components/#nav-justifiedRoslynrosmarin
@Underthecounter notice in screenshot #2 that the "active" tab is in the back. this is undesirable because there are now other tabs separating the active tab from its content pane.Dice
@IsmailFarooq adding nav-justified does look a little better, but it doesn't solve the separation problem between the active tab and its content pane.Dice
Remove padding from left right may help uRoslynrosmarin
Also size of font and toggle should be decreaseRoslynrosmarin
C
4

There is a solution combining both Css and Javascript.

But, you need to know the tab's height and width!

Because every tab has the same width and height in your pictures, this seems to not be a problem.

// Tab width
var tabWidth = 140;
// Tab height
var tabHeight = 42;

var updateAllTabs = function(){
	$('li.active a[data-toggle="tab"]').each(function(){
		updateTabs({target : this});
	});
};

var updateTabs = function(e){
	// Get elements
	var activeItem = e.target.parentNode;
	var menu = activeItem.parentNode;
	var items = menu.getElementsByTagName("li");
	
	// Reset menu
	menu.style.paddingLeft = 0;
	menu.style.paddingBottom = 0;
	
	var menuWidth = jQuery(menu).width();
	
	var otherItems = [];
	
	// Clear old changes - make new list
	for(var i=0; i < items.length; i++){
		items[i].style.marginLeft = "0px";
		items[i].style.marginTop = "0px";
		
		if(items[i] != activeItem)
			otherItems.push(items[i]);
	}
	
	// If one line return (or only one item)
	if(menuWidth >= items.length * tabWidth || items.length <= 1)
		return;
	
	// Make some calculations
	var perLine = Math.floor(menuWidth / tabWidth);
	var lines = Math.ceil(items.length / perLine);
	
	// 1 tab per line
	if(perLine == 1){
		menu.style.paddingBottom = jQuery(activeItem).height() + "px";
		return;
	} else if(perLine + 1 == items.length){
		menu.style.paddingBottom = jQuery(activeItem).height() + "px";
	}
	
	var pad = jQuery(activeItem).width();
	
	// More than one per tab
	menu.style.paddingLeft = pad + "px";
	
	// For every line exept the last
	for(var i=0; i < lines-1; i++){
		// Move first of the line
		otherItems[i*perLine].style.marginLeft = ((i+1)*pad*(-1)) + "px";
		otherItems[i*perLine].style.marginTop = (i*tabHeight) + "px";
	}
	
	return;
};

$( window ).resize(updateAllTabs);
$('a[data-toggle="tab"]').on('shown.bs.tab', updateTabs);
updateAllTabs();
.nav-tabs > li{
  width: 140px;
  height: 42px;
}

@media only screen and (max-width:  840px) {
  .nav-tabs{
    position: relative;
  }
  .nav-tabs > li.active{
    position: absolute;
    bottom: -1px;
    left: 0px;
  }
}
<link rel="stylesheet prefetch" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">

<div>

  <!-- Nav tabs -->
  <ul class="nav nav-tabs" role="tablist">
    <li role="presentation" class="active"><a href="#tab1" aria-controls="home" role="tab" data-toggle="tab">Tab 1</a></li>
    <li role="presentation"><a href="#tab2" aria-controls="profile" role="tab" data-toggle="tab">Tab 2</a></li>
    <li role="presentation"><a href="#tab3" aria-controls="messages" role="tab" data-toggle="tab">Tab 3</a></li>
    <li role="presentation"><a href="#tab4" aria-controls="settings" role="tab" data-toggle="tab">Tab 4</a></li>
    <li role="presentation"><a href="#tab5" aria-controls="settings" role="tab" data-toggle="tab">Tab 5</a></li>
    <li role="presentation"><a href="#tab6" aria-controls="settings" role="tab" data-toggle="tab">Tab 6</a></li>
  </ul>

  <!-- Tab panes -->
  <div class="tab-content">
    <div role="tabpanel" class="tab-pane active" id="tab1">This is the tab 1</div>
    <div role="tabpanel" class="tab-pane" id="tab2">This is the tab 2</div>
    <div role="tabpanel" class="tab-pane" id="tab3">This is the tab 3</div>
    <div role="tabpanel" class="tab-pane" id="tab4">This is the tab 4</div>
    <div role="tabpanel" class="tab-pane" id="tab5">This is the tab 5</div>
    <div role="tabpanel" class="tab-pane" id="tab6">This is the tab 6</div>
  </div>

</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>

So, how does this work?

The basic idea is that every time a new tab is activated, we set the active tab as position: absolute; left: 0px; bottom: -1px; then we add on the tabs menu padding equal to the width of the tab. Lastly, we add a margin minus the width of the tabs on the first tab of each line.

Some more info:

The code works even with more than 2 lines of tabs.

It can easily be converted to match the layout of your last image by adding fake items on the otherItems array and adding margin-right on the previus item. (got it? or I have to code it for you? :P)

Conclusive answered 12/7, 2016 at 22:45 Comment(2)
This is probably on the right path, but there's a bug with keeping the tabs in sync with their content - clicking on tab 6 shows content for tab 5, clicking 4 shows 3, etc.Dice
The problem was fixed. Wrong html attributes :)Conclusive
E
1

Try this solution: https://jsfiddle.net/DTcHh/22583/

CSS

.nav-tabs { border-bottom: 2px solid #DDD; }
.nav-tabs > li.active > a, .nav-tabs > li.active > a:focus, .nav-tabs > li.active > a:hover { border-width: 0; }
.nav-tabs > li > a { border: none; color: #666; }
.nav-tabs > li.active > a, .nav-tabs > li > a:hover { border: none; color: #4285F4 !important; background: transparent; }
.nav-tabs > li > a::after { content: ""; background: #4285F4; height: 2px; position: absolute; width: 100%; left: 0px; bottom: -1px; transition: all 250ms ease 0s; transform: scale(0); }
.nav-tabs > li.active > a::after, .nav-tabs > li:hover > a::after { transform: scale(1); }
.tab-nav > li > a::after { background: #21527d none repeat scroll 0% 0%; color: #fff; }
.tab-pane { padding: 15px 0; }
.tab-content{padding:20px}

UPDATE: I made some improvement, added some jquery. Look at fiddle now.

var active = $('.nav-tabs > li.active').html(),
    last = $('.nav-tabs > li:last-child').html();
    $('.nav-tabs > li.active').removeClass('active').html(last);
    $('.nav-tabs > li:last-child').addClass('active').html(active); 
    last = active;
    $('.nav-tabs li').on('click', function(){
        active = $(this).html();
        last = $('.nav-tabs > li:last-child').html();
        $(this).removeClass('active').html(last);
        $('.nav-tabs > li:last-child').addClass('active').html(active);      
        last = active;
    });
Evvie answered 12/7, 2016 at 21:8 Comment(1)
This is a good solution, but I think it's a little confusing to the end-user when the tab goes to the end. Is there a way you could adapt this solution to make it appear at the beginning instead? I realize you might want to post another answer for it because it would probably be drastically different since this answer depends on the existing stack - as opposed to shifting the inactive tabs to the back.Dice
H
1

I have also faced the same problem, I fixed out with a solutions for that. I think it will be help full for you.

Note: While handling the bootstrap tab it will not have a responsive tab on mobile device, so it has to be initiated with collapsible grid as mentioned below...

This was previously experienced by me.

Here is my code link.

codepen.io/nehemc/pen/RRQKZB

Here is the code, you need to add in javascript.

<script>
    var fakewaffle = (function($, fakewaffle) {
          'use strict';

          fakewaffle.responsiveTabs = function(collapseDisplayed) {

            fakewaffle.currentPosition = 'tabs';

            var tabGroups = $('.nav-tabs.responsive');
            var hidden = '';
            var visible = '';
            var activeTab = '';

            if (collapseDisplayed === undefined) {
              collapseDisplayed = ['xs', 'sm'];
            }

            $.each(collapseDisplayed, function() {
              hidden += ' hidden-' + this;
              visible += ' visible-' + this;
            });

            $.each(tabGroups, function(index) {
              var collapseDiv;
              var $tabGroup = $(this);
              var tabs = $tabGroup.find('li a');

              if ($tabGroup.attr('id') === undefined) {
                $tabGroup.attr('id', 'tabs-' + index);
              }

              collapseDiv = $('<div></div>', {
                'class': 'panel-group responsive' + visible,
                'id': 'collapse-' + $tabGroup.attr('id')
              });

              $.each(tabs, function() {
                var $this = $(this);
                var oldLinkClass = $this.attr('class') === undefined ? '' : $this.attr('class');
                var newLinkClass = 'accordion-toggle';
                var oldParentClass = $this.parent().attr('class') === undefined ? '' : $this.parent().attr('class');
                var newParentClass = 'panel panel-default';
                var newHash = $this.get(0).hash.replace('#', 'collapse-');

                if (oldLinkClass.length > 0) {
                  newLinkClass += ' ' + oldLinkClass;
                }

                if (oldParentClass.length > 0) {
                  oldParentClass = oldParentClass.replace(/\bactive\b/g, '');
                  newParentClass += ' ' + oldParentClass;
                  newParentClass = newParentClass.replace(/\s{2,}/g, ' ');
                  newParentClass = newParentClass.replace(/^\s+|\s+$/g, '');
                }

                if ($this.parent().hasClass('active')) {
                  activeTab = '#' + newHash;
                }

                collapseDiv.append(
                  $('<div>').attr('class', newParentClass).html(
                    $('<div>').attr('class', 'panel-heading').html(
                      $('<h4>').attr('class', 'panel-title').html(
                        $('<a>', {
                          'class': newLinkClass,
                          'data-toggle': 'collapse',
                          'data-parent': '#collapse-' + $tabGroup.attr('id'),
                          'href': '#' + newHash,
                          'html': $this.html()
                        })
                      )
                    )
                  ).append(
                    $('<div>', {
                      'id': newHash,
                      'class': 'panel-collapse collapse'
                    })
                  )
                );
              });

              $tabGroup.next().after(collapseDiv);
              $tabGroup.addClass(hidden);
              $('.tab-content.responsive').addClass(hidden);

              if (activeTab) {
                $(activeTab).collapse('show');
              }
            });

            fakewaffle.checkResize();
            fakewaffle.bindTabToCollapse();
          };

          fakewaffle.checkResize = function() {

            if ($('.panel-group.responsive').is(':visible') === true && fakewaffle.currentPosition === 'tabs') {
              fakewaffle.tabToPanel();
              fakewaffle.currentPosition = 'panel';
            } else if ($('.panel-group.responsive').is(':visible') === false && fakewaffle.currentPosition === 'panel') {
              fakewaffle.panelToTab();
              fakewaffle.currentPosition = 'tabs';
            }

          };

          fakewaffle.tabToPanel = function() {

            var tabGroups = $('.nav-tabs.responsive');

            $.each(tabGroups, function(index, tabGroup) {

              // Find the tab
              var tabContents = $(tabGroup).next('.tab-content').find('.tab-pane');

              $.each(tabContents, function(index, tabContent) {
                // Find the id to move the element to
                var destinationId = $(tabContent).attr('id').replace(/^/, '#collapse-');

                // Convert tab to panel and move to destination
                $(tabContent)
                  .removeClass('tab-pane')
                  .addClass('panel-body fw-previous-tab-pane')
                  .appendTo($(destinationId));

              });

            });

          };

          fakewaffle.panelToTab = function() {

            var panelGroups = $('.panel-group.responsive');

            $.each(panelGroups, function(index, panelGroup) {

              var destinationId = $(panelGroup).attr('id').replace('collapse-', '#');
              var destination = $(destinationId).next('.tab-content')[0];

              // Find the panel contents
              var panelContents = $(panelGroup).find('.panel-body.fw-previous-tab-pane');

              // Convert to tab and move to destination
              panelContents
                .removeClass('panel-body fw-previous-tab-pane')
                .addClass('tab-pane')
                .appendTo($(destination));

            });

          };

          fakewaffle.bindTabToCollapse = function() {

            var tabs = $('.nav-tabs.responsive').find('li a');
            var collapse = $('.panel-group.responsive').find('.panel-collapse');

            // Toggle the panels when the associated tab is toggled
            tabs.on('shown.bs.tab', function(e) {

              if (fakewaffle.currentPosition === 'tabs') {
                var $current = $(e.currentTarget.hash.replace(/#/, '#collapse-'));
                $current.collapse('show');

                if (e.relatedTarget) {
                  var $previous = $(e.relatedTarget.hash.replace(/#/, '#collapse-'));
                  $previous.collapse('hide');
                }
              }

            });

            // Toggle the tab when the associated panel is toggled
            collapse.on('shown.bs.collapse', function(e) {

              if (fakewaffle.currentPosition === 'panel') {
                // Activate current tabs
                var current = $(e.target).context.id.replace(/collapse-/g, '#');
                $('a[href="' + current + '"]').tab('show');

                // Update the content with active
                var panelGroup = $(e.currentTarget).closest('.panel-group.responsive');
                $(panelGroup).find('.panel-body').removeClass('active');
                $(e.currentTarget).find('.panel-body').addClass('active');
              }

            });
          };

          $(window).resize(function() {
            fakewaffle.checkResize();
          });

          return fakewaffle;
        }(window.jQuery, fakewaffle || {}));

        (function($) {
          fakewaffle.responsiveTabs(['xs', 'sm']);
        })(jQuery);
        $(document).ready(function() {
          $('.tabcontainer .accordion-toggle').click(function() {
            var divTarget = $(this).attr('href'),
              divTargetElt = $(this);

            setTimeout(function() {
              console.log(divTarget);
              $('html,body').animate({
                  scrollTop: divTargetElt.offset().top
                },
                'slow');
            }, 500);
          });
        });
    </script>
Heiney answered 18/7, 2016 at 14:14 Comment(0)
O
0

I'm a bit puzzled about what you actually are trying to achieve. But I made this solution to hide parts of the inactive tabs (and make them partly overlapping/stacking each other). I used the default bootstrap example. Explanation is in the code. With this technique you are basically making the tabs smaller so they fit a smaller screen properly. Note that you can wrap everything you'd like to disappear in a span.

This still won't do what you want with extreme amounts of tabs. In that case you will have to use some javascript to make some tabs disappear/reappear on the left/right side of your screen.

Example

html

<ul class="nav nav-tabs">
  <li role="presentation" class="active"><a href="#">Home <span>I m whatever was missing</span></a></li>
  <li role="presentation"><a href="#">P<span>rofile</span></a></li>
  <li role="presentation"><a href="#">M<span>essages</span></a></li>
</ul>
<div id="stuff"></div>

css

/*hide all the items after the part you want to show*/
.nav-tabs li span{
  display: none;
}
/*show the items on the parts with the bootstrap .active class*/
li.active span{
  display: inline-block;
}
#stuff{
  background-color: yellow;
  height: 200px;
}
.active a{
  background-color: yellow !important;
}
Oahu answered 13/7, 2016 at 14:17 Comment(0)
B
0

Don't use float property. I think there is no solution using float property. You can achieve this by using,
style="Position:Fixed;"
I wrote something. Hope it will help you.

CSS



    ul {
        list-style-type: none;
        margin: 0;
        padding: 0;
        background-color: #333;
        width:800px;
        height:45px; width:100%;
    }
    li a {
        position:fixed;
        color: white;
        text-align: center;
        padding: 14px 16px;
        text-decoration: none;

    }

    li a:hover:not(.active) {
        background-color: #111;
    }

    .active {
        background-color: #4CAF50;
    }


HTML

  <html>
    <body>
    <ul >
      <li><a class="active" href="#home">Tab1</a></li>
      <li><a href="#n2" style="left:80px;" >Tab2</a></li>
      <li><a href="#n3" style="left:160px;">Tab3</a></li>
      <li><a href="#n4" style="left:240px;">Tab4</a></li>
      <li><a href="#n5" style="left:320px;">Tab5</a></li>
      <li><a href="#n6" style="left:400px;">Tab6</a></li>
      <li><a href="#n7" style="left:480px;">Tab7</a></li>
      <li><a href="#n8" style="left:560px;">Tab8</a></li>
      <li><a href="#n9" style="left:640px;">Tab9</a></li>
      <li><a href="#n10" style="left:720px;" >Tab10</a></li>
    </ul>
    </body></html>
Boondocks answered 13/7, 2016 at 15:35 Comment(0)
T
0

Calculate the row index add added to li. On init or active tab changes, then it sorts li position until it is at bottom row.

Hope this what you are trying to achieve.

Demo: http://codepen.io/anon/pen/GqQdyb

HTML

    <div class="col-md-12">

      <ul id="tabs" class="nav nav-tabs" role="tablist">
        <li role="presentation" class="active"><a href="#home" aria-controls="home" role="tab" data-toggle="tab">Tab 1</a></li>
        <li role="presentation"><a href="#profile" aria-controls="profile" role="tab" data-toggle="tab">Tab 2</a></li>
        <li role="presentation"><a href="#messages" aria-controls="messages" role="tab" data-toggle="tab">Tab 3</a></li>
        <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">Tab 4</a></li>
        <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">Tab 5</a></li>
        <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">Tab 6</a></li>
        <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">Tab 7</a></li>
        <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">Tab 8</a></li>
        <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">Tab 9</a></li>
        <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">Tab 10</a></li>
        <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">Profile</a></li>
        <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">Message</a></li>
        <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">Very long tab name</a></li>
        <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">Programming</a></li>
      </ul>

      <!-- Tab panes -->
      <div class="tab-content">
        <div role="tabpanel" class="tab-pane active" id="home">...</div>
        <div role="tabpanel" class="tab-pane" id="profile">...</div>
        <div role="tabpanel" class="tab-pane" id="messages">...</div>
        <div role="tabpanel" class="tab-pane" id="settings">...</div>
      </div>      
    </div>    
  </div>  
</div>

JS

$( function() {  
    var _max = 0;  

    var _calc = function() {
      var _w = $('#tabs').width();
      var _h = $('#tabs').height();  
      var _a = _w;  

      var _index = 0;
      $('#tabs li').each(function(i, v) {            
        var _t = $(v).outerWidth();      
        if(_a < _t) {
          _a = _w;
          _index++;
        }
        $(v).data('rowindex', _index);      
        _a -= _t;      
      });

      _max = _index;
      console.log(_max);
    };



    var _sortTab = function() {      
      var _active = $('li.active').data('rowindex');      
      var _tabs = $('#tabs');
      _tabs.find('li').sort(function(a, b) {        
        var _a = $(a).data('rowindex');
        var _b = $(b).data('rowindex');        
        return (_a == _active && _a !== _b);
      }).appendTo(_tabs);      
      _calc();

      if(_active !== _max) {
        _sortTab();
      }      
    };


  $('#tabs li').on('shown.bs.tab', function (e) {  
    _sortTab();
  });  
  _calc();
  _sortTab();  
});
Type answered 19/7, 2016 at 9:24 Comment(0)
T
-1

A long list tab would not fit into fixed small screen so only way is to display the tabs in second line with is the default behavior.

Another one would be display additional tabs in dropdown menu. Something like here

Here's is a great example created in bootply. click for code

Type answered 15/7, 2016 at 13:30 Comment(2)
your presumption that "only way is to display the tabs in second line" may be true, but the question is how to display that second row in the background instead of the foreground.Dice
Thank you @Jeff for explainationType

© 2022 - 2024 — McMap. All rights reserved.