I have this side menu, each item with sub-items, that when clicked go to another page.
When I press the sub-item News of MAIN and I load another page, the menu collapses like in the first photo. Is there any way to keep the menu open when navigating to other pages. This below is my HTML code. I've been stuck for two days with this problem. I've tried options from similar questions here but no luck. I'm fairly new to front-end development, so any help would be appreciated.
<div id="main-menu" class="main-menu collapse navbar-collapse">
<ul class="nav navbar-nav">
<li> <i class="menu-icon fa fa-home"></i>Home </li>
<h3 class="menu-title">L&D</h3><!-- /.menu-title -->
<li class="menu-item-has-children dropdown">
<i class="menu-icon fa fa-calendar"></i>MAIN
<ul id="navigation" class="sub-menu children dropdown-menu">
<li>News</li>
<li>Sport</li>
<li>Lifestyle</li>
<li>Economy</li>
<li>Politics</li>
</ul>
</li>
<li class="menu-item-has-children dropdown">
<i class="menu-icon fa fa-bolt"></i>ABOUT
<ul class="sub-menu children dropdown-menu">
<li>Authors</li>
<li>History</li>
<li>Trivia</li>
</ul>
</li>
</ul>
</div>
var url = location.href;
$('.sub-menu').each(function() {
var $dropdownmenu = $(this);
$(this).find('li').each(function() {
if( $(this).find('a').attr('href')== url ) {
console.log( $dropdownmenu ); // this is your dropdown menu which you want to display
console.log($($dropdownmenu).parents('li')); // this is the parent list item of the dropdown menu. Add collapse class or whatever that collapses its child list
}
});
});
This code will help
*Modified
Maybe you can save the "status" of menu on a cookie or on session, like a snapshot and read it when you are loading the page.
Using javascript find the anchor that's URL matches the current URL and then toggle it's parent's visibility.
Obviously, you will need to tweak for your specific needs.
$(document).ready(function(){
//var current_url = window.location.href;
var current_url = "news";
$(".sub-menu").find("a[href='" + current_url + "']").closest("ul").addClass("show");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="main-menu" class="main-menu collapse navbar-collapse">
<ul class="nav navbar-nav">
<li> <i class="menu-icon fa fa-home"></i>Home </li>
<h3 class="menu-title">L&D</h3><!-- /.menu-title -->
<li class="menu-item-has-children dropdown">
<i class="menu-icon fa fa-calendar"></i>MAIN
<ul id="navigation" class="sub-menu children dropdown-menu">
<li>News</li>
<li>Sport</li>
<li>Lifestyle</li>
<li>Economy</li>
<li>Politics</li>
</ul>
</li>
<li class="menu-item-has-children dropdown">
<i class="menu-icon fa fa-bolt"></i>ABOUT
<ul class="sub-menu children dropdown-menu">
<li>Authors</li>
<li>History</li>
<li>Trivia</li>
</ul>
</li>
</ul>
</div>
When each main list item is expanded, it has a show class added. You could check what URL the user is going to, and if it's under that submenu, pass that into the list items classlist via your templating engine. It would look something like
<li class="menu-item-has-children dropdown {% if showClass %}show{% endif %}">
And resolve to
<li class="menu-item-has-children dropdown show">
Thus being expanded on the next page.
Edit: The same class needs to be added to the <ul> element below the parent <li>, i.e.
<li class="menu-item-has-children dropdown {% if showClass %}show{% endif %}">
...
<ul id="navigation" class="sub-menu children dropdown-menu {% if showClass %}show{% endif %}">
Related
I have this dropdown menu, I want if my menu has arrow class before ul then If I click on that parent li menu it will add a class on that li which is showMenu only that sub-menu.
this is the code:
<ul className="nav-links" >
<li>
<a href="#">
<i className='bx bx-grid-alt'></i>
<span className="link_name">Dashboard</span>
</a>
<ul className="sub-menu blank">
<li><a className="link_name" href="#">Category</a></li>
</ul>
</li>
<li onClick={subMenuToggle} className={`${subMenu ? 'showMenu' : ''}`}> // sub menu parent
<div className="iocn-link">
<a href="#">
<i className='bx bx-collection'></i>
<span className="link_name">Category</span>
</a>
<i className='bx bxs-chevron-down arrow'></i> // arrow class
</div>
<ul className="sub-menu">
<li><a className="link_name" href="#">Category</a></li>
<li>HTML & CSS</li>
<li>JavaScript</li>
<li>PHP & MySQL</li>
</ul>
</li>
<li onClick={subMenuToggle} className={`${subMenu ? 'showMenu' : ''}`}>// sub menu parent
<div className="iocn-link">
<a href="#">
<i className='bx bx-book-alt'></i>
<span className="link_name">Posts</span>
</a>
<i className='bx bxs-chevron-down arrow'></i> // arrow class
</div>
<ul className="sub-menu">
<li><a className="link_name" href="#">Posts</a></li>
<li>Web Design</li>
<li>Login Form</li>
<li>Card Design</li>
</ul>
</li>
</ul>
I tried this way set on click handler subMenuToggle into the parent submenu li if anyone clicks on that li it will set true and It will add a class 'subMenu' but when I click any sub menu parent item it becomes true and opens all the sub-menus I have.
const [subMenu, setSubMenu] = useState(false)
const subMenuToggle = () => {
setSubMenu(!subMenu)
}
Can anyone help me please?
I think you are doing it wrong. you dont need that subMenu state, just use classList of the current target element that is clicked on.
const subMenuToggle = (e)=>{
e.currentTarget.classList.toggle('showMenu');
}
and your li should be like this:
/* you can provide any default className that
this element must have beside that showMenu that you provide dynamically
*/
<li onClick={subMenuToggle} className=""> // sub menu parent
<div className="iocn-link">
<a href="#">
<i className='bx bx-collection'></i>
<span className="link_name">Category</span>
</a>
<i className='bx bxs-chevron-down arrow'></i> // arrow class
</div>
<ul className="sub-menu">
<li><a className="link_name" href="#">Category</a></li>
<li>HTML & CSS</li>
<li>JavaScript</li>
<li>PHP & MySQL</li>
</ul>
</li>
I am trying to produce a custom navigation bar. Clicking on the Services or Plans list items should produce a drop down menu. However currently when you click on the items it does not work as expected instead, it produces an error that says: 'cannot read property contains of undefined'.
I simply want to be able to select the sub-menu class and add sub-menu-active class(which I have set in my CSS to display as block). I have initially set the display property in my CSS file to 'none'.
Here is the select html file I am trying to manipulate
<nav class="custom-navigationbar-container" >
<ul class="menu">
<li class="logo">Home</li>
<li class="item">About</li>
<li class="item">Blog</li>
<li class="item special">
<a tabindex="0">Services <i class="fas fa-sort-down"></i></a>
<ul class="sub-menu" >
<li class="sub-item" >Web Design</li>
<li class="sub-item" >Web Development</li>
<li class="sub-item" >SEO</li>
<li class="sub-item" >Digital Marketing</li>
</ul>
</li>
<li class="item special has-sub-menu">
<a tabindex="0">Plans <i class="fas fa-sort-down"></i></a>
<ul class="sub-menu">
<li class="sub-item" >Freelancer</li>
<li class="sub-item" >StartUp</li>
<li class="sub-item" >Enterprise</li>
</ul>
</li>
<li class="item" >Blog</li>
<li class="item" >Contact</li>
<li class="item button" >Log In</li>
<li class="item button secondary" >Sign Up</li>
<li class="toggle"><i class="fas fa-bars" ></i></li>
</ul>
</nav>
This is the .Js code
const special = document.querySelectorAll(".special");
const specialToggle = function(){
const subMenu = this.getElementsByClassName('sub-menu');
if(subMenu.classList.contains(".sub-menu-active")){
console.log('I am removing sub-menu-active now');
ul.classList.remove('.sub-menu-active');
}else{
console.log('I am adding sub-menu-active now');
ul.classList.add('.sub-menu-active');
}
};
for(const specials of special){
specials.addEventListener('click', specialToggle)
}
Your main problem is this line:
const subMenu = this.getElementsByClassName('sub-menu');
This will return multiple elements. Or at least it will return a COLLECTION of zero or one elements.
You might consider:
const subMenu = this.getElementsByClassName('sub-menu')[0];
But this will only get the first one, even if you clicked on the second one.
So maybe it's best to return the event in the function, get the target's parent, and find the 'sub-menu' within it. Then you're good to go:
const subMenu = e.target.parentElement.getElementsByClassName('sub-menu')
Here's a working version of the code against a simplified HTML structure:
const special = document.querySelectorAll(".special");
const specialToggle = function(e){
const subMenu = e.target.parentElement.getElementsByClassName('sub-menu')[0];
if (subMenu == null) {
console.log('No sub-menu. Perhaps you have clicked on a child element?');
} else if(subMenu.classList.contains(".sub-menu-active")){
console.log('I am removing sub-menu-active now');
subMenu.classList.remove('.sub-menu-active');
} else{
console.log('I am adding sub-menu-active now');
subMenu.classList.add('.sub-menu-active');
}
};
for(const specials of special){
specials.addEventListener('click', specialToggle)
}
<ul class="menu">
<li class="logo">Home</li>
<li class="item special">
<a tabindex="0">Services <i class="fas fa-sort-down"></i></a>
<ul class="sub-menu" >
<li class="sub-item" >Web Design</li>
<li class="sub-item" >Web Development</li>
</ul>
</li>
<li class="item special has-sub-menu">
<a tabindex="0">Plans <i class="fas fa-sort-down"></i></a>
<ul class="sub-menu">
<li class="sub-item" >Freelancer</li>
<li class="sub-item" >StartUp</li>
</ul>
</li>
</ul>
I have my index calls from other HTML pages using the library CSI.js
my index:
<div class="col-6 col-md-2" id="nav">
<div data-include="./views/menu/sticky-menu.html"></div> <!-- menu -->
</div>
in menu page:
<div class="sticky-menu" id="sticky-menu">
<div class="sticky-menu-header">
<img class="logo" src="./img/logo-branco.svg" />
</div>
<ul>
<li class="active"><i class="fa fa-home"></i>Home </li>
<li><i class="fa fa-glass"></i>Eventos </li>
<li><i class="fa fa-file-image-o"></i>Alertas <span class="sticky-menu-label">4 </span></li>
<li><i class="fa fa-cog"></i>Indicadores
<ul class="submenu">
<li>Teste</li>
<li>Teste </li>
<li>
Teste
<ul class="submenu">
<li>Teste </li>
</ul>
</li>
<li>Consulting </li>
</ul>
</li>
<li>
<i class="fa fa-suitcase"></i>Grafana
<ul class="submenu">
<li>Item 0 </li>
<li>Item 1 <span class="sticky-menu-label">10 </span></li>
</ul>
</li>
</ul>
</div>
submenu have dropdown animation with jquery:
jQuery(document).ready(function() {
jQuery("#sticky-menu").jqueryAccordionMenu();
});
On the menu page (sticky-menu.html) it works fine, like this:
https://i.stack.imgur.com/oAPds.png
Notice that the + sign appears when clicked:
https://i.stack.imgur.com/9OJIu.png
However, not index.html, menu (jquery) does not work:
https://i.stack.imgur.com/tvBRR.png
When I enter the code in the console:
jQuery(document).ready(function() {
jQuery("#sticky-menu").jqueryAccordionMenu();
});
it works: https://i.stack.imgur.com/YZe8y.png
I've included the js code on both pages, I think it's silly, but I really can not. Thank you for your help.
EDIT:
I noticed that my sticky-menu.html is wearing after the script. How to solve?
https://i.stack.imgur.com/j1Pzu.png
EDIT 2 :
I solved with requireJS.
You could run some code as follows
function waitForElement(id, callback){
var checkForElement = setInterval(function(){
if(document.getElementById(id)){
clearInterval(checkForElement);
callback();
}
}, 100);
}
waitForElement("idOfElementToWaitFor", function(){
alert("element is loaded.. do stuff");
});
Where your id for your element would be #sticky-menu and your callback function would be jQuery("#sticky-menu").jqueryAccordionMenu();
Disclaimer! ... I am not real keen on front end development.
I have a nav menu that has two sub-menus. I am using the Symfony 3 framework for the application, so twig is involved. My base template has the navigation code so it should be available on all extended twig pages. Here is the html
<ul class="nav">
<!-- Main menu -->
<li class="current"><i class="glyphicon glyphicon-th"></i> Dashboard
</li>
<li><i class="glyphicon glyphicon-stats"></i> Emails</li>
<li><i class="glyphicon glyphicon-user"></i> Users</li>
<li class="submenu">
<a href="#">
<i class="glyphicon glyphicon-list"></i> Admin
<span class="caret pull-right"></span>
</a>
<ul>
<li><i class="glyphicon glyphicon-user"></i> My Profile</li>
<li>Change Password</li>
</ul>
</li>
<li class="submenu">
<a href="#">
<i class="glyphicon glyphicon-list"></i> Public Pages
<span class="caret pull-right"></span>
</a>
<ul>
<li>Home Page</li>
<li>About</li>
<li>Services</li>
<li>Contact</li>
</ul>
</li>
<li>Logout</li>
</ul>
On the dashboard page it works just as expected.When "Admin" is clicked, the sub menu opens and the sub links are displayed. Click the other, the first sub menu closes and the other opens up.
However, when I switch to another page such as "Emails" when any of the sub menus are clicked it immediately closes. I have tried multiple ways to try and fix this but it does not seem to work. Here is the javascript for "submenu"
$(".submenu > a").click(function(e) {
e.preventDefault();
var $li = $(this).parent("li");
var $ul = $(this).next("ul");
if($li.hasClass("open")) {
$ul.slideUp(350);
$li.removeClass("open");
} else {
$(".nav > li > ul").slideUp(350);
$(".nav > li").removeClass("open");
$ul.slideDown(350);
$li.addClass("open");
}
});
Thank you for your help.
I am writing my automated test scripts using selenium-webdriver, phantomJS and mocha.
My script file is javascript file by nature.
I want to wait till an element(<a>) becomes completely visible. After it becomes visible, the element will be clicked.
Let me explain in details:
There are some menus and submenus. The menu is collapsible in nature. When I click on a menu, then its corresponding submenus are displayed.
My following script first iterates(And clicks) through the menu and then iterates and displays the submenu status.
for(var iMajor = 2; iMajor <= majorLinkLast ; iMajor++)
{
majorMenuXPath = "//ul[contains(#id, 'side-menu')]/li["+iMajor+"]/a";
if(iMajor != 2)
{
driver.findElement(By.xpath(majorMenuXPath)).click();
driver.manage().timeouts().implicitlyWait(30 * 10000);
}
for(var iMinor = 1; iMinor <= minorSize[iMajor] ; iMinor++)
{
minorMenuXPath = "//ul[contains(#id, 'side-menu')]/li["+iMajor+"]/ul/li["+iMinor+"]/a";
driver.findElement(By.xpath(minorMenuXPath)).isDisplayed().then(function(elem){
console.log(elem);
});
}
The above code displays the status of the submenus like:
true
true
true
true
true
true
true
false
true
false
true
false
true
false
false
true
true
true
true
true
true
true
true
true
true
I have used implicit method for making the driver wait. But still some submenus are displayed as false(means they are not visible). They should have been visible after the parent menu got clicked.
I need some help regarding the following:
How can I wait till the submenu is visible? After each submenu
becomes visible, I need to perform some action.
Or, how can I make the submenu visible after a particular time.
Here is my HTML:
<ul id="side-menu" class="nav">
<li class="nav-header">
<img class="logo" alt="Track Revenue" src="/images/3c4939d.png">
<div class="logo-element"> TR </div>
</li>
<li class="">
<a href="#home">
<i class="fa fa-bolt"></i>
<span class="nav-label">Tr Admin Menu</span>
<span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level collapse" aria-expanded="false" style="height: 0px;">
<li>
All Users
</li>
<li>
All Companies
</li>
<li>
Devices
</li>
<li>
Send Email
</li>
<li>
Impersonate User
</li>
<li>
Test Encryption
</li>
</ul>
</li>
<li class="">
<a href="#home">
<i class="fa fa-th-large"></i>
<span class="nav-label">Campaigns</span>
<span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level collapse" aria-expanded="false" style="height: 0px;">
<li>
Overview
</li>
<li>
CPC Update
</li>
<li>
SubID Update
</li>
<li>
Add Campaign
</li>
</ul>
</li>
<li>
<a href="#home">
<i class="fa fa-bar-chart-o"></i>
<span class="nav-label">Stats</span>
<span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level collapse">
<li>
Campaign Stats
</li>
<li>
Week / Day Parting Stats
</li>
</ul>
</li>
<li>
<a href="#home">
<i class="fa fa-files-o"></i>
<span class="nav-label">Reports</span>
<span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level collapse">
<li>
Custom Data Reports
</li>
<li>
SubID Analysis Report
</li>
<li>
Scheduled Reports
</li>
</ul>
</li>
<li>
<a href="#home">
<i class="fa fa-cog"></i>
<span class="nav-label">Settings</span>
<span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level collapse">
<li>
Account
</li>
<li>
Plan Management
</li>
<li>
Campaign Groups
</li>
<li>
Affiliate Networks
</li>
<li>
Traffic Source
</li>
<li>
Manage Users
</li>
<li>
Manage Company
</li>
<li>
Blocking & Filter Rules
</li>
<li>
Domains
</li>
<li>
Campaign Maintenance
</li>
</ul>
</li>
</ul>
Note:
I am new to selenium. I need javascript procedure to solve the problem. I have no knowledge in JAVA or Python or C#
Can somebody help me please?
It is invisible for a reason, isn't it? Automate whatever a normal user would do to make that element visible. Sometimes sections of a page are hidden, because it there are multiple "views" on a single page. A user can usually navigate between those views by clicking on something or filling out forms. You have to automate the same interaction a user would do.
The following snippet helped the elements to get displayed.
minorMenuXPath = "//ul[contains(#id, 'side-menu')]/li["+iMajor+"]/ul/li["+iMinor+"]/a";
//Following snippet is stated for making the driver wait till the element is visble.
driver.wait(function()
{
return driver.isElementPresent(By.xpath(minorMenuXPath));
}, 20*1000);
driver.findElement(By.xpath(minorMenuXPath)).then(function(elem)
{
elem.isDisplayed().then(function(stat){
console.log(stat);
});
});