I have the following code.
How can I affect the status of other links when the cursor is on a link?
For example I would like that when the mouse is over a link I can affect the opacity of the other links.
I thought of a way to add the event to the link parent nav links and then check if the event is really on the link or outside.
Is the method I started good or I should approach event delegation
const nav = document.querySelector(".nav__links");
const item = Array.from( document.querySelectorAll(".nav__item"));
const link = Array.from( document.querySelectorAll(".nav__link"));
link.forEach(t => t.addEventListener('mouseover' ,function(e){
const targetLink = e.target ;
console.log(targetLink) ;
if ( t !==targetLink) {
t.style.opacity = 0.2;
}
})) ;
.nav__links {
display: flex;
align-items: center;
list-style: none;
justify-content:center;
}
.nav__item {
margin-left: 0.7rem;
}
<ul class="nav__links">
<li class="nav__item">
<a class="nav__link" href="#">Home</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#">About</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#">Testimonials</a>
</li>
If you're okay with dimming the options when the mouse is over the nav list and then not dimming the active one, you can do this just with CSS (I added a bit of transition to it, but you can remove that):
.nav__links:hover li {
opacity: 0.2;
transition: opacity 0.1s;
}
.nav__links:hover li:hover {
opacity: 1;
}
Live Example:
.nav__links {
display: flex;
align-items: center;
list-style: none;
justify-content: center;
}
.nav__links:hover li {
opacity: 0.2;
transition: opacity 0.1s;
}
.nav__links:hover li:hover {
opacity: 1;
}
.nav__item {
margin-left: 0.7rem;
}
<ul class="nav__links">
<li class="nav__item">
<a class="nav__link" href="#">Home</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#">About</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#">Testimonials</a>
</li>
</ul>
That also has the advantage that there's no flashing as you move the mouse across from one nav item to the next (going out of one li, over the ul, then into the next li).
But if you only want them dimmed when one of the nav items is hovered, I don't think we can do it just with CSS because there's no general sibling combinator (there's just the next sibling [+] or subsequent sibling [~]).
But we can let CSS do most of the work. The trick is making sure that we un-dim things when the mouse is no longer over any of the nav items. Remember that the mouse can jump out of elements (for instance, if the user Alt+Tabs to a different app, moves the mouse, and then tabs back to the page), so we have to be fairly aggressive about un-dimming things.
Here's a version that uses a passive mousemove handler on the document. Normally I'd avoid a mousemove handler on document since it will get fired a lot, but what we're doing inside the handler is really quick:
document.addEventListener("mouseover", event => {
const li = event.target.closest(".nav__links li");
if (li) {
nav.classList.add("dimmed");
} else {
nav.classList.remove("dimmed");
}
}, {passive: true});
Then we throw some CSS at it:
.nav__links.dimmed li {
opacity: 0.2;
transition: opacity 0.1s;
}
.nav__links.dimmed li:hover {
opacity: 1;
}
Live Example:
const nav = document.querySelector(".nav__links");
document.addEventListener("mouseover", event => {
const li = event.target.closest(".nav__links li");
if (li) {
nav.classList.add("dimmed");
} else {
nav.classList.remove("dimmed");
}
}, {passive: true});
.nav__links {
display: flex;
align-items: center;
list-style: none;
justify-content: center;
}
.nav__links.dimmed li {
opacity: 0.2;
transition: opacity 0.1s;
}
.nav__links.dimmed li:hover {
opacity: 1;
}
.nav__item {
margin-left: 0.7rem;
}
<ul class="nav__links">
<li class="nav__item">
<a class="nav__link" href="#">Home</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#">About</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#">Testimonials</a>
</li>
</ul>
Related
for some reason the close button on the sidebar is not showing nor working, the show sidebar works perfectly fine however the closing function is not working. im providing the html, css ans javascript related to the close function
here's my html:
<aside class="sidenav">
<div class="sidenav__close-icon">
<i class="bi bi-x-lg"></i>
</div>
<img class="sidenav__logo" src="../static/img/ManageX5.png">
<ul class="sidenav__list">
<li class="sidenav__list-item">
<a class="sidenav__list-item-items" href="{{url_for('dashboard')}}">Dashboard</a>
</li>
<li class="sidenav__list-item">
<a class="sidenav__list-item-items" href="{{url_for('profile')}}">Profile</a>
</li>
<li class="sidenav__list-item">
<a class="sidenav__list-item-items" href="{{url_for('users')}}">Users</a>
</li>
<li class="sidenav__list-item">
<a class="sidenav__list-item-items" href="{{url_for('projects')}}">Projects</a>
</li>
<li class="sidenav__list-item">
<a class="sidenav__list-item-items" href="{{url_for('sites')}}">Sites</a>
</li>
</ul>
<ul class="sidenav__logout">
<li class="list-logout-item">
<a class="sidenav__list-item-logout" href="#">logout</a>
</li>
</ul>
</aside>
this is the JS part:
const menuIconEl = $('.menu-icon');
const sidenavEl = $('.sidenav');
const sidenavCloseEl = $('.sidenav__close-icon');
/*===== Add and remove provided class names =====*/
function toggleClassName(el, className) {
if (el.hasClass(className)) {
el.removeClass(className);
} else {
el.addClass(className);
}
}
/*===== Open the side nav on click =====*/
menuIconEl.on('click', function() {
toggleClassName(sidenavEl, 'active');
});
/*===== Close the side nav on click =====*/
sidenavCloseEl.on('click', function() {
toggleClassName(sidenavEl, 'active');
});
and this is the css related to the sidebar:
.sidenav__close-icon {
position: absolute;
visibility: visible;
top: 8px;
right: 12px;
cursor: pointer;
font-size: 20px;
color: #000;
}
The reason of why you can't see the close icon isn't showing up is that you don't have the necessary styles that makes the element (.sidenav__close-icon) to be rendered/shown as an icon.
If you have it somewhere in your project, I suggest you making some tweaks like the following:
You can use jQuery's toggleClass method to toggle a class
function toggleClassName(el, className) {
el.toggleClass(className);
}
The close button/icon has CSS properties (position, top, right) that makes it positioned to top right of the page which results in not visible. You may have to add the following CSS in order to give close icon a relative position. In your case, the relativity can be inherited from its parent, the .sidenav element.
.sidenav {
position: relative;
width: 200px;
}
.sidenav__close-icon {
position: absolute;
visibility: visible;
top: 8px;
right: 12px;
cursor: pointer;
font-size: 20px;
color: #000;
}
Here's a snippet which toggles active class and demonstrates the change
const menuIconEl = $('.menu-icon');
const sidenavEl = $('.sidenav');
const sidenavCloseEl = $('.sidenav__close-icon');
/*===== Add and remove provided class names =====*/
function toggleClassName(el, className) {
el.toggleClass(className);
// Much shorter, but the following lines can also work
/*if (el.hasClass(className)) {
el.removeClass(className);
} else {
el.addClass(className);
}*/
}
/*===== Open the side nav on click =====*/
menuIconEl.on('click', function () {
toggleClassName(sidenavEl, 'active');
});
/*===== Close the side nav on click =====*/
sidenavCloseEl.on('click', function () {
toggleClassName(sidenavEl, 'active');
});
.sidenav {
position: relative;
width: 200px;
}
.sidenav__close-icon {
position: absolute;
visibility: visible;
top: 8px;
right: 12px;
cursor: pointer;
font-size: 20px;
color: #000;
}
.active {
outline: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<aside class="sidenav">
<div class="sidenav__close-icon">
<i class="bi bi-x-lg">X</i>
</div>
<img class="sidenav__logo" src="../static/img/ManageX5.png">
<ul class="sidenav__list">
<li class="sidenav__list-item">
<a class="sidenav__list-item-items" href="{{url_for('dashboard')}}">Dashboard</a>
</li>
<li class="sidenav__list-item">
<a class="sidenav__list-item-items" href="{{url_for('profile')}}">Profile</a>
</li>
<li class="sidenav__list-item">
<a class="sidenav__list-item-items" href="{{url_for('users')}}">Users</a>
</li>
<li class="sidenav__list-item">
<a class="sidenav__list-item-items" href="{{url_for('projects')}}">Projects</a>
</li>
<li class="sidenav__list-item">
<a class="sidenav__list-item-items" href="{{url_for('sites')}}">Sites</a>
</li>
</ul>
<ul class="sidenav__logout">
<li class="list-logout-item">
<a class="sidenav__list-item-logout" href="#">logout</a>
</li>
</ul>
</aside>
I am building a nav bar, where if you hover over an a item in the nav bar, it will add a class named nav-hover that adds certain styling to a when hovered.
Let me know if there is a better way to implement this, but in each li, I have added it's unique selector inside navIn()````. Which method can I use that would automatically extract the selector. Usingthis``` didn't work.
function navIn(obj) {
document.querySelector(obj).classList.add('nav-hover');
}
function navOut(obj) {
document.querySelector(obj).classList.remove('nav-hover');
}
<div class="navbar" id="navbar">
<div class="container" id="nav-position">
<ul class="nav">
<li onmouseover="navIn('#nav-position > ul > li:nth-child(1)')" onmouseout="navOut('#nav-position > ul > li:nth-child(1)')"><a data-scroll href="#about">About</a></li>
<li onmouseover="navIn('#nav-position > ul > li:nth-child(1)')" onmouseout="navOut('#nav-position > ul > li:nth-child(2)')"><a data-scroll href="#work">My Work</a></li>
<li onmouseover="navIn('#nav-position > ul > li:nth-child(1)')" onmouseout="navOut('#nav-position > ul > li:nth-child(3)')"><a data-scroll href="#">Resume</a></li>
<li onmouseover="navIn('#nav-position > ul > li:nth-child(1)')" onmouseout="navOut('#nav-position > ul > li:nth-child(4)')"><a data-scroll href="#contact">Contact</a></li>
</ul>
</div>
</div>
You can use the :hover pseudo-class to achieve the same hover effect:
EDIT: You can achieve the underline effect with just CSS. You can create the underline using pseudo-elements and apply transition effects onto it as shown below:
* {
margin: 0;
padding: 0;
}
a {
color: black;
text-decoration: none;
}
ul {
list-style: none;
display: flex;
align-items: center;
justify-content: space-around;
height: 70px;
background: rgba(0, 0, 0, 0.25);
}
/* Set the position on your <a> tag so the pseudo-element will position relative to it. */
.nav-link {
position: relative;
}
/* Create the underline element for each <a> tag */
.nav-link::before {
content: '';
position: absolute;
bottom: -10px;
left: 0;
width: 100%;
height: 2px;
background: blue;
transform: scaleX(0);
transform-origin: right;
transition: transform 300ms ease;
}
/* Apply the :hover pseudo-class on the pseudo-elements */
.nav-link:hover::before {
transform: scaleX(1);
transform-origin: left;
}
<nav>
<ul class="nav">
<li><a class="nav-link" href="#">About</a></li>
<li><a class="nav-link" href="#">My Work</a></li>
<li><a class="nav-link" href="#">Resume</a></li>
<li><a class="nav-link" href="#">Contact</a></li>
</ul>
</nav>
I'm currently in the middle of creating a responsive navigation. I've managed to finish and trying to fix an issue. Whenever I tried resizing the browser, it seems the toggleClass seems to be triggering multiple times. If refresh the browser it works OK, but after resizing it seems to trigger a couple of times in one click.
Here is the code that I have been working on.
JSFiddle - https://jsfiddle.net/kvpyzbxr/1/
<header>
<ul class="navigation secondary-navigation">
<li>
Schools
</li>
<li>
Faculty
</li>
<li>
Research
</li>
<li>
Contact Us
</li>
</ul>
<ul class="navigation primary-navigation">
<li>
Programs
<ul>
<li>Degree Programs</li>
<li>Master in Business Administration</li>
<li>Executive Master in Business Administration</li>
<li>Master in Entrepreneurship</li>
<li>Master of Science and Innovation and Business</li>
<li>Master in Development Management</li>
</ul>
</li>
<li>
Admissions
<ul>
<li>How to Apply</li>
<li>Application Form</li>
<li>Scholarship and Financial Aid</li>
</ul>
</li>
<li>
About Us
<ul>
<li>Why AIM</li>
<li>Leadership</li>
<li>Network and Alliances</li>
<li>Our Brand Story</li>
</ul>
</li>
<li>
News
</li>
<li>
Alumni
<ul>
<li>AIM Leader Magazine</li>
<li>My AIM Connect</li>
<li>Triple A Awardees</li>
</ul>
</li>
<li>
Give
<ul>
<li>Make A Gift</li>
</ul>
</li>
</ul>
</header>
<script>
$(document).ready(function() {
function detectMobile() {
if ($(window).width() < 1080) {
$('header').addClass('mobile');
$('.secondary-navigation').insertAfter('.primary-navigation');
}
else {
$('header').removeClass('mobile');
$('.secondary-navigation').insertBefore('.primary-navigation');
}
$('.navigation li').on('click', function() {
console.log('open');
$(this).toggleClass('expand-menu');
})
}
detectMobile();
$(window).resize(function() {
detectMobile();
})
})
</script>
header {
max-width: 1336px;
margin: 0 auto;
}
header .navigation {
padding: 10px 0;
clear: both;
}
header .navigation li {
padding: 10px;
display: inline-block;
position: relative;
font-family: 'Arial', sans-serif;
background-color: #272041;
color: #fff;
float: left;
}
header .navigation li a {
color: #fff;
}
header .navigation li ul {
position: absolute;
top: 0;
left: 0;
padding-top: 30px;
display: none;
}
header .navigation li ul li {
background-color: #231d39;
color: #95939e;
}
header .navigation li:hover ul {
display: block;
}
header.mobile .navigation li {
display: block;
float: none;
}
header.mobile .navigation li ul {
position: static;
display: none;
height: 0;
}
header.mobile .navigation li.expand-menu ul {
height: initial;
display: block;
}
That's because you add the click-event several times. Every time detectMobile() is called you bind a click event to $('.navigation li'). So just move
$('.navigation li').on('click', function() {
console.log('open');
$(this).toggleClass('expand-menu');
})
outside of your detectMobile() function.
I successfully created this not so bad css-only dropdown. I'm trying to hide the .submenutwo so that it is only visible when .submenu is on hover. If we can do this with css only that would be nice. But if javascript or jQuery can help it's fine.
/* define a fixed width for the entire menu */
.navigation {
min-width: 300px;
}
/* reset our lists to remove bullet points and padding */
.mainmenu,
.submenu {
list-style: none;
padding: 0;
margin: 0;
}
/* make ALL links (main and submenu) have padding and background color */
.mainmenu a {
display: block;
background-color: #CCC;
text-decoration: none;
padding: 10px;
color: #000;
}
/* add hover behaviour */
.mainmenu a:hover {
background-color: #C5C5C5;
}
/* when hovering over a .mainmenu item,
display the submenu inside it.
we're changing the submenu's max-height from 0 to 200px;
*/
.mainmenu li:hover .submenu {
display: block;
min-height: 200px;
height: auto;
}
/*
we now overwrite the background-color for .submenu links only.
CSS reads down the page, so code at the bottom will overwrite the code at the top.
*/
.submenu a {
background-color: #999;
}
/* hover behaviour for links inside .submenu */
.submenu a:hover {
background-color: #666;
}
/* this is the initial state of all submenus.
we set it to max-height: 0, and hide the overflowed content.
*/
.submenu {
overflow: hidden;
max-height: 0;
-webkit-transition: all 0.5s ease-out;
}
<nav class="navigation">
<ul class="mainmenu">
<li>Apples
<ul class="submenu">
<li>Green Apples
<ul class="submenutwo">
<li class='listOptionLvlThree'> Option 3
</li>
<li class='listOptionLvlThree'> Option 3
</li>
<li class='listOptionLvlThree'> Option 3
</li>
</ul>
</li>
</ul>
</li>
<li>Oranges
<ul class="submenu">
<li>Option 2
</li>
<li>Option 2
</li>
<li>Option 2
</li>
</ul>
</li>
<li>Grapes
<ul class="submenu">
<li>Purple Grapes
<ul class="submenutwo">
<li class='listOptionLvlThree'> Option 3
</li>
<li class='listOptionLvlThree'> Option 3
</li>
<li class='listOptionLvlThree'> Option 3
</li>
</ul>
</li>
<li>Green Grapes
</li>
<li>Red Grapes
</li>
</ul>
</li>
</ul>
</nav>
.submenutwo {
display: none }
.submenu:hover .submenutwo {
display: initial }
Here's how you can do it in css. Basically, what's happening here is. At first, you just need to hide .submenutwo. After the hover triggered, you just need to bring back the display to the default or even other display value will do.
may be this will help u
/* define a fixed width for the entire menu */
.navigation {
min-width: 300px;
}
.submenutwo {
display: none }
.submenu:hover .submenutwo {
display: initial }
/* reset our lists to remove bullet points and padding */
.mainmenu, .submenu {
list-style: none;
padding: 0;
margin: 0;
}
/* make ALL links (main and submenu) have padding and background color */
.mainmenu a {
display: block;
background-color: #CCC;
text-decoration: none;
padding: 10px;
color: #000;
}
/* add hover behaviour */
.mainmenu a:hover {
background-color: #C5C5C5;
}
/* when hovering over a .mainmenu item,
display the submenu inside it.
we're changing the submenu's max-height from 0 to 200px;
*/
.mainmenu li:hover .submenu {
display: block;
min-height: 200px;
height:auto;
}
/*
we now overwrite the background-color for .submenu links only.
CSS reads down the page, so code at the bottom will overwrite the code at the top.
*/
.submenu a {
background-color: #999;
}
/* hover behaviour for links inside .submenu */
.submenu a:hover {
background-color: #666;
}
/* this is the initial state of all submenus.
we set it to max-height: 0, and hide the overflowed content.
*/
.submenu {
overflow: hidden;
max-height: 0;
-webkit-transition: all 0.5s ease-out;
}
<nav class="navigation">
<ul class="mainmenu">
<li>Apples
<ul class="submenu">
<li>Green Apples
<ul class="submenutwo">
<li class='listOptionLvlThree'> Option 3</li>
<li class='listOptionLvlThree'> Option 3</li>
<li class='listOptionLvlThree'> Option 3</li>
</ul>
</li>
</ul>
</li>
<li>Oranges
<ul class="submenu">
<li>Option 2</li>
<li>Option 2</li>
<li>Option 2</li>
</ul>
</li>
<li>Grapes
<ul class="submenu">
<li>Purple Grapes
<ul class="submenutwo">
<li class='listOptionLvlThree'> Option 3</li>
<li class='listOptionLvlThree'> Option 3</li>
<li class='listOptionLvlThree'> Option 3</li>
</ul>
</li>
<li>Green Grapes</li>
<li>Red Grapes</li>
</ul>
</li>
</ul>
</nav>
Microsoft suggests using aria-haspop="true" to simulate hover on touch-enabled devices.
This is also the correct use in the W3 roles model spec.
Without including aria-haspop="true" I am unable to keep a submenu open when I click, and when I hold down a click it will fire a "right-click".
Codepen Example
HTML
<ul class="menu">
<li aria-haspopup="false">
aria-haspopup="false"
<ul class="sub-menu">
<li>Sub Menu Item 1</li>
<li>Sub Menu Item 2</li>
<li>Sub Menu Item 3</li>
</ul>
</li>
<li>
Menu Item 2
</li>
<li aria-haspopup="true">
aria-haspop="true"
<ul class="sub-menu">
<li>Sub Menu Item 1</li>
<li>Sub Menu Item 2</li>
<li>Sub Menu Item 3</li>
</ul>
</li>
</ul>
CSS
.menu > li {
display: inline-block;
position: relative;
background: #1abc9c;
}
.menu > li > a {
padding: 20px;
background: #1abc9c;
display: inline;
float: left;
}
.menu > li:hover .sub-menu {
display: block;
position: absolute;
background: #1abc9c;
top: 50px;
left: 0;
}
.menu .sub-menu {
display: none;
}
.menu .sub-menu li {
padding: 20px;
width: 140px;
}
Let's try to make the web a less accessible place. Is there any way to have the sub-menu work just as well without adding aria tags on a windows surface device? Preferably using only CSS. Javascript would be acceptable but less than ideal.
In the same Microsoft guide you linked, they recommend using the onclick event to show content.
The best practice is to not use hover to hide content that a user can interact with. Instead, consider using the onclick event to toggle the visibility.
The challenge, of course, is that your menu items are also links, and clicking on a link is likely to take you to another page. Only if they really are dead anchors that go nowhere (as in your example code) is it safe to use their onclick events to show or hide the submenus.
Here's some example code that uses onclick to show the submenus instead of using CSS :hover. Note that my example directly assigns the handlers for the sake of brevity, but better practice would be to use the addEventListener() method.
var parentMenuItems = document.querySelectorAll(".menu > li");
var len = parentMenuItems.length;
while (len--) {
parentMenuItems[len].onclick = showSubMenu;
parentMenuItems[len].onmouseenter = showSubMenu;
parentMenuItems[len].onmouseleave = hideSubMenu;
}
function showSubMenu() {
this.querySelector(".sub-menu").style.display = "block";
}
function hideSubMenu() {
this.querySelector(".sub-menu").style.display = "none";
}
.menu > li {
display: inline-block;
position: relative;
background: #1abc9c;
}
.menu > li > a {
padding: 20px;
background: #1abc9c;
display: inline;
float: left;
}
.menu > li .sub-menu {
position: absolute;
background: #1abc9c;
top: 50px;
left: 0;
}
.menu .sub-menu {
display: none;
}
.menu .sub-menu li {
padding: 20px;
width: 140px;
}
<ul class="menu">
<li aria-haspopup="false">
aria-haspopup="false"
<ul class="sub-menu">
<li>Sub Menu Item 1</li>
<li>Sub Menu Item 2</li>
<li>Sub Menu Item 3</li>
</ul>
</li>
<li aria-haspopup="true">
aria-haspop="true"
<ul class="sub-menu">
<li>Sub Menu Item 1</li>
<li>Sub Menu Item 2</li>
<li>Sub Menu Item 3</li>
</ul>
</li>
</ul>