在响应式导航中添加更多列表按钮

问题描述 投票:0回答:5

我有一个包含 12 个项目的导航,当分辨率变小时,项目会放在新行中。我需要做到当一个项目不再适合导航时,它应该在导航的右侧放置一个“更多”下拉按钮。并将不适合下拉列表的项目放入其中。 如果你不明白我的意思,请看下面的图片。

但问题是导航项并不总是相同的宽度,因为导航项是从 REST api 生成的。

我尝试制作 jQuery 脚本来计算项目宽度并将其添加到导航中。 这是我创建的脚本,我做得很匆忙,所以非常糟糕。

我需要帮助如何正确计算项目的宽度和导航宽度,以及计算何时将项目添加到导航或从导航中删除项目。

如果您看不到,这是图片: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>

javascript jquery responsive-design
5个回答
5
投票

如果您有固定宽度的列表项,那么收集额外的列表项并将它们推送到单独的列表中很简单。这是一个简单的例子。解释在代码注释中。

全屏查看代码片段并尝试更改窗口宽度

也是小提琴http://jsfiddle.net/abhitalks/860LzgLL/

全屏http://jsfiddle.net/abhitalks/860LzgLL/embedded/result/

片段

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>


更新:

这与您对列表项的不同/可变宽度的查询有关。会有微小的改变。

也是小提琴http://jsfiddle.net/abhitalks/tkbmcupt/1/

全屏http://jsfiddle.net/abhitalks/tkbmcupt/1/embedded/result/

片段

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>


2
投票

可以并且应该进行优化(因为从我的测试来看,它的效率相当低),但这取决于你。

$(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>


1
投票

这个问题太老了,但我也想发布我的答案。也许这是更干净、更简单的方法。我创建了一支笔:https://codepen.io/sergi95/pen/bmNoML

<div id="mainMenu" class="main-menu">
<ul id="autoNav" class="main-nav">
  <li>
    <a href="#">home</a>
  </li>
  <li>
    <a href="#">about us</a>
  </li>
  <li>
    <a href="#">portfolio</a>
  </li>
  <li>
    <a href="#">team</a>
  </li>
  <li>
    <a href="#">blog</a>
  </li>
  <li>
    <a href="#">contact</a>
  </li>
  <li id="autoNavMore" class="auto-nav-more">
    <a href="#" class="more-btn">more</a>
    <ul id="autoNavMoreList" class="auto-nav-more-list">
      <li>
        <a href="#">policy</a>
      </li>
    </ul>
  </li>
</ul>

const $mainMenu = $("#mainMenu");
    const $autoNav = $("#autoNav");
    const $autoNavMore = $("#autoNavMore");
    const $autoNavMoreList = $("#autoNavMoreList");
    autoNavMore = () => {
        let childNumber = 2;

        if($(window).width() >= 320) {
            // GET MENU AND NAV WIDTH
            const $menuWidth = $mainMenu.width();
            const $autoNavWidth = $autoNav.width();
            if($autoNavWidth > $menuWidth) {
                // CODE FIRES WHEN WINDOW SIZE GOES DOWN
                $autoNav.children(`li:nth-last-child(${childNumber})`).prependTo($autoNavMoreList);
                autoNavMore();
            } else {
                // CODE FIRES WHEN WINDOW SIZE GOES UP
                const $autoNavMoreFirst = $autoNavMoreList.children('li:first-child').width();
                // CHECK IF ITEM HAS ENOUGH SPACE TO PLACE IN MENU
                if(($autoNavWidth + $autoNavMoreFirst) < $menuWidth) {
                    $autoNavMoreList.children('li:first-child').insertBefore($autoNavMore);
                }
            }
            if($autoNavMoreList.children().length > 0) {
                $autoNavMore.show();
                childNumber = 2;
            } else {
                $autoNavMore.hide();
                childNumber = 1;
            }
        }
    }
    // INIT 
    autoNavMore();
    $(window).resize(autoNavMore);


.main-menu {
        max-width: 800px;
    }
    .main-nav {
        display: inline-flex;
        padding: 0;
        list-style: none;
    }
    .main-nav li a {
        padding: 10px;
        text-transform: capitalize;
        white-space: nowrap;
        font-size: 30px;
        font-family: sans-serif;
        text-decoration: none;
    }
    .more-btn {
        color: red;
    }
    .auto-nav-more {
        position: relative;
    }
    .auto-nav-more-list {
        position: absolute;
        right: 0;
        opacity: 0;
        visibility: hidden;
        transition: 0.2s;
        text-align: right;
        padding: 0;
        list-style: none;
        background: grey;
        border-radius: 4px;
    }
    .auto-nav-more:hover .auto-nav-more-list {
        opacity: 1;
        visibility: visible;
    }

1
投票

Abhitalks 制作的脚本对于不同的元素大小无法正常工作。我稍微修改了代码,它确实做到了:

$(function() {
    function makeMenuFit() {
        //Get data
        var menuSize = menu.width();

        //Determine how many items that fit
        var menuTotalWidth = 0;
        var itemThatFit = 0;
        for(var i = 0; i < menuItems.length; i++) {
            menuTotalWidth += menuItems[i];
            if(menuTotalWidth <= menuSize) {
                itemThatFit++;
                continue;
            }
            break;
        }

        menu.children().css({"display": "block", "width": "auto"});
        var collectedSet = menu.children(":gt(" + (itemThatFit - 1) + ")");
        $("#submenu").empty().append(collectedSet.clone());  
        collectedSet.css({"display": "none", "width": "0"});
    }

    var menu = $(".tabletNavigation > ul");
    var menuItems = [];
    menu.children().each(function() {
        menuItems.push($(this).outerWidth());
    });

    $(window).resize(makeMenuFit);
    makeMenuFit();
});

0
投票

@Sergo Kupreishvili 的解决方案非常出色。我只是稍微改进了一下。仅当菜单无法适合调整大小的窗口时,才会出现“更多”按钮。 检查代码笔:https://codepen.io/adnanahmed237/pen/vYorqdr

$(function () {
      const $mainMenu = $("#mainMenu");
      const $autoNav = $("#autoNav");
      const $autoNavMore = $("#autoNavMore");
      const $autoNavMoreList = $("#autoNavMoreList");
      let originalItems = []; // Store original items

      // Store original items on load
      $autoNav.children("li:not(#autoNavMore)").each(function () {
        originalItems.push($(this));
      });

      function autoNavMore() {
        const menuWidth = $mainMenu.width();
        let usedWidth = 0;
        $autoNavMoreList.empty(); // Clear "More" dropdown

        // Reset menu to original items on each resize
        $autoNav.prepend(originalItems);

        // Iterate over menu items to check if they fit
        $autoNav.children("li:not(#autoNavMore)").each(function () {
          usedWidth += $(this).outerWidth(true);

          if (usedWidth > menuWidth) {
            $(this).appendTo($autoNavMoreList); // Move excess items to "More" dropdown
          }
        });

        // Toggle "More" button based on list content
        $autoNavMore.toggle($autoNavMoreList.children().length > 0);
      }

      // Initialize and set event listener for window resize
      autoNavMore();
      $(window).resize(autoNavMore);
    });
.main-menu {
      max-width: 1280px;
      margin: 0 auto;
    }

    .main-nav {
      display: inline-flex;
      padding: 0;
      list-style: none;
    }

    .main-nav li a {
      padding: 10px;
      text-transform: capitalize;
      white-space: nowrap;
      font-size: 20px;
      font-family: sans-serif;
      text-decoration: none;
    }

    .more-btn {
      color: red;
    }

    .auto-nav-more {
      position: relative;
      display: none;
    }

    .auto-nav-more-list {
      position: absolute;
      right: 0;
      opacity: 0;
      visibility: hidden;
      transition: 0.2s;
      text-align: right;
      padding: 0;
      list-style: none;
      background: grey;
      border-radius: 4px;
    }

    .auto-nav-more:hover .auto-nav-more-list {
      opacity: 1;
      visibility: visible;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<!-- MAIN MENU -->
  <div id="mainMenu" class="main-menu">
    <ul id="autoNav" class="main-nav">
        <li><a href="#">home</a></li>
        <li><a href="#">about us</a></li>
        <li><a href="#">portfolio</a></li>
        <li><a href="#">team</a></li>
        <li><a href="#">blog</a></li>
        <li><a href="#">contact</a></li>
        <li><a href="#">Page 1</a></li>
        <li><a href="#">Page 2</a></li>
        <li><a href="#">Page 3</a></li>
        <li><a href="#">Page 4</a></li>
        <li><a href="#">Page 5</a></li>
        <li id="autoNavMore" class="auto-nav-more">
            <a href="#" class="more-btn">more</a>
            <ul id="autoNavMoreList" class="auto-nav-more-list"></ul>
        </li>
    </ul>
  </div>
  <!-- MAIN MENU END -->
  <p>Resize the window to test the menu.</p>

© www.soinside.com 2019 - 2024. All rights reserved.