Add/Remove class on mouseleave - javascript

I have a dropdown Menu opening on click and when the font-awesome fa-bars icon is clicked the fa-bars class is removed and then the font-awesome fa-times class is added.
I also have a mouseleave function that closes the dropdown container when your mouse exits. My question is to see if I can have the icon class change from fa-times to fa-bars on mouseleave.
This is the code that changes the icon on click
<i id="toggleMega" class="fa fa-bars megaNavIcon"></i>
$('#toggleMega').click(function(){
var ele = $('.megaNavIcon');
if(ele.hasClass('fa-bars')){
ele.removeClass('fa-bars')
.addClass('fa-times')
}
else{
ele.addClass('fa-bars')
.removeClass('fa-times')
}
});
This is the code that closes the container on mouseleave
$('.megaNav').on('mouseleave', function() {
$(".megaNav").collapse("hide");
});

Just add the the removeClass() and addClass() methods inside your mouseleave function like this:
$('.megaNav').on('mouseleave', function() {
$(".megaNav").collapse("hide");
$('#toggleMega').removeClass('fa-times');
$('#toggleMega').addClass('fa-bars');
});
Or you can simplify this further by using the toggleClass() method like this:
$('.megaNav').on('mouseleave', function() {
$(".megaNav").collapse("hide");
$('#toggleMega').toggleClass(function(){
return $(this).hasClass('fa-times') ? 'fa-bars' : '';
});
});

CSS should be your first choice for UI/layout functionality. A simple .myclass:hover{} rule is a super easy way to highlight items on mouseenter mouseleave events.
li:hover {
font-weight: bold;
cursor: pointer;
}
<ul>
<li>Foo</li>
<li>Bar</li>
<li>Boo</li>
<li>Yah</li>
<ul>

Related

JS - mouseover and mouseout events are inconstant

HTML
<a class="level-item like-icon">
<div class="icon is-medium">
<i class="far fa-heart" onmouseover="change(true)" onmouseout="change(false)"></i>
</div>
</a>
JS
change = (state) => {
state
? event.currentTarget.setAttribute('data-prefix', 'fas')
: event.currentTarget.setAttribute('data-prefix', 'far');
};
Goal:
To change icon (class, or in this case, attribute) when someone hovers over the icon and revert it back when the user hovers out of it. The above code seems to work but there are a couple issues. 1) It fires way to many times when I hover over it and 2) Many times, it doesn't change the attribute back to "far" (state = false). I tried attaching those events to <a> instead of <li> but the issues persist.
p.s. NOT using JQUERY
Something like this one ?
Here i adding and removing a class hover, but ti also may be any attribute or something else
window.addEventListener('mousemove', e => {
let hovered = document.querySelector('.hover');
if (e.target === hovered) return;
if (hovered) {
console.log('mouse out from', hovered);
hovered.classList.remove('hover');
}
if (!e.target.classList.contains('icon'))
return;
e.target.classList.add('hover');
console.log('mouse over on', e.target)
})
.icon {
display: inline-block;
width: 50px;
height: 50px;
transition: 100ms;
border: solid;
text-align: center;
line-height: 50px;
}
.hover {
color: red;
border-radius: 30%;
transform: rotate(10deg)
}
<div class="icon">1</div>
<div class="icon">2</div>
<div class="icon">3</div>
<div class="icon">4</div>
<div class="icon">5</div>
<div class="icon">6</div>
<div class="icon">7</div>
There are 7 'onmouse...' events...
onmousedown,
onmouseenter,
onmouseleave,
onmousemove,
onmouseout,
onmouseover,
onmouseup
... so it is important to use the right one for the job.
In the example clicking and mouse movement within the Element doesn't apply - all we want is a function to be called once when the mouse enters and the element and once agian when the mouse leaves. Therefore...
<!-- HTML -->
<a class="level-item like-icon">
<div class="icon is-medium">
<i class="far fa-heart"
onmouseenter="change(this)"
onmouseleave="change(this)"></i>
</div>
</a>
So here it seems sensible to use the onmouseenter and onmouseleave attributes to call the change() function, and in this case to passes the HTML Element under the mouse as an argument via the this keywords.
Now the function can scrutinize the element and check if it has the desired and required 'data-prefix' attribute, and if so what that attribute is set to. We can then use this condition to set/reset the 'data-prefix' attribute's value...
/* JavaScript */
change = (elem) => {
// does the Element have a 'data-prefix' attribute
// and what is it set to?
let isSet = elem.hasAttribute("data-prefix") && (
"far" === elem.getAttribute("data-prefix")
);
elem.setAttribute("data-prefix", (isSet ? "fas" : "far"));
}
However, as has already been mentioned using the Element.addEventListener() method is more robust and flexible than relying on HTML attributes like onmouse....
This sounds like a duplicate of How do I simulate a mouseover in pure JavaScript that activates the CSS ":hover"?
It's not ideal to deal with mouseover in pure JS, but here is a working example (insipired by an answer to the post I linked).
var element = document.getElementById('hoverIcon');
element.addEventListener('mouseover', function() {
console.log('Mouse over, set Font Awesome class here');
});
element.addEventListener('mouseout', function() {
console.log('Mouse out, remove Font Awesome class here');
});
var event = new MouseEvent('mouseover', {
'view': window,
'bubbles': true,
'cancelable': true
});
<a class="level-item like-icon">
<div class="icon is-medium">
<i id="hoverIcon" class="far fa-heart">ICON</i>
</div>
</a>
Here is a second version of my initial answer, this time with multiple elements.
var elements = document.getElementsByClassName("hover-icon");
var i;
for (i = 0; i < elements.length; i++) {
element = elements[i];
element.addEventListener('mouseover', function(data) {
console.log('Mouse over, set Font Awesome of ID ' + data.originalTarget.id + " here");
});
element.addEventListener('mouseout', function(data) {
console.log('Mouse out, remove Font Awesome of ID ' + data.originalTarget.id + " here");
});
}
var event = new MouseEvent('mouseover', {
'view': window,
'bubbles': true,
'cancelable': true
});
<a class="level-item like-icon">
<div class="icon is-medium">
<i id="hoverIcon1" class="hover-icon far fa-heart">ICON1</i>
<i id="hoverIcon2" class="hover-icon far fa-heart">ICON2</i>
</div>
</a>

JQuery - Not Switching Classes

It is late so I hope that I am missing something simple.
I have a div that uses a font-awesome fa-plus symbol. If clicked I want it to change to fa-minus and vice versa.
There is an additional class, add-team||remove-team, which is what the click event runs on.
It will change from + to - but not back to + when clicked a second time.
I have also done it from - to + and it doesn't change back to -.
Here is my div:
<div class="col-1 fa fa-plus add-team"></div>
And here are my simple jQuery lines:
$(".add-team").click(function () {
$(this).addClass("fa-minus remove-team").removeClass("fa-plus add-team");
});
$(".remove-team").click(function () {
$(this).addClass("fa-plus add-team").removeClass("fa-minus remove-team");
});
Hope that I'm just missing something simple. Thanks.
The problem is that your event listeners are only set to listen to elements that currently have those classes. The listeners do not dynamically change when the element's classes change. So since the element starts with add-team only the add-team listener is going to work.
What you can do is use setup a delegate event listener, a listener that is setup on a parent element that does not change, eg document and run the listeners off of that.
$(document).on('click','.add-team',function(){
//...
});
$(document).on('click','.remove-team',function(){
//...
});
But you can also just use a single event listener and use jQuery's toggleClass() to toggle all the classes in one call
$(this).toggleClass("fa-plus fa-minus add-team remove-team");
Demo
$(".col-1").click(function () {
$(this).toggleClass("fa-plus fa-minus add-team remove-team");
});
.col-1 {
font-size:24px;
font-weight:bold;
}
.fa-plus:after {
content:'+';
}
.fa-minus:after {
content:'-';
}
.add-team {
color:green;
}
.remove-team {
color:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="col-1 fa fa-plus add-team"></div>

Toggle multiple divs with multiple buttons using animate.css - jQuery

I have three different buttons with three different divs that each toggle an applicable div in same place. Trying to do 2 things.
Add class on .app-icon button when relative .app-div is showing so that I can add background color when active.
Add an animate.css effect when toggling same .app-div open and closed so that when the div disappears it slides down, rather than just abruptly disappearing.
HTML:
<i class="fa fa-calculator" aria-hidden="true"></i>
<i class="fa fa-sun-o" aria-hidden="true"></i>
<i class="fa fa-rss" aria-hidden="true"></i>
<div class="app-div">
content div 1
</div>
<div class="app-div">
content div 2
</div>
<div class="app-div">
content div 3
</div>
jQuery:
$('.app-icon').click(function() {
var index = $(this).index();
$('.app-div').eq(index).toggle().siblings('.app-div').hide();
});
//animate.css effect
$('.app-div').addClass('animated fadeInUp');
Here's what I have so far: https://jsfiddle.net/frshjb373/a5osx7y6/3/
One option is to handle an if statement checking if the icon clicked is actually active like this:
$('.app-icon').click(function() {
var index = $(this).index();
if($(this).hasClass('active')) {
$(this).removeClass('active');
$('.app-div').eq(index).addClass('fadeOutDown')
} else {
$('.app-icon').removeClass('active');
$(this).addClass('active')
$('.app-div').hide().eq(index).show().removeClass('fadeOutDown')
}
});
Updated Demo
Not sure if that's what you're looking for (it's difficult to make "no-glitchy" toggle using only animate.css)
$('.app-icon').click(function() {
$('.app-icon.active').not(this).removeClass('active');
var index = $(this).toggleClass('active').index();
$('.app-div').eq(index).toggleClass('fadeInUp fadeOutDown').show().siblings('.app-div').removeClass('fadeInUp').addClass('fadeOutDown').hide();
});
and apply style for .active class
.app-icon.active{ color:green; }
JSFiddle
"addClass" and "removeClass" (two last sentences)
I think the ".hide()" was the problem, but you also need to add and remove class for "fadeOutDown" effect.
Try this:
$('.app-icon').click(function() {
var index = $(this).index();
if($('.app-div').eq(index).is(":visible"))
{
$('.app-div').eq(index).addClass('fadeOutDown').slideUp();
}
else
{
$('.app-div').eq(index).removeClass('fadeOutDown').slideDown().siblings('.app-div').slideUp();
}
$('.app-icon.current').removeClass("current");
$(this).addClass("current");
});
EDIT:
To remove the current class by clicking itself you should move the addClass("current") inside the else statement.
Here is a working demo https://jsfiddle.net/a5osx7y6/5/
Here is the perfect finishing to Artur reply
HTML
<i class="fa fa-calculator" aria-hidden="true"></i>
<i class="fa fa-sun-o" aria-hidden="true"></i>
<i class="fa fa-rss" aria-hidden="true"></i>
<div style="position: relative">
<div class="app-div">
content div 1
</div>
<div class="app-div">
content div 2
</div>
<div class="app-div">
content div 3
</div>
</div>
CSS
.app-div {display: none; position: absolute; top: 0; left: 0;}
.app-icon .fa {transition: background-color 0.5s ease;}
.app-icon .fa:hover {background: #ccc;}
.app-icon.active {background: #CD87BA;}
JavaScript
$('.app-icon').click(function() {
$('.app-icon.active').not(this).removeClass('active');
var index = $(this).toggleClass('active').index();
$('.app-div').eq(index).toggleClass('fadeInUp fadeOutDown').show().siblings('.app-div').removeClass('fadeInUp').addClass('fadeOutDown');
});
//animate.css effect
$('.app-div').addClass('animated fadeOutDown');
Working JSFiddle

Can you transition a display property? If not, what's the best solution?

I'm trying to do a transition on a list item:
HTML
<ul>
<li class="resume"><i class="fa fa-file-text-o"></i> Resumé</li>
<li><i class="fa fa-instagram"></i> Bio</li>
<li id="backtoprojects" class="is-hidden"></i> Back to Projects</li>
</ul>
Javascript
$('#backtoprojects').removeClass('is-hidden');
// hide back-to-projects-button on click of "backtoproject"
$('#backtoprojects a').on('click', function(){
hasher.setHash();
$('#backtoprojects').addClass('is-hidden');
});
$('[data-type="projectDie"]').click(function() {
$('body').removeClass('projectLoaded');
return false;
})
;
CSS
.navbar ul li.is-hidden {
display: none;
}
So right off the bat the "Back to Projects" is hidden, it appears when a project is clicked on, and if you click on "Back to Projects" again it hides itself. I tried swapping display:none for opacity or visibility but you can still see the spacing as if "Back to Projects" was still there. I envisioned the "Back to Projects" link to slide in from the right as the entire <ul> is float: right.
Try something like this:
$('#backtoprojects').hide();
$('a').on('click', function(){
$('#backtoprojects').show('slow');
});
$('#backtoprojects').on('click', function(){
$(this).hide('slow');
});
Full code here: https://jsfiddle.net/hdbj2rec/1/
Or you can use CSS to hide #backtoprojects initially.

Prevent CSS :hover style propagation

In the following example, when I mouse over the 'X' button, the list-item hover style gets enabled as well, I do not want this to happen.
Is it possible to have a hover style on the button independent of the hover style on the list-group-item? Something like prevent the 'hover' propagation?
Is there any other way to achieve that? Maybe assembling all of this HTML/CSS/JS in a different way?
Working sample here
<ul class="list-group">
<li class="list-group-item">
Lalalalaiaia
<button class="btn btn-default btn-xs pull-right remove-item">
<span class="glyphicon glyphicon-remove"></span>
</button>
</li>
<li class="list-group-item">
Panananannaeue
<button class="btn btn-default btn-xs pull-right remove-item">
<span class="glyphicon glyphicon-remove"></span>
</button>
</li>
</ul>
CSS
.list-group-item:hover {
background: #fafafa;
cursor: pointer;
}
JavaScript
$('.list-group-item').on('click', function(){
console.log('clicked item');
});
$('.remove-item').on('click', function(e){
console.log('clicked remove-item btn');
e.stopPropagation();
});
UPDATE
The problem seems to be that when hovering the inner X button, the mouse actually doesn't leave the 'list-group-item' element, thus, it keeps the hover state.
I was able to solve it by manually dispatching mouseenter and mouseleave on the 'list-group-item' in the mouseleave and mouseenter event of the 'remove-item' button, respectively, without the need to use 'event.stopPropagation()' (except for the button click handler).
The drawback is that I need a mouseenter and a mouseleave event handler for both elements. Preferably I'd use only CSS, but that seems to be impossible.
I'm just not sure whether this is a clean solution, what do you think?
Working sample here
CSS
.list-group-item.mouseover {
background: #fafafa;
cursor: pointer;
}
.list-group-item .remove-item.mouseover {
background: #aaf;
cursor: pointer;
}
JavaScript
// LIST-ITEM EVENT HANDLERS
$('.list-group-item').on('mouseenter', function(e){
$(this).addClass('mouseover');
}).on('mouseleave', function(e){
$(this).removeClass('mouseover');
});
$('.list-group-item').on('click', function(){
console.log('clicked item');
});
// LIST-ITEM REMOVE BUTTON EVENT HANDLERS
$('.remove-item').on('mouseenter', function(e){
$(this).addClass('mouseover');
$(this).parent().mouseleave();
}).on('mouseleave', function(e){
$(this).removeClass('mouseover');
$(this).parent().mouseenter();
});
$('.remove-item').on('click', function(e){
console.log('clicked remove-item btn');
e.stopPropagation();
});
This is impossible to do with CSS only, except the not-so-clean way described by #Pointy.
You can do this with javascript by using event.stopPropagation(). So your hover style should become a class that you toggle on mouseover.
This question is a duplicate of css :hover only affect top div of nest
You can make a negation caluse like Pointy suggests but a more solid solution involves adding an extra node. The idea is that the row and the button become proper siblings since you can't style a TextNode.
<ul class="list-group">
<li class="list-group-item">
<div>Lalalalaiaia</div>
<button class="btn btn-default btn-xs pull-right remove-item">
<span class="glyphicon glyphicon-remove"></span>
</button>
</li>
<li class="list-group-item">
<div>Panananannaeue</div>
<button class="btn btn-default btn-xs pull-right remove-item">
<span class="glyphicon glyphicon-remove"></span>
</button>
</li>
</ul>
Now you can do:
.list-group-item div:hover {
background: #fafafa;
cursor: pointer;
}
You will need some extra trickery to get the button in the right place, like:
// untested
.list-group-item {
position: relative;
}
.list-group-item button {
position: absolute;
top: 5px;
left: 5px;
}
Ok so there is actually a solution that only requires the use of CSS (no HTML or JS stuff)
The following selector will only select those elements with the class "parent" on hover, which do not have a child with the class "child" that is also being hovered on.
.parent:has(:not(.child:hover)):hover {}
The only problem I can see with the :has() selector/pseudo class is browser support (especially older versions) - so before you use it check the currerrent compatibility lists to see if it fits your requirements.
I could not find an answer that worked in all cases, and was also simple to implement. Sadly, there appears to be no consistent solution that is purely CSS and/or requires special arrangements of the HTML.
Here is a jQuery solution that seems to work in all cases.
Any element with .ui-hoverable will receive a .ui-hover class that does not propagate. So you can stack .ui-hoverable elements and only the top-most under the mouse will have the .ui-hover class.
$('.ui-hoverable').each(function() {
var el = $(this);
el.on('mousemove', function () {
var parent = $(event.target).closest('.ui-hoverable');
if(parent.length && parent[0] == el[0]) {
el.addClass('ui-hover');
return;
}
el.removeClass('ui-hover');
});
el.on('mouseleave', function () {
el.removeClass('ui-hover');
});
});
This works because the mousemove event searches for the closest .ui-hoverable and if it is not the current element the .ui-hover is removed. So the top most will receive the .ui-hover and an element under it will have it removed.
Enjoy, report any problems.
Thanks,

Categories

Resources