How do I highlight only the innermost table row of a nested table?
Asked Answered
A

3

6

I have several nested tables and I want to highlight the innermost row that is below the mouse pointer. How can I do that?

Some pointers: I use nested tables to display recursive tabular data. The tables can be nested 10 levels deep. The nesting is just like you would expect:

<table><tr><td>
   <table><tr><td>
      <table><tr><td>
          ...

There are can be rows which don't have nested tables. I want the highlight on the deepest / innermost <tr> that is under the mouse cursor.

I can use CSS or jQuery.

Albumen answered 18/9, 2013 at 14:22 Comment(4)
please share the relevant html sampleCystic
@ArunPJohny: This is a standard nested table. Nothing strange or fancy about it. My guess the answer is a one-line CSS selector that sets prevents the highlight of the outer rows.Albumen
There are lots of strange things in regards to nested tables. The nested table could have multiple rows. Perhaps the question is worded awkwardly.Aleron
@AaronDigulla, I added a solution that I find nice, would love to get your feedback.Sculpturesque
S
3

I would like to propose something slightly more elegant (at least shorter):

Using delegated mouse events:

$('#mainTable').on('mouseenter mouseleave', 'tr', {el: false}, function (e) {
    var hl = e.data.el;
    hl && hl.removeClass('hover');

    e.data.el = (e.type === 'mouseenter') ?
        $(this).addClass('hover') :
        $(this).parent().closest('tr:hover').addClass('hover');
});

It stores the currently highlighted node in the (persistent) delegated data object and handles the mouse events, as follows:

  • If the mouse enters an element (the innermost hovered tr), remove the current highlight and highlight current element.
  • If the mouse leaves an element, highlight the closest hovered ancestor tr instead of the current one.

The main advantages of solutions using event delegation (such as $.delegate() and $.on() with a selector) are attaching only a single event listener (compared to potentially dozens, hundreds or more using traditional, per-element, methods) and being able to support dynamic changes to the element.

I chose this solution over the use if the mouseover event since I believe the enter/leave events should provide better performance, as they do not bubble.

JSFiddle.

Note:

It has a problem with jQuery 1.9.x but works with the rest, as far as I tested, including newer and older releases. This is due to an issue with the :hover pseudo-selector in that version.


CSS4

CSS level-4 has a suggested feature that can enable this behavior using CSS only:

tr, !tr:hover tr:hover {
    background-color: transparent;
}
tr:hover {
    background-color: #DDFF75;
}

Of course, since this feature is not currently final and is not supported by any major browser at the moment, this section will serve as future reference.

Sculpturesque answered 18/9, 2013 at 19:19 Comment(1)
+1 looks really interesting. I especially like the "just one listener" and "no need to reinstall them when the DOM changes". I'm a bit unhappy about the jQuery 1.9.x bug but I can upgrade to 1.10, so it's just a minor problem.Albumen
G
1

Using javascript mouse events, the event target should be the deepest element:

$('tr').mouseover(function(e){
    $(e.target).parents('tr').removeClass('hover').first().addClass('hover');
});

And this to clear when mouse leaves the table:

$('#main-table').mouseout(function(e){
    $(this).find('tr').removeClass('hover');
});         

http://jsfiddle.net/tN865/1/

Geist answered 18/9, 2013 at 23:22 Comment(0)
A
0

This isn't as simple as it sounds since there is no "element with children with certain attributes" in CSS; selectors always only match the last element in the chain. But with a little jQuery magic, you can make it work. First the style:

.hover {
    background: #eaf0ff;
}

Then call this function whenever new tables are added:

var installInnerMostHover = function(){

    var updateHover = function() {
        $('.hover').removeClass('hover');
        $('.hover-hint').each(function(index,e) {
            if($(e).find('.hover-hint').length === 0) {
                $(e).addClass('hover');
            }
        });
    };

    $("tr").off("mouseenter mouseleave");
    $("tr").hover( function(){
        $(this).addClass('hover-hint');
        updateHover();
    },  function(){ 
        $(this).removeClass('hover-hint');
        updateHover();
    } );
};

This little gem will add hover-hint to all rows below the cursor. Afterwards, it will look for any element with the class hover-hint and then add the class hover to the all elements which don't have any children with hover-hint. There will be only one such element: The innermost row.

But when you try this, you will get an ugly flicker when you move the mouse in the space between rows of a nested table because this gap (the cell spacing) isn't part of the row as far as CSS is concerned, so the parent row will be triggered. To prevent this, you need to remove the cell spacing:

table { 
    border-spacing:0;
    border-collapse:collapse;
}
Albumen answered 18/9, 2013 at 15:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.