首页 > 解决方案 > 如何添加窗口事件以关闭打开的导航下拉菜单,但在导航项目上保持切换打开/关闭导航下拉菜单的行为?

问题描述

我已经为此工作了几天,我觉得我快到了。我继承了一个导航组件,其行为类似于任何导航——用户单击导航项以显示下拉菜单,然后单击导航项或下拉菜单将其关闭。但是,当用户单击页面上的其他任何位置时,我现在需要能够关闭任何打开的下拉菜单。

我面临的问题是我需要让我的窗口事件忽略导航。否则,单击窗口会删除用户在导航单击时关闭导航的能力。

我在下面粘贴了我的 JavaScript 代码。有人可以帮助我吗?谢谢你。

navClick() 函数已经存在。我添加了 closeMenuOnWindowClick()。

function initNav() {
const primaryNavElement = document.querySelector("#dcom-nav-primary");
const secondaryNavElement = document.querySelector("#dcom-nav-secondary");
const secondaryNavLinks = document.querySelectorAll(".dcom-nav-secondary-links");

//These variables are defined in the markup and dumped at that level. Added eslint lines below so it doesn't scream at us.
// eslint-disable-next-line
let navigationArray = [];
if(primaryNavElement){
    navigationArray = primaryNavElement.getElementsByTagName("li");
}
let i;

const navClick = function() {
    
    let navElm = this;
    //Navigation buttons are relying on the navlevel attribute in the markup to determine how they behave
    if(navElm,navigationArray.length,secondaryNavLinks){
        if(navElm.attributes.navlevel){
            if(navElm.attributes.navlevel.nodeValue == 'primary'){
                //Primary Nav Click

                //Reset and apply active link tab
                for(i = 0; i<navigationArray.length; i++){
                    navigationArray[i].desktopDOMs[0].classList.remove('dcom-c-navigation-bar__li--active');
                    navigationArray[i].desktopDOMs[1].classList.add('dcom-c-navigation-bar__secondary-row--hidden');
                }
                navigationArray[navElm.index].desktopDOMs[0].classList.add('dcom-c-navigation-bar__li--active');
                navigationArray[navElm.index].desktopDOMs[1].classList.remove('dcom-c-navigation-bar__secondary-row--hidden');
                
            } else if(navElm.attributes.navlevel.nodeValue == 'secondary'){
                //Secondary Nav Click
                let dropdown = navElm.querySelector(".dcom-c-navigation-bar__dropdown");
                if(dropdown){
                    for(i = 0; i<secondaryNavLinks.length; i++){
                        for(let ii = 0; ii<secondaryNavLinks[i].children.length; ii++){
                            let sDropdown = secondaryNavLinks[i].children[ii].querySelector(".dcom-c-navigation-bar__dropdown");
                            if(sDropdown != dropdown){
                                sDropdown.classList.add('dcom-c-navigation-bar__dropdown--hidden');
                            }
                        }
                    }
                    
                    if(dropdown.children.length > 0){ //In the event the secondary dropdown has no tertiary links, do nothing, let it navigate.

                        // If there are only 2 items, remove flex-wrap
                        if(dropdown.children.length < 3) {
                            dropdown.style.flexWrap = 'nowrap';                                    
                        }

                        if(dropdown.classList.contains('dcom-c-navigation-bar__dropdown--hidden')) {
                            
                            dropdown.classList.remove('dcom-c-navigation-bar__dropdown--hidden');
                            dropdown.style.width = "auto"; //Reset width

                            //Handle dropdown width
                            //First, find out how many columns we need;
                            let dropdownCols = [[],[],[]];
                            let c = 0;
                            let r = 0;
                            for(i = 0; i < dropdown.children.length; i++){  
                                dropdownCols[c].push(dropdown.children[i]);
                                r++;
                                if(r > 5){
                                    r = 0;
                                    c++;
                                }
                            }                        
                            //Then step through the links we have, find the largest, and set the largest size in each column
                            let colWidths = [0,0,0];
                            for(i = 0; i<dropdownCols.length; i++){
                                for(let ii = 0; ii<dropdownCols[i].length; ii++){
                                    let linkWidth = dropdownCols[i][ii].offsetWidth;                                
                                    if(linkWidth > colWidths[i])colWidths[i]=linkWidth;
                                }
                            }                        
                            //Set the width of the dropdown to the size of the largest columns
                            dropdown.style.width = (colWidths[0]+colWidths[1]+colWidths[2]+20)+"px";

                            //For IE fix we have to manually set the height. It's stupid, I know.
                            let linkHeight = dropdownCols[0][0].offsetHeight + 3; //get the height of one link element

                            //Check if its less than 6 (which wraps)
                            if(dropdown.children.length < 6){
                                //If its less than 6, make the height the height of the number of children
                                dropdown.style.height = (linkHeight * dropdown.children.length)+"px";
                            } else {
                                //Otherwise, set it to 6
                                dropdown.style.height = linkHeight * 6+"px";
                            }

                            //Check if this div is going off the screen
                            dropdown.style.right = "";
                            dropdown.style.left = "";
                            
                            var dropdownRect = dropdown.getBoundingClientRect();

                            if((dropdownRect.left + dropdown.offsetWidth) > window.innerWidth){
                                dropdown.style.right = 0;
                                dropdown.style.left = "auto";
                            }

                        } else {
                            dropdown.classList.add('dcom-c-navigation-bar__dropdown--hidden');
                            //hide it if its open
                        }
                    }
                }
            }
            /* Note: Since we don't have a Single Page Application here (haha), the assumption is that tertiary links will cause a page reload/navigation. 
            * In the event this is used in a SPA framework, can add a check here for tertiary controls, and add a click handler above.
            */
        } else {
            //Otherwise, using a switch based on the element id (for now! have had success with custom attributes before to determine button purpose that has less risk of conflict)
            switch(navElm.id) {
                default:
                    //Do nothing
            }
        }
    }
};

if(primaryNavElement,secondaryNavElement,secondaryNavLinks.length){ //check all elements exist
//Find and add click handlers to all primary nav links
    for(i = 0; i<primaryNavElement.children.length; i++){
        if(primaryNavElement.children[i].nodeName == "LI"){ //double checking I'm grabbing list items
            navigationArray[i].desktopDOMs = [];
            navigationArray[i].desktopDOMs.push(primaryNavElement.children[i]); //Assign to reference array for future use
            primaryNavElement.children[i].index = i; //I add an index here for reference
            primaryNavElement.children[i].addEventListener('click', navClick);

            //each primary nav's secondary navs
            
        }
    }

    

    //Find all secondary links bars, store reference
    
    for(i = 0; i<secondaryNavElement.children.length; i++){
        navigationArray[i].desktopDOMs.push(secondaryNavElement.children[i]);
        secondaryNavElement.children[i].index = i;
        //secondaryNavElement.children[i]
        
    }

    //For all links, add handler, push to desktopDOMS
    //console.log(secondaryNavLinks);
    for(i = 0; i<secondaryNavLinks.length; i++){
        for(let ii = 0; ii<secondaryNavLinks[i].children.length; ii++){
            secondaryNavLinks[i].children[ii].index = i;
            secondaryNavLinks[i].children[ii].addEventListener('click', navClick);    
        }
    }

}


function closeMenuOnWindowClick() {      
    //Get all dropdowns
    let menus = document.getElementsByClassName('dcom-c-navigation-bar__dropdown');
    //Convert to array
    menus = [...menus];
    let openMenu;

    //Loop through to find secondary level menu that is currently open
    menus.forEach(menu => {
        //If it's a secondary level nav and it's showing
        if(menu.attributes.navlevel = 'secondary' && !menu.classList.contains('dcom-c-navigation-bar__dropdown--hidden')){
            //Get it and label it
            openMenu = menu;
            openMenu.classList.add('dcom-c-navigation-bar__dropdown--hidden');
        }
    })      
 }       

window.addEventListener('mouseup', function() {
    closeMenuOnWindowClick();
    navClick();
}, false);  

}

module.exports = initNav;

标签: javascript

解决方案


感谢stackoverflow,我在这里和那里找到了一些片段并且能够成功!

以下是我完成解决方案的方法:

// Click anywhere on the page to close an open menu.
window.addEventListener('click', function (ev) {

// Click anywhere, excluding element with a class you want to ignore, to hide the dropdown.
    if (ev.target.className !== 'ignoreThisClass') {
    dropdown.classList.add('addYourOwnHideClass');
    }
});

推荐阅读