Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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();
  });
like image 500
zok Avatar asked Aug 11 '14 15:08

zok


3 Answers

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

like image 74
Issam Zoli Avatar answered Oct 22 '22 04:10

Issam Zoli


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;
}
like image 20
Halcyon Avatar answered Oct 22 '22 05:10

Halcyon


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,

like image 2
Reactgular Avatar answered Oct 22 '22 03:10

Reactgular