为响应式布局重新调整大小时操作 jquery 菜单
Posted
技术标签:
【中文标题】为响应式布局重新调整大小时操作 jquery 菜单【英文标题】:Manipulate jquery menu on re-size for responsive layout 【发布时间】:2013-03-03 10:39:38 【问题描述】:有带有列表项的简单菜单。 UL LI。 LI 的宽度和数量是动态的。并且在悬停/单击时会出现“更多”下拉菜单,它将显示剩余的 LI ,这将不适合可用空间。
我尝试在用户从右到左调整窗口大小时使用 jquery,它会隐藏最后一个可见的菜单项。有什么可能的方法来做到这一点,并在“更多”链接中添加 LI。
尝试了一些选项,因为当我们调整大小时宽度较小,然后列表项移动到下方并增加 UL 的高度,所以使用这种方法我可以隐藏最后一个可见的。 代码
http://jsbin.com/flexmenu/2/edit
第 1 步
第二步
第三步
当用户重新调整大小(增加宽度)时,这些步骤将相反
标记
<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 文件吗? 这可以通过Responsive Design
模式来实现。
显然。这就是重点。他想将它用于响应式设计……它甚至被标记为“响应式设计”。
为了清楚起见,您希望它在电脑或移动设备上工作还是两者兼而有之?
@razzak 是的,也适用于智能手机。
【参考方案1】:
好吧,我已经尝试构建一些脚本来执行此操作,这就是我所拥有的:
$().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 sample
必须更改您的 css 样式以使其正常工作。我们要做的是:
-
获取父容器的宽度,主菜单的初始
width
为零,但我们可能必须显示附加菜单,因此我们也获取了它的大小。
在window.resize
上,我们循环遍历主菜单(水平)中的所有元素,并在ulWidth
变量中累积每个元素的宽度。
一旦主菜单的width
大于父容器的width
-> 我们需要将其余的菜单项移动到子菜单(垂直) - 所以我们将这些元素推送到数组liForMoving
。
如果数组liForMoving
不是为空 - 我们克隆它的元素,将它们附加到子菜单中,然后从主菜单中删除它们。
如果mainMenu
中所有元素中的width
小于其容器的宽度,我们需要检查是否可以将一些元素从子菜单移动到主菜单
我们遍历子菜单元素,抓取每个项目,附加到主菜单(如果菜单中有不同的字体和填充 - 元素的最终大小会有所不同),检查其大小是否适合父容器。如果 是 - 我们从子菜单中删除元素 ($(moved[i]).remove()
),如果 不是 - 我们删除附加元素 (tmpLi.remove()
)
最后我们检查子菜单是否有任何项目,并显示/隐藏它。
【讨论】:
感谢 jsfiddle 演示。看起来不错。将整合并查看它是否在我的本地工作。 只要确保你的 css 是正确的,以在我的小提琴中显示它(即使没有 javascript 代码)。我在那里遇到了一些麻烦,所以不得不改变几乎所有的风格:D 授予您赏金 .. 如果您的代码中需要更多帮助,将对此发表评论。交易【参考方案2】:$(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();
);
);
DEMO
必须修改 CSS 才能使其工作。 我没有为任何元素提供静态尺寸,以便菜单无论其内容如何都能做出响应。
它是如何工作的:
只有2个函数contract()
和expand()
,当页面加载时contract()
将被调用以将额外的项目移动到子菜单,当窗口调整大小时,如果它正在扩展expand()
将被调用,如果它正在收缩contract()
将被调用被调用。
更新: 在右侧添加了动画和固定子菜单位置,请参阅演示。
【讨论】:
谢谢@razzak。将包含在我的代码中。感谢您添加两个功能合同和扩展。它可以轻松管理。 这非常有效。但是我想在滚动 5px 左右之后触发扩展和收缩功能,除了调整大小。你能告诉我我们该怎么做吗?我也想在滚动时重建菜单项,因为菜单区域是粘性的,并且当它粘性时我缩小了标题部分。所以,滚动后我们还有更多的空间。【参考方案3】:试试这个:让你的 jQuery 检测nav
栏中有多少项必须被截断。然后,根据截断的词条数,使用document.createElement()
JavaScript 方法将li
元素添加到ul
。例如,如果您的 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
s。 new_li
的 innerHTML
将设置为截断元素的内容(可能使用数组),然后 new_li
将附加到“更多”子菜单中的 ul
。
如有必要,我可以提供 JSFiddle/JSBin。
【讨论】:
谢谢!我认为这只会一次将那些“LI”添加到“更多”中。 实际上,for
循环会根据需要不断添加li
标签。
如有必要,我可以更详细地回答我的问题。您想要一个 JSFiddle 来展示如何实现这一点吗?
尝试使用此代码jsbin.com/flexmenu/2/edit。看看是否可以在调整大小时完成。jsbin.com/flexmenu/2/edit【参考方案4】:
我认为下拉菜单应该颠倒,以保持选项卡序列在可聚焦项目中的顺序相同。
为了便于访问,您还可以设置集合大小和集合中的位置。 出于可访问性的原因,当移动的元素具有焦点时,我将焦点重置为克隆项目,并添加了 setWaiAria 来设置 setsize 和 set position。当更多链接具有焦点并消失时,将焦点设置到最后一项。见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 version 4以上是关于为响应式布局重新调整大小时操作 jquery 菜单的主要内容,如果未能解决你的问题,请参考以下文章