Tricky toggle active onmouseover with Vanilla JavaScript - javascript

I have a structure like below and I want to toggle active the currently hovered .item element.
I'm using a simple Vanilla JavaScript function that I usually use for click-like situations and it works.
function myFunction(e) {
var elems = document.querySelectorAll(".hover");
[].forEach.call(elems, function(el) {
el.classList.remove("hover");
});
e.target.classList.add("hover");
}
<div class="main-container">
<div class="item hover" onmouseover="myFunction(event)">
item 1
</div>
<div class="item" onmouseover="myFunction(event)">
item 2
</div>
<div class="item" onmouseover="myFunction(event)">
item 3
</div>
</div>
So far so good, but here comes the tricky part. When the mouse goes to a sibling element the hover correctly is changing to the inner one.
I tried some CSS ticks but I can't manage to make it work, any thoughts would be much appreciated
P.S. I prefer Vanilla JavaScript than jQuery

You can use CSS to add and remove the hover. Basic code showing that and added code to toggle active so it can move around.
const menu = document.querySelector(".main-container")
menu.addEventListener("click", function (evt) {
const item = evt.target.closest(".item")
if (item) {
menu.querySelector(".item.active").classList.remove("active")
item.classList.add("active")
}
});
.main-container .item.active {
background-color: green;
}
.item,
.main-container:hover .item.active {
background-color: yellow;
transition: background-color .3s;
}
.main-container:hover .item:hover {
background-color: lime;
transition: background-color .3s;
}
<div class="main-container">
<div class="item active">
item 1
</div>
<div class="item">
item 2
</div>
<div class="item">
item 3
</div>
</div>

You don't need JS for that.
Just overwrite style when the container is hovered
.main-container:hover .item.hover {
background-color: transparent;
}
.item.hover {
background-color: red;
}
.item:hover {
background-color: red !important;
}
<div class="main-container" >
<div class="item hover">
item 1
</div>
<div class="item">
item 2
</div>
<div class="item">
item 3
</div>
</div>
See jsfiddle: https://jsfiddle.net/hs2yfxm1/

If you're trying to style this based on a hover event you should be utilizing the proper pseudo-class. You mentioned that you always want the first item to be "active", why not set an .active class that matches the format styling of :hover? For example:
SCSS
.item {
border-color: red;
&.active,
&:hover {
border-color: blue;
}
}
CSS
.item {
border-color: red;
}
.item.active,
.item:hover {
border-color: blue;
}
Note: In general, it's best practice to limit your use of JS whenever possible. If something is attainable simply with HTML and CSS, that should be the preferred implementation in most cases.
Other than the stylistic portion, just one comment on your JS. In your example, you're utilizing e.target this can be dangerous if the element has children in that the target could be the child. Since you're targeting each element individually (a lot of event listeners that you may want to consider re-working) you can make use of e.currentTarget for other JS needs.

Is this what you are after?
.active { background-color:grey; }
.item:hover { background-color:yellow; }
<div class="main-container" >
<div class="item active">
item 1
</div>
<div class="item">
item 2
</div>
<div class="item">
item 3
</div>
</div>

Related

Is it possible to select a element to hover it and it is not it's child with CSS only?

I have an <aside> and <header> and the header has child called container and cont. has some children one of them is called burger bar so what I want here is when I :hover the burger bar the <aside> will be visible so I wonder how to do that or if it is impossible I tried to do this header .container .burger_bar:hover + aside but the aside is not element beside the burger_bar so it's not going to work.
more explain...
<div class='header'>
<div class='container'>
<div class='burger_bar'>...</div>
</div>
</div>
<aside>...</aside> /* <---when hovering the burger bar this will be
transform: translate(0%) right after being transform: translate(-100%) */
If trigger and target are on the same level, you can use .trigger:hover ~ .target to target your .target element while .trigger is being hovered.
.trigger:hover ~ .target {
color: green;
}
<div class="trigger">Hover Me</div>
<div class="target">Target</div>
If your trigger and target are not on the same level, it's better to use some javascript to add a class to your target.
const trigger = document.querySelector('.trigger');
const target = document.querySelector('.target');
trigger.addEventListener('mouseover', () => {
target.classList.add('active');
});
trigger.addEventListener('mouseleave', () => {
target.classList.remove('active');
});
.target.active {
color: green;
}
<div class="parent1">
<div class="trigger">Hover Me</div>
</div>
<div class="parent2">
<div class="target">Target</div>
</div>
Or you could use :has() pseudo class but be aware if its poor coverage (only works in Safari right now)
.parent1:has(.trigger:hover) ~ .parent2 .target {
color: green;
}
<div class="parent1">
<div class="trigger">Hover Me</div>
</div>
<div class="parent2">
<div class="target">Target</div>
</div>

Change css class when hovering html element

I have a loop of divs with a class item. When mouseover on an element, active-item class is added via javascript:
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<!--on mouseover-->
<div class="item active-item"></div>
<div class="item"></div>
<div class="item"></div>
When item active-item class appears, I want to add opacity: 0 to item class, and to add opacity :1 to item active-item. I need to do it without :hover, because that its expand card and when mouse will leave item, it will be opacity:0.
No need to use JS for this task
.itemcontainer:hover .item:not(:hover) {
opacity: 0
}
<div class="itemcontainer">
<div class="item">1
</div>
<div class="item">2
</div>
<div class="item">3
</div>
</div>
So, if you already have the Js part that adds the active class, then you can do this:
.item {
opacity: 0;
}
.item.active {
opcaity: 1;
}
But if you want to avoid Js at all you can do that:
.item {
opacity: 0;
}
.item:hover {
opcaity: 1;
}

jquery remove div inside of other divs

I have a main <div> with many other divs inside like this:
[HTML]
<div class="container">
<div class="row">
<div class="col">
<div class ="deleteMe">
delete me
</div>
</div>
</div>
</div>
I want to remove the div with class name "deleteMe", i tried to remove it by using ,find() method from jquery:
$('.container').find('.row').find('.col').find('deleteMe').remove();
or
$('.container').find('.row').find('.col').removeClass('.deleteMe');
But didn't work, what is the best way to remove it?
here is the fiddle link test this exemple:
fiddle
//$('.container').find('.row').find('.col').find('deleteMe').remove();
$('.container').find('.row').find('.col').removeClass('.deleteMe');
.row {
background: #f8f9fa;
margin-top: 20px;
}
.col {
border: solid 1px #6c757d;
padding: 10px;
}
.deleteMe {
border: solid 1px #6c757d;
padding: 10px;
background: red;
color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!--
Bootstrap docs: https://getbootstrap.com/docs
-->
<div class="container">
<div class="row">
<div class="col">
<div class="deleteMe">
delete me
</div>
</div>
</div>
</div>
You do not need jquery for the job, see the following code snippet ( The setTimeout wrapper delays the deletion by 1s and only serves to see what is happening.
setTimeout ( () => {
document.querySelector(".deleteMe").remove();
}, 1000);
<div class="container">
<div class="row">
<div class="col">
<div class ="deleteMe">
delete me
</div>
</div>
</div>
</div>
Given your original code, you might want the selector to be more specific:
document.querySelector(".container > .row > .col > .deleteMe").remove(); // Adjacent sub-selectors reference elements in a parent/child relation
document.querySelector(".container .row .col .deleteMe").remove(); // The elements are in a ancestor/descendant relation, not necessarily child/parent
Try this
$(".container .deleteMe").remove();
below line will do
$( ".container .row .col .deleteMe" ).remove();
There is a little typo in your code: a . is missing before deleteMe.
$('.container').find('.row').find('.col').find('.deleteMe').remove();
correctly.
Another thing: removeClass don't remove an element with the specified class; It removes the specified class from the element.

JQuery Hamburger Menu Functions

Below is the script I am trying to write to control two functions when the website's menu button is clicked; it is a hamburger menu that toggles the menu links. The first function shows/hides the menu links and the second fades an element on the page, both activated when the menu button is clicked.
In the first function, I am having trouble creating a delay/fadeIn for the menu links. I need '.navbar-item' to fade in and out when the menu is clicked. In the second function, I need to revert the opacity to 1.0 when the menu is clicked a second time. I can not get any of the effects to occur after the first effect has completed, i.e Menu is clicked to fade in menu links and dim '.values', menu is clicked to fade out menu links and revert '.values' to 100% opacity.
<div class="container">
<section class="header">
<h2 class="title">Title
<li class="client-item"><a class="client-link" href="#"><i class="fa fa-bars"></i></a></li></h2>
</section>
<nav class="navbar" style="display: none;">
<ul class="navbar-list">
<li class="navbar-item"><a class="navbar-link" href="#" target="_top">Contact</a></li>
<li class="navbar-item navbar-link">Store</li>
</ul>
</nav>
<div class="section values">
<div class="container">
<div class="row">
<div class="one-full column">
</div>
</div>
</div>
</div>
// Main Script For Site
$(document).ready(function() {
$('.client-link').click(function() {
$('.navbar').slideToggle("fast");
$('.values').animate({opacity:'0.6'});
});
});
This answer gives how to get simultaneous animations. jQuery's own docs describe slideToggle, including the bits you'd need to set similarly to how animate would need to be set.
I might also point out that there's no reason to separate the animate calls like you have. Since they're triggered by the same thing, they should be called from the same place.
Something like this, I think:
$(document).ready(function() {
$('.client-link').click(function() {
var $this = $(this);
var opening = !$this.data('isOpen');
$this.data('isOpen',opening);
if(opening) {
// opening animations
$('.navbar').slideDown({duration:'fast',queue:false});
$('.values').animate({opacity:1},{queue:false});
} else {
// closing animations
$('.navbar').slideUp({duration:'fast',queue:false});
$('.values').animate({opacity:0},{queue:false});
}
});
});
Though you may be better off moving your animations to CSS and just toggling a class.
You were very close, you have just made some simple mistakes. Here is a JSFiddle gives you a solution to your problem: https://jsfiddle.net/nv1gytrs/1/
HTML:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<div class="client-link"></div>
<div class="navbar"></div>
<div class="values"></div>
CSS:
.client-link {
height: 100px;
width: 100px;
border: 2px solid green;
}
.navbar {
height: 100px;
width: 100px;
border: 2px solid red;
}
.values {
height: 100px;
width: 100px;
border: 2px solid blue;
transition: all 1s;
}
.fade {
opacity: 0.2;
}
JS:
// Main Script For Site
$(document).ready(function() {
$('.client-link').on("click", function() {
$('.navbar').slideToggle("fast");
$('.values').toggleClass("fade");
});
});
Of course, all of your HTML and CSS would be unique to what you are trying to accomplish, this is just an example.

Can this JS be written in pure CSS?

jQuery('.parent:visible').each(function () {
if (jQuery(this).find('.child-1').is(':hidden')) {
jQuery(this).find('.child-2').css('color', '#000')
}
});
Selecting the children are easy, separately, but since there are no if statements in CSS, I'm hoping there's some magic CSS I'm missing.
edit: fixing js as per suggestions
.parent:not(.hidden) .child-1:not(.hidden) + .child-2 perhaps?
Demo
.parent { border:1px solid red; }
.hidden { display:none; }
.parent:not(.hidden) .child-1:not(.hidden) + .child-2 {
color:green;
}
<div class="parent">
<div class="child-1">one</div>
<div class="child-2">two</div>
</div>
<div class="parent">
<div class="child-1 hidden">one</div>
<div class="child-2">two</div>
</div>
<div class="parent hidden">
<div class="child-1">one</div>
<div class="child-2">two</div>
</div>
<div class="parent">
<div class="child-1">one</div>
<div class="child-2">two</div>
</div>
If you can add classes to the elements based on their visibility, then you can do this.
.parent.visible .child-1.not-visible + .child-2 {
color: #000
}
This will check if a .child-1 inside a .parent.visible has a class of .not-visible -- if it does, then the adjacent sibling with a class .child-2 will inherit this rule.
Otherwise, you have to use JavaScript as CSS doesn't have a way to test if an element is visible or not.

Categories