Страницы

Поиск по вопросам

суббота, 13 июля 2019 г.

Изменение ширины пунктов выпадающего списка

Приветствую. Есть такое меню, с обычным выпадающим списком:

Как видим, это обычная подача с абсолютной позицией и выпадающими пунктами вниз. Однако, пунктов стало много и появилась необходимость сделать пункты в несколько колонок. Проблем с этим нет, задать 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; }

Комментариев нет:

Отправить комментарий