There is simple menu with list itmes. UL LI. width and numbers of LI is dynamic. And there is dropdown kind of thing "more" on hover/click it will show remaining LI , which will not fit available space.
I tried using jquery while user resize windows from right to left, it will hide the last visible menu item. What is the possible way to do this revers and also add LI in "more" link.
Tried some option, as width is less when we resize then list item move below and increase height of UL so using this method I am able to hide last visible. code
http://jsbin.com/flexmenu/2/edit
Step 1
Step 2
Step 3
These steps will be reverse when user re-size (increase the width)
Markup
<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>
Basically this menu will be used for responsive layout menu.Any help in this will be helpful. Edit 1: added markup
Well, I've tried to build some script for doing it, here's what I've got:
$().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
});
And JSFiddle sample
Had to change your css styles to make it work properly. What we do is:
width
of main menu is zero, but we probably have to show the additional menu, so we get its size as well.window.resize
we loop through all elements in main menu (horizontal) accumulating each element width in the ulWidth
variable.width
of main menu is more than width
of parent container -> we need to move the rest of menu items to the submenu (vertical) - so we push those elements to an array liForMoving
.liForMoving
isn't empty - we clone its elements, append them to the submenu and remove them from the main menu.width
of all elements in mainMenu
is minor than the width of its container, we need to check if we can move some elements from the submenu to the main one$(moved[i]).remove()
), if it's not - we remove appended element (tmpLi.remove()
)$(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
Had to modify CSS to make it work. I didn't give static dimensions for any element so that the menu would be responsive regardless to it's contents.
How it works:
there are simply 2 functions contract()
and expand()
, when page load contract()
will be called to move extra items to submenu, when window is resized if it's expanding expand()
will be called and if it's contracting contract()
will be called instead.
UPDATE: added animation and fixed submenu location to the right, see the demo.
Try this: Have your jQuery detect how many terms in the nav
bar have to be truncated. Then, add li
elements to the ul
by using the document.createElement()
JavaScript method based on the number of terms truncated. For example, if your jQuery detects that 5 terms have been truncated, then use the following code:
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);
}
In the above code, the jQuery would figure out that 5 elements need to be truncated, and run a for
loop in which it would create li
s for each truncated element. The innerHTML
of the new_li
would be set to the content of the truncated element (using an array possibly), and then the new_li
would be appended to the ul
in the "More" submenu.
I can provide a JSFiddle/JSBin if necessary.
I think the drop down menu should be reversed to keep the tab sequence in focusable items in the same order.
For accessibility you could also set the setsize and the position in the set. For accessibility reason I reset the focus to the cloned item when the moved element had focus and added setWaiAria to set the setsize and set position. And set the focus to the last item when the more link had focus and disappears. see 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
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With