代码笔中有一个名为“Flex Priority Menu#1.2”的项目是一个华丽的菜单,当它有许多链接或“调整大小”窗口时,它隐藏并放入一个下拉菜单[更多...] 。纯,下拉列表打开并关闭,点击其自身。但我也希望这个下拉菜单在点击身体其他任何地方时关闭。
有人可以帮我一把吗?
谢谢。
// Variables for the priority nav
const priorityContainer = document.querySelector('.js-priority-menu');
const priorityMenu = priorityContainer.querySelector('.priority');
const overflowMenu = priorityContainer.querySelector('.overflow');
const overflowTrigger = priorityContainer.querySelector('.js-overflow-trigger');
const menuItems = priorityMenu.children;
const delay = 100;
let throttled = false;
let breakout = [];
let timeout = 0;
// Variables for the trigger svg
const mySvg = document.querySelector('.js-svg-gradient');
const myGradient = mySvg.querySelector('.linearGradient');
const colours = [];
// Helpers could be shipped as part of an Object Literal/Module
const throttle = (callback, delay, scope) => {
if (!throttled) {
callback.call(scope);
throttled = true;
setTimeout(function() {
throttled = false;
}, delay);
}
};
// Initialise
const initialise = () => {
checkNav();
addListeners();
makeStepGradient(colours);
}
/* return the current width of the Navigation */
const getPriorityWidth = () => {
return priorityMenu.offsetWidth + 1;
}
/* return the break point of a menu item */
const getItemBreakPoint = item => {
return item.offsetLeft + item.offsetWidth;
}
/* test breakpoint against menu width */
const itemBreaks = (breakPoint, menuWidth) => {
return breakPoint > menuWidth;
}
/* test menuWidth against breakOut */
const itemBreaksOut = (index, priorityWidth) => {
if (breakout[index] < priorityWidth) {
return true;
}
}
/* move item to overflow menu */
const addToOverflow = (item, itemBreakPoint) => {
overflowMenu.insertBefore(item, overflowMenu.firstChild);
breakout.unshift(itemBreakPoint);
}
/* remove from the overflwo menu */
const removeFromOverflow = (breaksOut) => {
for (let item of breaksOut) {
breakout.shift();
priorityMenu.appendChild(item);
}
}
/* Set button visibility */
const checkTriggerHidden = (value) => {
if (value.toString() != overflowTrigger.getAttribute('aria-hidden')) {
overflowTrigger.setAttribute('aria-hidden', value);
checkNav();
}
}
/* Check priority and overflow */
const checkNav = () => {
/* check priorityMenu */
let priorityWidth = getPriorityWidth();
/* Iterate over the priority menu */
let priorityIndex = menuItems.length;
while (priorityIndex--) {
let item = menuItems[priorityIndex];
let itemBreakPoint = getItemBreakPoint(item);
if (itemBreaks(itemBreakPoint, priorityWidth)) {
addToOverflow(item, itemBreakPoint);
//add colour to svg
console.log(item);
console.log(window.getComputedStyle(item).backgroundColor);
let bgColour = window.getComputedStyle(item).backgroundColor;
addToSVG(bgColour);
};
};
/* iterate the overflow */
let overflowIndex = overflowMenu.children.length;
let breaksOut = [];
while (overflowIndex--) {
if (itemBreaksOut(overflowIndex, priorityWidth)) {
breaksOut.unshift(overflowMenu.children[overflowIndex]);
// remove colour from svg
removeFromSVG();
}
}
removeFromOverflow(breaksOut);
/* check the trigger visibility */
checkTriggerHidden(breakout.length == 0);
makeStepGradient(colours);
}
/* Add Event listeners */
const addListeners = () => {
window.addEventListener('resize', () => {
//throttle
throttle(checkNav, delay);
//debounce
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
checkNav();
}, delay);
})
overflowTrigger.addEventListener('click', () => {
overflowMenu.setAttribute('aria-hidden', overflowMenu.getAttribute('aria-hidden') === 'true' ? 'false' : 'true');
overflowTrigger.classList.toggle('active');
})
}
/* SVG indicator on the trigger */
/* Add to colour array */
const addToSVG = bgColour => {
colours.unshift(bgColour);
}
const removeFromSVG = bgColor => {
colours.shift();
}
const createStop = (offset, index, colour) => {
let offsetValue = offset * index;
let stop = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
stop.setAttribute('offset', offsetValue + '%');
stop.setAttribute('stop-color', colour);
return stop;
}
const makeStepGradient = colourArray => {
// Calculate offset values (%)
const colourCount = colourArray.length;
const offset = 100/colourCount;
myGradient.innerHTML = "";
// Loop the colours
colourArray.forEach((colour, index) => {
let stop1 = createStop(offset, index, colour);
let stop2 = createStop(offset, index + 1, colour);
myGradient.appendChild(stop1);
myGradient.appendChild(stop2);
});
}
/* Initilaise the menu */
initialise();
/* Structural */
.priority-menu {
display: flex;
flex-flow: row nowrap;
position: relative;
}
.priority-menu nav {
flex-basis: 100%;
display: flex;
text-align: center;
}
.priority-menu nav a {
white-space: nowrap;
text-align: center;
}
.priority-menu .priority {
overflow: hidden;
}
.priority-menu .priority a {
flex-basis: 100%;
display: block;
}
.priority-menu .overflow {
position: absolute;
top: 100%;
right: 0;
display: block;
}
.priority-menu .overflow a {
display: block;
}
.priority-menu [aria-hidden="true"] {
display: none;
}
.priority-menu [aria-hiiden="false"] {
display: true;
}
/* Decorative */
body {
padding: 12px;
background: #2B3440;
font-family: Arial, Helvetica, sans-serif;
color: #cbe4ed;
}
a {
color: white;
}
:focus {
outline: none;
}
.priority-menu nav a {
text-decoration: none;
padding: 15px 30px;
color: #2B3440;
}
.priority-menu nav a.menu10 {
background-color: #A8E6CE;
}
.priority-menu nav a.menu09 {
background-color: #FFD3B5;
}
.priority-menu nav a.menu08 {
background-color: #f7e651;
}
.priority-menu nav a.menu07 {
background-color: #DCEDC2;
}
.priority-menu nav a.menu06 {
background-color: #A8E6CE;
}
.priority-menu nav a.menu05 {
background-color: #FF8C94;
}
.priority-menu nav a.menu04 {
background-color: #FFAAA6;
}
.priority-menu nav a.menu03 {
background-color: #FFD3B5;
}
.priority-menu nav a.menu02 {
background-color: #DCEDC2;
}
.priority-menu nav a.menu01 {
background-color: #A8E6CE;
}
.priority-menu .overflow {
border-top: 2px solid #2B3440;
}
.priority-menu .overflow-trigger {
position: relative;
background: #2B3440;
padding: 12px 10px;
border: none;
border-left: 2px solid #2B3440;
font-size: 16px;
}
.priority-menu .overflow-trigger .svg-gradient {
width: 73px;
position: absolute;
top: 0;
left: 0;
height: 100%;
fill: url(#linearGradient);
}
.priority-menu .overflow-trigger span {
position: relative;
}
.priority-menu .overflow-trigger.active {
color: #cbe4ed;
}
.priority-menu .overflow-trigger.active .svg-gradient {
fill: #2B3440;
height: 46px;
border: 1px solid #cbe4ed;
}
.blurb {
margin: 50px auto;
width: 400px;
}
<div class="js-priority-menu priority-menu">
<nav class="priority">
<a href="#" class="menu01">Menu Item 1</a>
<a href="#" class="menu02">Menu Item 2</a>
<a href="#" class="menu03">Menu Item 3</a>
<a href="#" class="menu04">Item Item 4</a>
<a href="#" class="menu05">Menu Item 5</a>
<a href="#" class="menu06">Item Item 6</a>
<a href="#" class="menu07">Menu Item 7</a>
</nav>
<button class="js-overflow-trigger overflow-trigger" aria-hidden="true">
<svg class="js-svg-gradient svg-gradient" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="linearGradient" class="linearGradient"
x1="0" y1="0" x2="100%" y2="0" spreadMethod="pad">
<stop offset="50%" stop-color="#000"></stop>
</linearGradient>
</defs>
<rect x="0" y="0" width="100%" height="100%" rx="0" ry="0"/>
</svg>
<span>More...</span>
</button>
<nav class="overflow" id="overflow" aria-hidden="true" data-behaviour="menu"></nav>
</div>
<div class="blurb">
<h1>Flex Priority Menu #1.2</h1>
<p> Resize window to trigger the overflow nav drop down</p>
<p>Pretty much the same as <a href='https://codepen.io/kungfuyou/pen/RaJaNZ'>Flex Priority Nav #1.1</a> but with a dynamic svg on as a menu indicator.</p>
<p>To do: open / close state for the Menu trigger.</p>
</div>
答案 0 :(得分:0)
You can attach a listener to whole document body and hide the overflow menu when user clicks anywhere but on the More...
button (which is handled by another function).
const addListeners = () => {
//Other Listeners ...
document.body.addEventListener('click', (e) => {
if(e.target != overflowTrigger && e.target.parentNode != overflowMenu) {
overflowMenu.setAttribute('aria-hidden', 'true');
overflowTrigger.classList.remove('active');
}
})
}
In case you want it to work with right clicks too:
const addListeners = () => {
//Other Listeners ...
(['click','contextmenu']).forEach(event => {
document.body.addEventListener(event, (e) => {
if(e.target != overflowTrigger && e.target.parentNode != overflowMenu) {
overflowMenu.setAttribute('aria-hidden', 'true');
overflowTrigger.classList.remove('active');
}
})
})
}