Приветствую. Есть такое меню, с обычным выпадающим списком:
Как видим, это обычная подача с абсолютной позицией и выпадающими пунктами вниз. Однако, пунктов стало много и появилась необходимость сделать пункты в несколько колонок. Проблем с этим нет, задать position: relative для первого списка и сместить вложенный список влево со 100% шириной.
Итак, вопрос. Точнее 2 вопроса:
1) Как по средствам JS превратить выпадающий список в многоколоночную подачу?
Сама стилистика прописана изначально, прописан column-width и column-gap. То есть, нужно определить ширину родителя и задать её для вложенного списка. С этим справится не сложно:
function resize(){$('#sub').width($('#nav').width())}resize();
$( window ).resize(function() {resize()});
Только вот меню будет идти не от начала родителя, так как position: relative задан для пункта списк в котором находится второй пункт.
Как посчитать ширину пунктов впереди идущих и сместить выпадающий список в начало?
2) Опять же, по средствам JS, высчитать ширину впереди идущих пунктов и отнять от полученной ширины родителя. Чтоб получилось вот так:
То есть, чем ближе пункт к краю, тем уже становится выпадающий список по ширине. Плюс, установить минимальное значение в 240рх. Добавляю код для примера с простым выпадающим списком для тестов:
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
}
a {
text-decoration: none;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
.menu {
position: relative;
margin: 0 auto;
display: table;
}
.menu li {
float: left;
}
.menu li span {
display: block;
padding: 25px 18px;
text-transform: uppercase;
font-weight: bold;
font-size: 0.85em;
color: #555;
cursor: pointer;
}
.menu .sub {
position: relative;
}
.menu .sub ul {
opacity: 0;
visibility: hidden;
-webkit-column-width: 200px;
-moz-column-width: 200px;
column-width: 200px;
-webkit-column-gap: 2em;
-moz-column-gap: 2em;
column-gap: 2em;
min-width: 240px;
top: 100%;
left: 0;
position: absolute;
padding: 20px;
background: #333;
margin-top: 10px;
z-index: 9999;
}
.menu .sub ul:before {
content: '';
position: absolute;
left: 20px;
top: 0;
width: 0;
height: 0;
border-style: solid;
border-width: 0 7px 7px 7px;
border-color: transparent transparent #333;
}
.menu .sub:hover ul {
margin-top: 0;
opacity: 1;
visibility: visible;
}
.menu .sub:hover ul:before {
top: -7px;
}
.menu .sub ul li {
float: none;
}
.menu .sub ul span {
text-transform: none;
font-weight: 400;
padding: 10px;
color: #ccc;
}
.menu .sub ul span:hover {
color: #fff;
}
Спасибо за помощь, особенно за готовый пример. Если долго описывать, хотя бы укажите, что потребуется сделать на словах.
PS: Повторюсь, что знаю о решении через CSS. Интересен именно вариант с использованием JS.
Ответ
Чтобы выпадающего меню всегда совпадало по краю с основным, сдвинем его при помощи margin-left на нужное количество пикселей. Для этого подсчитаем координаты основного и выпадающего меню методом .offset().left и вычтем из координат основного координаты субменю — так мы получим отрицательное значение и присвоим его margin-left
Для примера разбил субменю на 4 колонки. Добавил пунктам флексбокс-раскладку, с расположением в колонку и переносом. Осталось посчитать высоту для меню — для этого будем суммировать высоту каждого 4-го пункта (поскольку у нас 4 колонки) и затем присвоим ее субменю. Также нам нужна ширина, равная всему меню, потому возьмем ширину экрана — 100vw — и вычтем из неё расстояние до левого края экрана. Примем за аксиому, что меню центрируется, а значит слева и справа одинаковое расстояние, потому это расстояние просто умножаем на 2; в итоге получаем строку .width('calc(100vw - ' + menuOffset.left * 2 + 'px)')
Все эти операции делаем при событии mouseover (первая функция в .hover()) и добавляем в инлайн-стили субменю, при mouseout (вторая функция в .hover()) очищаем инлайн-стили.
$(function() {
$('.sub').hover(function() {
var menuOffset = $('.menu').offset();
var subMenuOffset = $('ul', this).offset();
var heightSum = 0;
// так сдвигаем влево на уровень с меню выпадающее субменю
$('ul', this).css('margin-left', menuOffset.left - subMenuOffset.left);
$('li', this).each(function(index) {
if(index % 4 == 0) heightSum += +$(this).height();
});
$('ul', this).height(heightSum).width('calc(100vw - ' + menuOffset.left * 2 + 'px)');
}, function() {
$('ul', this).removeAttr('style');
});
});
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
}
a {
text-decoration: none;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
.menu {
position: relative;
margin: 0 auto;
display: table;
}
.menu li {
float: left;
}
.menu li span {
display: block;
padding: 25px 18px;
text-transform: uppercase;
font-weight: bold;
font-size: 0.85em;
color: #555;
cursor: pointer;
}
.menu .sub {
position: relative;
}
.menu .sub ul {
opacity: 0;
visibility: hidden;
min-width: 240px;
display: flex;
flex-direction: column;
flex-wrap: wrap;
top: 100%;
left: 0;
position: absolute;
padding: 20px;
background: #333;
margin-top: 10px;
z-index: 9999;
}
.menu .sub ul li {
width: 200px;
}
.menu .sub:before {
content: '';
position: absolute;
left: calc(50% - 7px);
bottom: 0;
width: 0;
height: 0;
border-style: solid;
border-width: 0 7px 7px 7px;
border-color: transparent transparent #333;
opacity: 0;
}
.menu .sub:hover ul {
margin-top: 0;
opacity: 1;
visibility: visible;
}
.menu .sub:hover:before {
opacity: 1;
}
.menu .sub:hover ul:before {
top: -7px;
}
.menu .sub ul li {
float: none;
}
.menu .sub ul span {
text-transform: none;
font-weight: 400;
padding: 10px;
color: #ccc;
}
.menu .sub ul span:hover {
color: #fff;
}
Альтернативный вариант, где от JS требуется только сдвинуть субменю влево и задать ширину:
$(function() { $('.sub').hover(function() { var menuOffset = $('.menu').offset(); var subMenuOffset = $('ul', this).offset(); // так сдвигаем влево на уровень с меню выпадающее субменю $('ul', this).css('margin-left', menuOffset.left - subMenuOffset.left); $('.sub ul').width($('.menu').width()); }, function() { $('ul', this).removeAttr('style'); }); }); * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } body { margin: 0; padding: 0; font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; } a { text-decoration: none; } ul { margin: 0; padding: 0; list-style: none; } .menu { position: relative; margin: 0 auto; display: table; } .menu li { float: left; } .menu li span { display: block; padding: 25px 18px; text-transform: uppercase; font-weight: bold; font-size: 0.85em; color: #555; cursor: pointer; } .menu .sub { position: relative; } .menu .sub ul { opacity: 0; visibility: hidden; -webkit-column-width: 200px; -moz-column-width: 200px; column-width: 200px; -webkit-column-gap: 2em; -moz-column-gap: 2em; column-gap: 2em; min-width: 240px; top: 100%; left: 0; position: absolute; padding: 20px; background: #333; margin-top: 10px; z-index: 9999; } .menu .sub ul li { width: 200px; } .menu .sub:before { content: ''; position: absolute; left: calc(50% - 7px); bottom: 0; width: 0; height: 0; border-style: solid; border-width: 0 7px 7px 7px; border-color: transparent transparent #333; opacity: 0; } .menu .sub:hover ul { margin-top: 0; opacity: 1; visibility: visible; } .menu .sub:hover:before { opacity: 1; } .menu .sub:hover ul:before { top: -7px; } .menu .sub ul li { float: none; } .menu .sub ul span { text-transform: none; font-weight: 400; padding: 10px; color: #ccc; } .menu .sub ul span:hover { color: #fff; }
Комментариев нет:
Отправить комментарий