22

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();
  });
8
  • 3
    Just make a style for .list-group-item:hover button that un-does the hover style for the group item itself. Commented Aug 11, 2014 at 15:07
  • 3
    @Pointy - But a rule on .list-group-item:hover button would change the CSS for the button, not the list item, which is what the OP wants to change. He wants it when you hover over the button, that the list hover doesn't activate (or doesn't look like it activates). Commented Aug 11, 2014 at 15:15
  • @j08691 oh well I guessed that he didn't want the button to be affected by the inheritable styles on the list group, but looking at those I'm not sure why; the background probably won't change the button background. Maybe it's the cursor? Commented Aug 11, 2014 at 15:19
  • @Pointy, it's the other way round, like j08691 wrote :) Commented Aug 11, 2014 at 17:32
  • 1
    @zok Oh oh I see. So you want it to darken the item when you're over it, but then when you're over the button it should not keep the item darkened. That's hard/impossible with CSS. I kind-of wonder if it wouldn't look a little weird anyway; I mean, the cursor is still over the item, after all. Commented Aug 11, 2014 at 18:02

4 Answers 4

12

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.

Edit:

It seems the provided solution doesn't work for some people / browser versions. An alternative (provided by Eric in the comments) is .parent:not(:has(.child:hover)):hover. (I'm not sure as to why both solutions work different for some browsers - as the logic is the same for both) - Chrome v122 both worked for me / Chrome v124 for Chris Hayes only the second one worked

Sign up to request clarification or add additional context in comments.

8 Comments

I was reading about this last week! It seems it's only missing in Firefox for now (among the popular ones)
@zok yeah but even in Firefox you can already activate it manually. So pretty sure it will be added there soonish.
This worked for me: .parent:not(:has(.child:hover)):hover but :has(:not did not.
@Eric looking at the logic, both should work the same - I tested both and both worked fine. If one didn't work for you, I suspect something else going on (maybe another selector of yours has some influence here - or soemthing about your setup/implementation - because in a vacuum the version I provided and the one you provided should work interchangebly)
@Lord-JulianXLII perhaps so, was still worth mentioning incase others had the same issue :)
|
6

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

1 Comment

Yeah, I guess the only way is to have an event handler to handle the mouseover event. or to place the remove button outside the li but next to it somehow, but that doesn't seem to be a good idea
3

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;
}

2 Comments

That's a good idea, and the button would be perfectly aligned by adding display: inline to the wrapping div, but the background color would be applied only to the text, not the whole list-item background..
Remove any padding from the list-item. It doesn't matter if the background is on the list-item or the div.
2

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,

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.