Prevent CSS :hover style propagation - javascript

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,

Related

Add/Remove class on mouseleave

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>

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>

Need slide out buttons to work separately. When one button is clicked both do the task.

Hello I have two buttons that I want when one is clicked that it slides out to show more information. When one button is clicked both slide out. I changed the second button classes to button2 and inner2 and doubled the javascript and changed classes in that to button2 and inner2 but that really made things get weird.
<a class="text button1 btn btn-primary btn-lg">Learn more<a class="link inner is-hidden btn btn-primary btn-lg" href="#">Movie Data</a><a class="link inner is-hidden btn btn-primary btn-lg" href="#">Full Trailer</a><a class="link inner is-hidden btn btn-primary btn-lg" href="#"More Reviews</a><a class="link inner is-hidden btn btn-primary btn-lg" href="#">Photos</a></a>
$('.button1').on('click', function() {
animateDiv();
})
$(document).keyup(function(e) {
if (e.keyCode === 27 && $('.inner').hasClass('visible')) {
animateDiv();
}
})
function animateDiv() {
$('.inner').toggleClass('visible');
$('.inner').animate({
width: 'toggle',
}, 0);
}
In animateDiv, you're referring to .inner which will match both buttons, or at least is going to match every element with that class. Unless you somehow specify which outer button it's referring to, it won't work.
Try passing the button into your function, and then scoping it:
$('.button1').on('click', function() {
animateDiv($(this));
})
function animateDiv(button) {
$('.inner', button).toggleClass('visible');
$('.inner', button).animate({
width: 'toggle',
}, 0);
}
You'll still have an issue with your other bit of code, however, because there's no way that it could know which button you're referring to on a keyup for the whole document. The only way of not having it do both is to scope the keyup to the button too:
$('.button1').keyup(function(e) {
if (e.keyCode === 27 && $('.inner', $(this)).hasClass('visible')) {
animateDiv($(this));
}
})
Edit:
Actually, I thought your HTML was formatted differently. It looks odd. Is there even a button2? Why are there so many .inners inside .button1?
I thought at first that you had several cases of .inner divs inside of .button123 divs. Difficult to tell when it's all on one line. But now wondering if your HTML is mangled or there's something missing in your question?

Use button to expand div, then scroll the page to that div

I have a div at the bottom of the page on every page of the site (footer). I have a button to expand that div, but I want it to also scroll the page down so that the user can actually see the expanded content.
Currently, I have:
$(document).ready(function () {
$("#footerContent").on("hide.bs.collapse", function () {
$(".btn").html('INFO <span class="glyphicon glyphicon glyphicon-plus"></span>');
});
$("#footerContent").on("show.bs.collapse", function () {
$(".btn").html('INFO <span class="glyphicon glyphicon-minus"></span>');
});
});
.btn-success, .btn-success:hover, .btn-success:active {
color: #848484;
background-color: #FFFFFF;
border-color: #fff;
}
<script src="http://able.thebrewroom.com/bootstrap/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button type="button" class="btn btn-success" data-toggle="collapse" data-target="#footerContent">INFO <span class="glyphicon glyphicon-plus"></span>
</button>
<div id="footerContent" class="collapse">some content here</div>
Yes, I know, this is poor UX, which I have tried to explain to the designer, but they want to do it anyway. I just want the button to expand the DIV, and then for the page to scroll down so that I can actually see the content. Thanks!
What you need is a scrollTo function in think, check out the below link they've used it in a innovative way in the example section,
http://demos.flesler.com/jquery/scrollTo/

How to indicate that that something has been clicked by changing color of something else?

Here I want the color of text +Timer to change once user clicks on stopwatch or timer. I tried to use some JavaScript but could not implement it.
Here is my code
$(".icon-clock").shieldButton({
events: {
click: function () {
clearInterval(timer);
startTime = Date.now();
timer = setInterval(updateProgress, 100);
$(".colorchange").color(rgb(#0F0));
//It is not going to work I know
}
}
});
Here is the timer part
<li><a href="#" class="dropdown-toggle icon-plus" data-toggle="dropdown">
<span data-placement="bottom" title="Show Timer" class="colorchange">+ Timer </span></a>
<ul class="dropdown-menu timer-wrapper" style=" padding:5px;">
<div class="btn-group">
<button title="" type="button" class="btn btn-default b_fix icon-stopwatch" onclick="start(0);" data-original-title="Stopwatch"></button>
<button title="" type="button" class="btn btn-default b_fix icon-clock" onclick="start(1);" data-original-title="Timer"></button>
</div>
You are trying to use a method called color() in jQuery, but I'm afraid this method doesn't exists. Instead, use the method css(), which is made to change css properties:
$(".colorchange").css('color', '#00ff00');
Further, to make your code more reusable, I suggest you to create a class for this purpose and add it to the element with addClass(). Unless you have to change to many different colors.
Try:
$(".colorchange").css('color: #0F0');
You may use jQuerys css method even with an object with several style definitions:
$('.colorchange').css({
'color' : '#0f0',
'font-weight' : bold
});
It would be an even better solution if you defined several styles for the element that should change the color. You would only need to change the objects class name. Put this into your css:
.colorchange {
color: #666;
font-weight: normal;
}
.colorchange.highlight {
color: #0f0;
font-weight: bold;
}
And this in your javascript:
$('.colorchange').addClass('highlight');
This solution would be more future proof as you could manage all the styles in your css and all the logic in your javascript.

Categories

Resources