Расширение области наведения на внешний элемент
У меня есть раскрывающееся меню, в котором подменю размещено на другом элементе. Таким образом, в основном, когда мышь покидает элемент меню, подменю немедленно закрывается, потому что подменю не является дочерним.
var menuItem = $(".menu-item");
menuItem.hover(hoverIn, hoverOut);
function hoverIn() {
var mnItemMeta = $(this)[0].getBoundingClientRect();
$(".sub-menu").css({
opacity: 1,
left: mnItemMeta.left
})
}
function hoverOut() {
$(".sub-menu").css({
opacity: 0
})
}
html,body{background-color: #efefef;}
.menu {
list-style: none;
padding-left: 0;
display: flex;
justify-content: center;
}
a {
display: block;
padding: 10px 20px;
text-decoration: none;
color: inherit;
}
.sub-menu {
opacity: 0;
background-color: white;
position: absolute;
transition: .2s ease;
}
.sub-menu-list {
list-style: none;
padding-left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul class="menu">
<li class="menu-item"><a href="#">Menu Item</a>
</li>
</ul>
<div class="sub-menu">
<ul class="sub-menu-list">
<li><a href="#">Sub Menu 1</a>
</li>
<li><a href="#">Sub Menu 2</a>
</li>
<li><a href="#">Sub Menu 3</a>
</li>
<li><a href="#">Sub Menu 4</a>
</li>
</ul>
</div>
Ответы
Ответ 1
Хорошо, я понял решение. Спасибо всем вам за советы, но я не могу принять ответы от вас, ребята, потому что решение нуждается в более обходном пути.
Итак, в основном я создал две функции, которые являются startCloseTimeout()
и stopCloseTimout()
, и привязываю их как к menu-item
, так и к submenu
.
Вот сама функция:
var startCloseTimeout = function (){
closeDropdownTimeout = setTimeout( () => closeDropdown() , 50 );
},
stopCloseTimeout = function () {
clearTimeout( closeDropdownTimeout );
};
И вот как я привязываюсь к событиям мыши:
//- Binding mouse event to each menu items
menuItems.forEach( el => {
//- mouse enter event
el.addEventListener( 'mouseenter', function() {
stopCloseTimeout();
openDropdown( this );
}, false );
//- mouse leave event
el.addEventListener( 'mouseleave', () => startCloseTimeout(), false);
} );
//- Binding mouse event to each sub menus
menuSubs.forEach( el => {
el.addEventListener( 'mouseenter', () => stopCloseTimeout(), false );
el.addEventListener( 'mouseleave', () => startCloseTimeout(), false );
} );
Итак, как работает этот код?
Создав обработчик закрытого таймаута, мы можем контролировать, когда выпадающее окно должно закрыться или нет.
Когда мышь вводит элемент меню, это происходит:
1. Остановить текущий ток closeDropdownTimout
2. Откройте соответствующее раскрывающееся меню
когда мышь покидает элемент меню, он запускает closeDropdownTimout
.
Но как открывается раскрывающееся меню? Поскольку мы устанавливаем одно и то же действие в раскрывающемся меню, closeDropdownTimout
хорошо очищается и отменяется действие закрытия.
Для полного исходного кода вы можете проверить его на codepen http://codepen.io/ariona/pen/pENkXW
Спасибо.
Ответ 2
Вы можете просто поместить sub-menu
в menu-item
.
var menuItem = $(".menu-item");
menuItem.hover(hoverIn, hoverOut);
function hoverIn() {
var mnItemMeta = $(this)[0].getBoundingClientRect();
$(".sub-menu").css({
opacity: 1,
left: mnItemMeta.left
})
}
function hoverOut() {
$(".sub-menu").css({
opacity: 0
})
}
html, body {
background-color: #efefef;
}
.menu {
list-style: none;
padding-left: 0;
display: flex;
justify-content: center;
}
a {
display: block;
padding: 10px 20px;
text-decoration: none;
color: inherit;
}
.sub-menu {
opacity: 0;
background-color: white;
position: absolute;
transition: .2s ease;
}
.sub-menu-list {
list-style: none;
padding-left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul class="menu">
<li class="menu-item"><a href="#">Menu Item</a>
<div class="sub-menu">
<ul class="sub-menu-list">
<li><a href="#">Sub Menu 1</a></li>
<li><a href="#">Sub Menu 2</a></li>
<li><a href="#">Sub Menu 3</a></li>
<li><a href="#">Sub Menu 4</a></li>
</ul>
</div>
</li>
</ul>
Ответ 3
Вы можете добавить
.sub-menu::before{
content:'';
height: <height of menu item>
width: 100%;
position:absolute;
bottom:100%;
}
и поместите hoverOut
на .sub-menu
.
Ответ 4
если вы измените первую строку js на: var menuItem = $(".menu-item, .sub-menu");
, а затем добавьте top: 3em;
в .menu-list
css (чтобы обеспечить крошечное совпадение с div .menu
и удалить мерцание), тогда все должно быть хорошо.
var menuItem = $(".menu-item, .sub-menu");
menuItem.hover(hoverIn, hoverOut);
function hoverIn() {
var mnItemMeta = $(this)[0].getBoundingClientRect();
$(".sub-menu").css({
opacity: 1,
left: mnItemMeta.left
})
}
function hoverOut() {
$(".sub-menu").css({
opacity: 0
})
}
html,body{background-color: #efefef;}
.menu {
list-style: none;
padding-left: 0;
display: flex;
justify-content: center;
}
a {
display: block;
padding: 10px 20px;
text-decoration: none;
color: inherit;
}
.sub-menu {
opacity: 0;
background-color: white;
position: absolute;
transition: .2s ease;
top: 3em;
}
.sub-menu-list {
list-style: none;
padding-left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul class="menu">
<li class="menu-item"><a href="#">Menu Item</a>
</li>
</ul>
<div class="sub-menu">
<ul class="sub-menu-list">
<li><a href="#">Sub Menu 1</a>
</li>
<li><a href="#">Sub Menu 2</a>
</li>
<li><a href="#">Sub Menu 3</a>
</li>
<li><a href="#">Sub Menu 4</a>
</li>
</ul>
</div>
Ответ 5
Вот пример, где
- псевдо-элемент добавляется в подменю, чтобы обеспечить перекрывающуюся область для наведения. Это желтый цвет только для демонстрационных целей.
- зависание из меню и подменю устанавливает только переменную. Подменю скрыто в отдельной функции, которая оценивает переменные. Требуется небольшой тайм-аут, позволяющий перейти от одного к другому.
var menuItem = $(".menu-item");
var submenuItem = $(".sub-menu");
var hoverMenu = false;
var hoverSubmenu = false;
menuItem.hover(hoverIn, hoverOut);
function hoverIn() {
hoverMenu = true;
var mnItemMeta = $(this)[0].getBoundingClientRect();
$(".sub-menu").css({
opacity: 1,
left: mnItemMeta.left
})
}
function hoverOut() {
hoverMenu = false;
setTimeout (hide, 10);
}
submenuItem.hover(hoverSmIn, hoverSmOut);
function hoverSmIn() {
hoverSubmenu = true;
}
function hoverSmOut() {
hoverSubmenu = false;
setTimeout (hide, 10);
}
function hide() {
if (hoverMenu == false && hoverSubmenu == false) {
$(".sub-menu").css({
opacity: 0
})
}
}
html,body{background-color: #efefef;}
.menu {
list-style: none;
padding-left: 0;
display: flex;
justify-content: center;
}
a {
display: block;
padding: 10px 20px;
text-decoration: none;
color: inherit;
}
.sub-menu {
opacity: 0;
background-color: white;
position: absolute;
transition: .2s ease;
}
.sub-menu:before {
content: "";
position: absolute;
width: 100%;
left: 0px;
bottom: 100%;
height: 26px;
background-color: yellow;
}
.sub-menu-list {
list-style: none;
padding-left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul class="menu">
<li class="menu-item"><a href="#">Menu Item</a>
</li>
</ul>
<div class="sub-menu">
<ul class="sub-menu-list">
<li><a href="#">Sub Menu 1</a>
</li>
<li><a href="#">Sub Menu 2</a>
</li>
<li><a href="#">Sub Menu 3</a>
</li>
<li><a href="#">Sub Menu 4</a>
</li>
</ul>
</div>
Ответ 6
Я играл некоторое время на вашем script сегодня, начиная с вашего Fiddle, а не частичного фрагмента...
Ты был так близок...
Но дело в том, что у вас есть два разных класса родительских элементов для обработки (чтение: обработчики событий для привязки к ним)... И для обработки по-разному.
Когда вы перемещаете мышь из элемента, который открыл подменю, чтобы другое открывало, некоторые события не должны запускаться. Событие mouseout
должно происходить только в том случае, если мышь не вводит другой menu___item
или dropdown-menu__content
"достаточно быстро".
mouseenter
и mouseout
являются довольно быстрыми на триггере... быстрее, чем движение мыши.
Здесь нужна небольшая задержка в 100 мкс.
A setTimeout()
, чтобы установить dropdown-holder
в display:none
при выходе из этих элементов и a clearTimeout
при вводе.
$(".menu__item").hover(
function() {
$(".dropdown-holder").css({"display":"block"});
displaySubMenu( $(this) );
clearTimeout(NavDelay);
},
function(){
setNavDelay();
});
$(".dropdown-menu__content").hover(
function() {
clearTimeout(NavDelay);
},
function(){
setNavDelay();
});
Функция setTimout проста:
function setNavDelay(){
NavDelay = setTimeout(function(){
$(".dropdown-holder").css({"display":"none"});
},100);
}
И вот функция отображения подменю, которая не была так сильно изменена:
function displaySubMenu(element){
var itemMeta = element[0].getBoundingClientRect();
//console.log( itemMeta );
var subID = element.data('sub');
console.log(subID);
var subCnt = $(subID).find(".dropdown-menu__content").css({"display":"block"});
var subMeta = subCnt[0].getBoundingClientRect();
//console.log( subMeta );
var subCntBtm = subCnt.find(".bottom-section");
menuHoveredID = subID; // Let Keep this info in memory in a var that has global scope
$(drBg).css({
"display":"block",
"left": itemMeta.left - ((subMeta.width / 2) - itemMeta.width / 2),
"width": subMeta.width,
"height": subMeta.height
});
$(drBgBtm).css({
"top": subCntBtm.position().top
});
$(drArr).css({
"display":"block",
"left": itemMeta.left + itemMeta.width / 2 - 10
});
$(drCnt).css({
"display":"block",
"left": itemMeta.left - ((subMeta.width / 2) - itemMeta.width / 2),
"width": subMeta.width,
"height": subMeta.height
});
// Ensure the right content is displayed
$(".dropdown-menu__content").css({
"display":"none"
});
$(menuHoveredID).find(".dropdown-menu__content").css({
"display":"block"
});
}
Чтобы обеспечить отображение правильного содержимого, переменная menuHoveredID
передается функции с помощью обработчика mouseenter
menu__item
hover
.
Ваши объявления onload:
var dr = $(".dropdown__content"),
drBg = $(".dropdown__bg"),
drBgBtm = $(".dropdown__bg-bottom"),
drArr = $(".dropdown__arrow"),
drMenu = $(".dropdown-menu__content"),
drCnt = $(".dropdown__content"),
menuHoveredID ="",
NavDelay;
Я убрал unnessary и добавил два vars...
Если вы заметили, я также исправил точку с запятой/кома...;)
Рабочий кодPen здесь