调整jQuery菜单以适应响应式布局的重新调整大小

18

这里有一个简单的菜单,使用UL和LI标签来制作。LI的数量和宽度是动态的。在鼠标悬停/点击时,会出现下拉框“more”,其中显示不适合可用空间的其余LI。

我尝试使用jQuery,在用户从右到左调整窗口大小时,隐藏最后一个可见的菜单项。如何反向实现并在“more”链接中添加LI?

我尝试了一些选项,当我们调整大小时,由于宽度较小,列表项会移至下方,并增加UL的高度,因此可以使用此方法隐藏最后一个可见的菜单项。

http://jsbin.com/flexmenu/2/edit

步骤1:

resize of window

步骤2:enter image description here

步骤3:enter image description here

当用户重新调整窗口大小(增加宽度)时,这些步骤将被反转。

标记:

<div class="twelve columns filter-wrapper">
    <ul class="nav-bar-filter" id="nav-bar-filter">
        <li><a href="#">All</a></li>
        <li><a href="#">Small</a></li>
        <li><a href="#">Medium</a></li>
        <li><a href="#">Extra large</a></li>
        <li><a href="#">Text</a></li>
        <li><a href="#">Small-1</a></li>
        <li><a href="#">Medium-1</a></li>
        <li><a href="#">Extra large text</a></li>
        <li><a href="#">Large text</a></li>
        <li><a href="#">Text</a></li>
    </ul>
<ul id="more-nav">
  <li><a href="#">More > </a>
    <ul class="subfilter"><li><a href="#">Text</a></li></ul>
  </li>    
</ul>
</div>

基本上,这个菜单将用于响应式布局的菜单。任何帮助都会有所帮助。 编辑1:添加了标记


我们能看一下CSS文件吗? - Shrey Gupta
1
显然,这就是整个重点。他想要用它来实现响应式设计...甚至还标记了“responsive-design”。 - Shrey Gupta
只是为了明确,您希望它在电脑上工作还是移动设备上工作,还是两者都可以? - razz
@razzak 是的,也适用于智能手机。 - Wasim Shaikh
我们能否仅使用CSS完成这个任务?或者至少使用简单的jQuery? - anjan.com.np
显示剩余2条评论
4个回答

14

好的,我尝试编写了一些脚本来完成这个任务,以下是我的成果:

$().ready(function () { 

    //we reconstruct menu on window.resize
    $(window).on("resize", function (e) {                                                   
        var parentWidth = $("#nav-bar-filter").parent().width() - 40;
        var ulWidth = $("#more-nav").outerWidth();                  
        var menuLi = $("#nav-bar-filter > li");                 
        var liForMoving = new Array();      
        //take all elements that can't fit parent width to array
        menuLi.each(function () {                       
            ulWidth += $(this).outerWidth(); 
            if (ulWidth > parentWidth) {
                console.log(ulWidth);
                liForMoving.push($(this));
            }
        });                         
        if (liForMoving.length > 0) {   //if have any in array -> move them to "more" ul
            e.preventDefault();                     
            liForMoving.forEach(function (item) {
                item.clone().appendTo(".subfilter");
                item.remove();
            });                         
        }
        else if (ulWidth < parentWidth) { //check if we can put some 'li' back to menu
            liForMoving = new Array();
            var moved = $(".subfilter > li");
            for (var i = moved.length - 1; i >= 0; i--) { //reverse order
                var tmpLi = $(moved[i]).clone();
                tmpLi.appendTo($("#nav-bar-filter"));
                ulWidth += $(moved[i]).outerWidth();
                if (ulWidth < parentWidth) {                                
                    $(moved[i]).remove();
                }
                else {
                    ulWidth -= $(moved[i]).outerWidth();
                    tmpLi.remove();
                }                           
            }                       
        }                       
        if ($(".subfilter > li").length > 0) { //if we have elements in extended menu - show it
            $("#more-nav").show();
        }
        else {
            $("#more-nav").hide();
        }
    });

    $(window).trigger("resize"); //call resize handler to build menu right
});

并且 JSFiddle 示例

为了使其正常工作,必须更改您的 css 样式。我们所做的是:

  1. 获取父容器的宽度,主菜单的初始 width 为零,但我们可能需要显示附加菜单,因此还要获取其大小。
  2. window.resize 上,我们循环遍历主菜单(水平)中的所有元素,将每个元素的宽度累积到 ulWidth 变量中。
  3. 一旦主菜单的 width 大于父容器的 width,则需要将其余的菜单项移至子菜单(垂直),因此我们将这些元素推入一个名为 liForMoving 的数组中。
  4. 如果数组 liForMoving 不为空,则克隆其元素,将其附加到子菜单并从主菜单中删除它们。
  5. 如果所有元素在主菜单中的 width 小于其容器的宽度,则需要检查是否可以将某些元素从子菜单移到主菜单。
  6. 我们遍历子菜单元素,获取每个项目,将其附加到主菜单中(如果您的菜单中有不同的字体和填充,元素的最终大小将有所不同),检查其大小是否适合父容器。如果适合,则从子菜单中删除该元素($(moved[i]).remove()),如果不适合,则删除附加的元素(tmpLi.remove())。
  7. 最后,我们检查子菜单是否有任何项目,并显示/隐藏它。

谢谢提供 jsfiddle 示例。看起来不错。我会将其集成并查看它是否在我的本地环境下工作。 - Wasim Shaikh
请确保您的CSS正确,以便在我的fiddle中显示它(即使没有JavaScript代码)。我在那里遇到了一些麻烦,所以不得不改变几乎所有的样式:D - Sergio
授予您赏金..如果您的代码需要更多帮助,我会在评论中提出。谢谢。 - Wasim Shaikh

11
$(document).ready(function () {
    var menu = $("#nav-bar-filter"),
        subMenu = $(".subfilter"),
        more = $("#more-nav"),
        parent = $(".filter-wrapper"),
        ww = $(window).width(),
        smw = more.outerWidth();

    menu.children("li").each(function () {
        var w = $(this).outerWidth();
        if (w > smw) smw = w + 20;
        return smw
    });
    more.css('width', smw);

    function contract() {
        var w = 0,
            outerWidth = parent.width() - smw - 50;
        for (i = 0; i < menu.children("li").size(); i++) {
            w += menu.children("li").eq(i).outerWidth();
            if (w > outerWidth) {
                menu.children("li").eq(i - 1).nextAll()
                    .detach()
                    .css('opacity', 0)
                    .prependTo(".subfilter")
                    .stop().animate({
                    'opacity': 1
                }, 300);
                break;
            }
        }
    }

    function expand() {
        var w = 0,
            outerWidth = parent.width() - smw - 20;
        menu.children("li").each(function () {
            w += $(this).outerWidth();
            return w;
        });
        for (i = 0; i < subMenu.children("li").size(); i++) {
            w += subMenu.children("li").eq(i).outerWidth();
            if (w > outerWidth) {
                var a = 0;
                while (a < i) {
                    subMenu.children("li").eq(a)
                        .css('opacity', 0)
                        .detach()
                        .appendTo("#nav-bar-filter")
                        .stop().animate({
                        'opacity': 1
                    }, 300);
                    a++;
                }
                break;
            }
        }
    }
    contract();

    $(window).on("resize", function (e) {
        ($(window).width() > ww) ? expand() : contract();
        ww = $(window).width();
    });

});

演示

不得不修改CSS才能让它工作。 我没有给任何元素静态尺寸,这样菜单就可以响应其内容而不受影响。

它是如何工作的: 只有两个函数contract()expand(),在页面加载时,contract()将被调用以将额外的项目移动到子菜单中。当窗口大小调整时,如果正在扩展,则会调用expand(),如果正在收缩,则会调用contract()

更新: 添加了动画并修复了子菜单的位置,详情请见演示。


感谢@razzak。我会在我的代码中包含这个功能。感谢你添加了两个函数:contract和expand。这将更易于管理。 - Wasim Shaikh
这个功能完美地运作。但是我想在滚动时触发展开和收缩函数,而不仅仅是在调整大小时触发。你能告诉我怎么做吗?我还想在滚动时重新构建菜单项,因为菜单区域是粘性的,并且当它是粘性的时,我会缩小标题部分。所以,在滚动后我们有更多的空间。 - M.S Shohan
当用户调整大小并扩展时,它不起作用。 - Saravana

1
我认为下拉菜单应该反转,以保持焦点项的制表顺序。 为了实现可访问性,您还可以设置集合大小和位置。出于可访问性原因,当移动的元素具有焦点并且添加了setWaiAria以设置集合大小和位置时,我将焦点重置为克隆的项目。当“更多”链接获得焦点并消失时,将焦点设置为最后一个项目。请参见fiddle
$().ready(function () { 

  var setWaiAria = function(menuLi){
    menuLi.each(function (i,el) {
      var $el = $(el);
      $el.attr('aria-setsize',menuLi.length);
      $el.attr('aria-posinset',i+1);
    });
  }

  // set wai aria aria-setsize and aria-posinset before cloning elements in other list
  setWaiAria($("#nav-bar-filter > li"));

  //we reconstruct menu on window.resize
  $(window).on("resize", function (e) {                         
    var parentWidth = $("#nav-bar-filter").parent().width() - 40;
    var ulWidth = $("#more-nav").outerWidth();          
    var menuLi = $("#nav-bar-filter > li");         
    var liForMoving = new Array();
    var activeElement = $(document.activeElement)[0];

    // before remove item check if you have to reset the focus
    var removeOriginal = function(item,clone){
      // check focused element
      if(item.find('a')[0] === activeElement){
        activeElement = clone.find('a')[0];
      }

      item.remove();
    }

    //take all elements that can't fit parent width to array
    menuLi.each(function () {
      var $el = $(this);            
      ulWidth += $el.outerWidth(); 
      if (ulWidth > parentWidth) {
        liForMoving.unshift($el);
      }
    }); 

    if (liForMoving.length > 0) { //if have any in array -> move em to "more" ul
      e.preventDefault(); 

      liForMoving.forEach(function (item) {
        var clone = item.clone();
        clone.prependTo(".subfilter");

        removeOriginal(item, clone);
      });

    }
    else if (ulWidth < parentWidth) { //check if we can put some 'li' back to menu
      liForMoving = new Array();

      var moved = $(".subfilter > li");
      for (var i=0, j = moved.length ; i < j; i++) { 
        var movedItem = $(moved[i]);

        var tmpLi = movedItem.clone();
        tmpLi.appendTo($("#nav-bar-filter"));


        ulWidth += movedItem.outerWidth();
        if (ulWidth < parentWidth) {
          removeOriginal(movedItem, tmpLi);
        }
        else {
          // dont move back
          ulWidth -= movedItem.outerWidth();
          tmpLi.remove();
        }

      }
    }           
    if ($(".subfilter > li").length > 0) { //if we have elements in extended menu - show it
      $("#more-nav").show();
    }
    else {
      // check if 'more' link has focus then set focus to last item in list
      if($('#more-nav').find('a')[0] === $(document.activeElement)[0]){
        activeElement = $("#nav-bar-filter > li:last-child a")[0];
      }

      $("#more-nav").hide();
    }

    // reset focus
    activeElement.focus();
  });

  $(window).trigger("resize"); //call resize handler to build menu right
});

将示例更新为带有切换菜单的fiddle。当菜单的子元素获得焦点时打开菜单:fiddle版本4 - Ron Jonk

1
尝试这样做:让您的jQuery检测导航栏中需要截断的术语数量。然后,使用基于截断术语数的document.createElement() JavaScript方法向ul添加li元素。例如,如果您的jQuery检测到已截断5个术语,则使用以下代码:
var truncated_elements = 5;
for (i=1; i<truncated_elements; i++){
    var new_li = document.createElement('li');
    new_li.innerHTML = "truncated_term";
    document.getElementsByTagName('ul')[0].appendChild(new_li);
}

在上述代码中,jQuery会发现需要截断5个元素,并运行一个for循环,为每个被截断的元素创建li。new_li的innerHTML将设置为被截断元素的内容(可能使用数组),然后将new_li附加到“更多”子菜单中的ul中。
如果需要,我可以提供JSFiddle / JSBin。

谢谢!我认为这只会一次性将那些“LI”添加到“more”中。 - Wasim Shaikh
实际上,for循环将根据需要不断添加li标签。 - Shrey Gupta
如果需要,我可以详细说明我的答案。您想要一个 JSFiddle 演示如何实现吗? - Shrey Gupta
尝试使用这段代码http://jsbin.com/flexmenu/2/edit。看看在调整大小时是否可以完成。http://jsbin.com/flexmenu/2/edit - Wasim Shaikh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接