Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

adding more button for list in responsive navigation

I have a navigation of lets say 12 items, and when resolution gets smaller items drop in a new line. I need to make that when an item doesn't fit on a navigation anymore it should put a "MORE" dropdown button on the right side of nav. and put that item that doesn't fit in a dropdown. If you don't understand me there is image below.

But the problem is that navigation items aren't always the same width because navigation items are generated from REST api.

I tryed to make jQuery script for calculating items width and adding them to navigation. Here is the script I created, I made it in a hurry so it's really bad.

I need to help on how to properly calculate items witdh and navigation width and calculating when to add items to navigation or remove items from navigation.

Here is image if you don't get it: http://img.hr/aagV

   

    /*
    * Here we check how many items can we put on the navigation bar
    * If item doesn't fit we clone it on the more dropdown button
    */
    function removeMany() {
        var i = $items.length - 1;

        if (itemsWidth > navWidth) {
            while (itemsWidth > navWidth) {
                $($items[i]).removeClass('first-level-item').addClass('second-level-item');
                dropdownItems.push($items[i]);
                $($items[i]).removeClass('showed');
                $items.pop();
                
                i--;
                getItemsWidth();
            }

            $nav.append($navMore);

            dropdownItems.reverse().forEach(function (element, index, array) {
                $('ul.second-level').append(element);
            });

            getItems();
        }
    }

    //If window is resized to bigger resolution we need to put back items on the navbar
    function addMany() {
        var i = dropdownItems.length - 1;

        if (dropdownItems.length != 0) {

            do {
                $('ul.first-level').append(dropdownItems.reverse()[i]);
                $items.push(dropdownItems[i]);
                dropdownItems.pop();

                i--;
                getItemsWidth();
            } while (itemsWidth < navWidth);

            $navMore.remove();

            $items.each(function (i) {
                $(this).addClass('first-level-item showed').removeClass('second-level-item');
            });

            if (!(dropdownItems != 0)) {
                return;
            } else {
                $nav.append($navMore);
            }


        }
    }

 
body {
  margin: 0;
  padding: 0;
  border: 0; }

ul, li {
  margin: 0;
  padding: 0;
  list-style: none; }

ul.second-level li {
  display: block !important; }
ul.second-level li > a {
  color: black; }

a {
  color: #fff;
  text-decoration: none;
  text-transform: uppercase; }

.second-level-item a {
  color: #333 !important; }

.navigation {
  width: 960px;
  max-width: 100%;
  background: #211;
  color: #aaa;
  margin: 0 auto; }

.first-level .first-level-item {
  display: inline-block;
  padding: 10px; }
.first-level .item-more {
  display: inline-block; }
  .first-level .item-more .second-level-item {
    display: inline-block; }

.second-level {
  position: absolute;
  top: 100%;
  right: 0;
  width: 200px;
  background: #fff;
  padding: 10px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.4); }

.has-second-level {
  position: relative; }
  .has-second-level .second-level {
    display: none; }
  .has-second-level:hover {
    background: #fff;
    color: #000; }
    .has-second-level:hover .second-level {
      display: block; }

/*# sourceMappingURL=style.css.map */
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>DropDown</title>

    <link rel="stylesheet" href="css/reset.css"/>
    <link rel="stylesheet" href="css/style.css"/>
</head>
<body>
    <nav class="navigation">

        <ul class="first-level">

            <li class="first-level-item showed"><a href="#">Introduction to Irish Culture</a></li>
            <li class="first-level-item showed"><a href="#">Cellular and Molecular Neurobiology</a></li>
            <li class="first-level-item showed"><a href="#">Guitar foundations</a></li>
            <li class="first-level-item showed"><a href="#">Startup Inovation</a></li>
            <li class="first-level-item showed"><a href="#">Astrophysics</a></li>


            <li class="first-level-item item-more has-second-level">
                <span> More </span>

                <ul class="second-level">

                </ul>

            </li>

        </ul>

    </nav>

    <script src="https://code.jquery.com/jquery-2.1.1.js"></script>
</body>
</html>
like image 242
vedran Avatar asked Jan 10 '23 06:01

vedran


2 Answers

If you have fixed-width list-items, then it is simple to collect extra list-items and push them into a separate list. Here is a simple example. Explanation is in the code comments.

View the snippet in full-screen and try changing the window width.

Also a Fiddle: http://jsfiddle.net/abhitalks/860LzgLL/

Full Screen: http://jsfiddle.net/abhitalks/860LzgLL/embedded/result/

Snippet:

var elemWidth, fitCount, fixedWidth = 120,  
    $menu = $("ul#menu"), $collectedSet;

// Assuming that the list-items are of fixed-width.

collect();
$(window).resize(collect);

function collect() {
    // Get the container width
    elemWidth = $menu.width();
  
    // Calculate how many list-items can be accomodated in that width
    fitCount = Math.floor(elemWidth / fixedWidth) - 1; 
  
    // Create a new set of list-items more than the fit count
    $collectedSet = $menu.children(":gt(" + fitCount + ")");
  
    // Empty the collection submenu and add the cloned collection set
    $("#submenu").empty().append($collectedSet.clone());    
}
* { box-sizing: border-box; margin: 0; padding: 0; }
div { position: relative; background-color: #ccc; height: 32px; overflow: visible; }
ul#menu, ol { height: 32px; max-width: 80%; overflow: hidden; }
ul#menu > li, ol > li { display: block; float: left;  height: 32px; width: 120px; padding: 4px 8px; }
ol { position: absolute; right: 0; top: 0; overflow: visible; }
ol > li { min-width: 120px; }
ol ul { position: absolute; top: 120%; right: 10%; }
ol li ul > li { list-style: none; background-color: #eee; border: 1px solid gray; padding: 4px 8px;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
    <ul id="menu">
        <li>Option One</li><li>Option Two</li><li>Option Three</li>
        <li>Option Four</li><li>Option Five</li><li>Option Six</li>
    </ul>
    <ol><li>Collected<ul id="submenu"></ul></li></ol>
</div>

Update:

This is regarding your query on differing / variable widths of list-items. There would be a minor change.

Also a Fiddle: http://jsfiddle.net/abhitalks/tkbmcupt/1/

Full Screen: http://jsfiddle.net/abhitalks/tkbmcupt/1/embedded/result/

Snippet:

var elemWidth, fitCount, varWidth = 0, ctr, $menu = $("ul#menu"), $collectedSet;

// Get static values here first
ctr = $menu.children().length;         // number of children will not change
$menu.children().each(function() {
    varWidth += $(this).outerWidth();  // widths will not change, so just a total
});

collect();  // fire first collection on page load
$(window).resize(collect); // fire collection on window resize

function collect() {
    elemWidth = $menu.width();  // width of menu 
  
    // Calculate fitCount on the total width this time
    fitCount = Math.floor((elemWidth / varWidth) * ctr) - 1;
    
    // Reset display and width on all list-items
    $menu.children().css({"display": "block", "width": "auto"});
  
    // Make a set of collected list-items based on fitCount
    $collectedSet = $menu.children(":gt(" + fitCount + ")");
  
    // Empty the more menu and add the collected items
    $("#submenu").empty().append($collectedSet.clone());  
  
    // Set display to none and width to 0 on collection,
    // because they are not visible anyway.
    $collectedSet.css({"display": "none", "width": "0"});
}
* { box-sizing: border-box; margin: 0; padding: 0; }
div { position: relative; background-color: #ccc; height: 32px; overflow: visible; }
ul#menu, ol { height: 32px; max-width: 80%; overflow: hidden; }
ul#menu > li, ol > li { display: block; float: left; height: 32px; white-space: nowrap; padding: 4px 8px; }
ol { position: absolute; right: 0; top: 0; overflow: visible; }
ol > li { min-width: 120px; }
ol ul { position: absolute; top: 120%; right: 10%; }
ol li ul > li { list-style: none; background-color: #eee; border: 1px solid gray; padding: 4px 8px;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
    <ul id="menu">
        <li>Option One</li><li>Option Two</li><li>Option Three</li>
        <li>Option Four</li><li>Option Five</li><li>Option Six</li>
    </ul>
    <ol><li>Collected<ul id="submenu"></ul></li></ol>
</div>
like image 172
Abhitalks Avatar answered Jan 22 '23 06:01

Abhitalks


Can and SHOULD be optimised (as it is quite inefficient from what i've tested), but that's up to you.

$(document).ready(function(){			
  var moreW = $(".more").outerWidth(), //width of your "more" element
      totalW = -moreW, //cumulated width of list elements
      totalN = $('.nav li').length - 1,  //number of elements minus the "more" element
      dw = document.documentElement.clientWidth;

  $('.nav li').each(function(){
    totalW += $(this).outerWidth();
  });

  function moveToDropdown(){
    dw = document.documentElement.clientWidth;
    //moves elements into the list
    while(totalW > (dw - moreW)){
      var temp = $(".nav li:nth-last-child(2)"); //element to be moved

      totalW = totalW - temp.outerWidth();
      $(".dropdown").append(temp.clone());
      temp.remove();
    }
    //moves elements out of the list
    var newList = $('.dropdown li').length; //check if we have elements
    if(newList > 0){
      var element = $('.dropdown li:last-child'), //element to be moved
          elementW = $('.dropdown li:last-child').outerWidth(); //width of element to be moved

      if(totalW +  elementW < dw - moreW){
        while(totalW +  elementW < dw - moreW ){
          var element = $('.dropdown li:last-child'),
              elementW = $('.dropdown li:last-child').outerWidth();

          totalW = totalW + elementW;
          $(".nav > li:last-child").before(element.clone());
          element.remove();
        }
      }						
    }
  }

  moveToDropdown();
  $(window).resize(moveToDropdown)
});
.clearfix:after{
  display:block;
  content:'';
  clear:both;  
}
body,html{
  width:100%;
  height:100%;
  margin:0;
  padding:0;
}
ul{
  list-style:none;
  width:100%;
  padding:0;
  margin:0;
}
ul li{    
  float:left;
  padding:5px;
}
.nav > li {
  position:relative;
}
.nav ul{
  position:absolute;
  top:25px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="nav clearfix">
  <li><a href="#">Item</a></li>
  <li><a href="#">Item</a></li>
  <li><a href="#">Item</a></li>
  <li><a href="#">Item</a></li>
  <li><a href="#">Item</a></li>
  <li><a href="#">Item</a></li>
  <li><a href="#">Item</a></li>

  <li class="more">
    <a href="#">more</a>
    <ul class="dropdown">
      <!-- we'll add elements here -->        
    </ul>
  </li>
</ul>
like image 25
Radu Andrei Avatar answered Jan 22 '23 06:01

Radu Andrei