Манипулирование меню jquery с изменением размера для гибкой компоновки
Существует простое меню со списком itmes. UL LI. ширина и числа LI являются динамическими. И есть выпадающий вид вещи "больше" при наведении/щелчке на нем покажет оставшийся LI, который не будет соответствовать доступному пространству.
Я попытался использовать jquery, пока пользователь изменяет размер окна справа налево, он скроет последний видимый элемент меню. Каков возможный способ сделать это, а также добавить 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: добавлена разметка
Ответы
Ответ 1
$(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()
, будет Вместо этого вызывается.
UPDATE:
добавлена анимация и место фиксированного подменю справа, см. демонстрацию.
Ответ 2
Ну, я попытался создать для этого несколько script, вот что у меня получилось:
$().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, чтобы они работали правильно. Что мы делаем:
- Получить ширину родительского контейнера, начальное
width
главного меню - ноль, но нам, вероятно, нужно показать дополнительное меню, поэтому мы также получим его размер.
- В
window.resize
мы прокручиваем все элементы главного меню (по горизонтали), накапливая каждую ширину элемента в переменной ulWidth
.
- Когда
width
главного меню больше width
родительского контейнера → нам нужно переместить остальные пункты меню в подменю (по вертикали) - поэтому мы нажимаем эти элементы на массив liForMoving
.
- Если массив
liForMoving
не является пустым - мы клонируем его элементы, добавим их в подменю и удалим их из главного меню.
- Если
width
всех элементов в mainMenu
меньше, чем ширина его контейнера, нам нужно проверить, можем ли мы перенести некоторые элементы из подменю в основной
- Мы перебираем элементы подменю, захватываем каждый элемент, добавляем в главное меню (если у вас разные шрифты и прокладки в меню - конечный размер элемента будет отличаться), проверьте, хорош ли его размер для размещения родительского контейнера. Если - мы удаляем элемент из подменю (
$(moved[i]).remove()
), если он не - мы удаляем добавленный элемент (tmpLi.remove()
)
- Наконец, мы проверяем, есть ли в подменю какие-либо элементы, и показать/скрыть его.
Ответ 3
Попробуйте следующее: пусть ваш jQuery определит, сколько терминов в строке nav
нужно усечь. Затем добавьте li
элементы в ul
с помощью метода document.createElement()
JavaScript, основанного на количестве усеченных терминов. Например, если ваш 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
для каждого усеченного элемента. innerHTML
из new_li
будет установлен в содержимое усеченного элемента (возможно, с использованием массива), а затем new_li
будет добавлен к ul
в подменю "Дополнительно".
Я могу при необходимости предоставить JSFiddle/JSBin.
Ответ 4
Я думаю, что выпадающее меню должно быть изменено на противоположное, чтобы сохранить последовательность вкладок в настраиваемых элементах в том же порядке.
Для обеспечения доступности вы также можете установить серию и позицию в наборе.
По соображениям доступности я reset фокус на клонированный элемент, когда перемещенный элемент имел фокус, и добавил 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
});