Saturday, March 26, 2016

Alter Bootstrap nav tabs row wrapping

Leave a Comment

With Bootstrap 3, rows of nav-tabs wrap in a way where the widest rows tend to be at the top while the shorter rows are at the bottom:

enter image description here

This leaves the tabs looking awkward and unbalanced. Is there a way that nav-tabs can be modified so that the rows are wider at the bottom? More like this:

enter image description here

Here's the JSFiddle that produced the first image.

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">  <div class="container">   <div class="row">     <div class="col-sm-6">       <ul class="nav nav-tabs">         <li class="active"><a href="#">Foo Bar 1</a></li>         <li><a href="#">FooBar 2</a></li>         <li><a href="#">FooBar 3</a></li>         <li><a href="#">FooBar 4</a></li>         <li><a href="#">FooBar 5</a></li>         <li><a href="#">FooBar 6</a></li>         <li><a href="#">FooBar 7</a></li>         <li><a href="#">FooBar 8</a></li>         <li><a href="#">FooBar 9</a></li>         <li><a href="#">FooBar 10</a></li>         <li><a href="#">FooBar 11</a></li>         <li><a href="#">FooBar 12</a></li>         <li><a href="#">FooBarBaz 13</a></li>         <li><a href="#">FooBarBaz 14</a></li>       </ul>     </div>   </div> </div> 

6 Answers

Answers 1

This is as close as I can get to solving your problem.

$(document).ready(function() {     $(window).resize(); })  $(window).resize(function() {     var W = $('#nav-bar').width();      var li_w = $.map($('#nav-bar li'),function(val)     {     return $(val).outerWidth();     });      if(W > li_w[0] * 2)     {         $('#nav-bar li').removeClass('clear');          var row = 0;          for(i=13; i>-1; i--)         {             row = row + li_w[i];              if(row > W)             {                 row = 0;                 i++;                 $('#nav-bar li:nth-child('+(i+1)+')').addClass('clear');             }         }     } }); 

I switched back to default float positioning used by Bootstrap. What I am doing here is to calculate the width of the parent ul, and the individual widths of all li. Afterwards I iterate from last list element to first, adding up their widths until they would not fit in one line i.e. the sum will be equal to width of the parent ul. At this breakpoint, I insert a class to the li in question using clear:left to break the element and all following it into a new line, effectively wrapping from behind.

This is not the most optimum solution, though. A more complex algorithm is required here to predict the number of lines that would be required to fill the list elements and to distribute the elements amongst the lines in such a way that each line contains as many elements as possible but still has width less than or equal to the line beneath it. Though I feel that it would not be worth it, performance-wise.

Finally, I prefer using CSS-only to solve layout-related problems as far as possible. The current problem can be easily solved using CSS as well. You will need to use media-queries to determine the length of ul and add predetermined breakpoints. It would be static but optimum when you have a fixed number of menu items.

Answers 2

I didn't understand what exactly you are looking for, and added following css to look good https://jsfiddle.net/2tu6a2yh/2/

.nav-tabs { display: table; table-layout: fixed; width : 100%; }  .nav-tabs li { display: table-cell; width:120px; // constant width } 

Answers 3

I think you need to set the column sizes for each list item for a more consistent look. Otherwise positions are determined according to the text length they contain. I think, if you make a few trial you'll find the combination you want for each screen size. Try something like following approach

<div class="container"> <div class="row"> <div class="col-sm-6">   <ul class="nav nav-tabs" id="sync-tabs">     <li class="active col-sm-4"><a href="#">Foo Bar 1</a></li>     <li class="col-sm-4"><a href="#">FooBar 2</a></li>     <li class="col-sm-4"><a href="#">FooBar 3</a></li>     <li class="col-sm-4"><a href="#">FooBar 4</a></li>     <li class="col-sm-4"><a href="#">FooBar 5</a></li>     <li class="col-sm-4"><a href="#">FooBar 6</a></li>     <li class="col-sm-3"><a href="#">FooBar 7</a></li>     <li class="col-sm-3"><a href="#">FooBar 8</a></li>     <li class="col-sm-3"><a href="#">FooBar 9</a></li>     <li class="col-sm-3"><a href="#">FooBar 10</a></li>     <li class="col-sm-3"><a href="#">FooBar 11</a></li>     <li class="col-sm-3"><a href="#">FooBar 12</a></li>     <li class="col-sm-3"><a href="#">FooBarBaz 13</a></li>     <li class="col-sm-3"><a href="#">FooBarBaz 14</a></li>   </ul> </div> 

Answers 4

Here is a fiddle that i have done according to your requirements

FIDDLE LINK

.nav-tabs {   display: -webkit-flex;    display: flex;    -webkit-flex-direction: column;     flex-direction: column;    -webkit-align-items: flex-start;    align-items: flex-start; }     <div class="container">   <div class="row">     <div class="col-sm-6">       <ul class="nav nav-tabs" id="sync-tabs">         <li class="active"><a href="#">Foo Bar 1</a></li>         <li><a href="#">FooBar 2</a></li>         <li><a href="#">FooBar 3</a></li>         <li><a href="#">FooBar 4</a></li>         <li><a href="#">FooBar 5</a></li>         <li><a href="#">FooBar 6</a></li>         <li><a href="#">FooBar 7</a></li>         <li><a href="#">FooBar 8</a></li>         <li><a href="#">FooBar 9</a></li>         <li><a href="#">FooBar 10</a></li>         <li><a href="#">FooBar 11</a></li>         <li><a href="#">FooBar 12</a></li>         <li><a href="#">FooBarBaz 13</a></li>         <li><a href="#">FooBarBaz 14</a></li>       </ul>     </div>   </div> </div> 

UPDATED FIDDLE LINK AS PER THE IMAGE GIVEN

Hope this helps...

Answers 5

Are you trying to do something like shown in the fiddle

You can explicitly use css clear trick to achieve what you want.

.clear {    clear: both;  }
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>  <div class="container">    <div class="row">      <div class="col-sm-6">        <ul class="nav nav-tabs" id="sync-tabs">          <li class="active"><a href="#">Foo Bar 1</a></li>          <li><a href="#">FooBar 2</a></li>          <li><a href="#">FooBar 3</a></li>          <li><a href="#">FooBar 4</a></li>          <li class="clear"><a href="#">FooBar 5</a></li>          <li><a href="#">FooBar 6</a></li>          <li><a href="#">FooBar 7</a></li>          <li><a href="#">FooBar 8</a></li>          <li><a href="#">FooBar 9</a></li>          <li class="clear"><a href="#">FooBar 10</a></li>          <li><a href="#">FooBar 11</a></li>          <li><a href="#">FooBar 12</a></li>          <li><a href="#">FooBar 13</a></li>          <li><a href="#">FooBar 14</a></li>        </ul>      </div>    </div>  </div>

Answers 6

I know it's not the best solution from the performance point of view but, anyway...Hope, this will be helpful. This solution doesn't depends on specific tab width and provides the result you want.

JSFiddle: https://jsfiddle.net/cpxd8eh0/6/

For bigger screen size

For smaller screen size

$(function(){   var $container = $('#sync-tabs');    updateTabs($container);   $(window).resize(function(){      updateTabs($container);   });    function updateTabs($tabsContainer){     var $containerWidth = $tabsContainer.width();     var tabWidths = [];     var $tabs = $tabsContainer.find('li');     $tabs.each(function(index, tab){         tabWidths.push($(tab).width());     });      var formattedTabs = [];     var maxWidth = $containerWidth;     var maxWidthSet = false;     var rowWidth = 0;     for(var i = tabWidths.length - 1; i >= 0; i--){         var tabWidth = tabWidths[i];         if(rowWidth + tabWidth > maxWidth){             if(!maxWidthSet){               maxWidth = rowWidth;               maxWidthSet = true;             }             rowWidth = tabWidth;             formattedTabs.unshift($('<div class="spacer"></div>'));         }else{            rowWidth += tabWidth;         }         formattedTabs.unshift($tabs.get(i));       }        var $tempContainer = $('<div></div>');       formattedTabs.forEach(function(tab, index){         $tempContainer.append(tab);       });       $tabsContainer.html($tempContainer.html());   } }); 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment