Prevent CSS :hover style propagation

25,811

Solution 1

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

Solution 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,

Solution 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;
}
Share:
25,811
zok
Author by

zok

Updated on July 09, 2022

Comments

  • zok
    zok almost 2 years

    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();
      });
    
  • zok
    zok almost 10 years
    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..
  • zok
    zok almost 10 years
    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
  • Halcyon
    Halcyon almost 10 years
    Remove any padding from the list-item. It doesn't matter if the background is on the list-item or the div.