Pergunta

I'm creating a grid that can have n rows and is separated into two views: a leftChild and a rightChild. The leftChild has the same amount of rows as the rightChild but the leftChild stays in its place. The only difference with the rightChild is that it can be scrolled horizontally. When I hover over a leftChild or rightChild element, I want to add some sort of hovering effect... that's easy, but what I want to do is add a hovering effect across the entire row. So if I hover over the 3rd row in the leftChild, I want to highlight the 3rd row in the rightChild.

Now, ideally, I'd love a complete CSS solution, similar to this, but that's not possible because my rows don't follow directly after each other. I was trying to think of another way of solving this, but it doesn't seem possible with straight CSS.

Enter JavaScript. I'm thinking the next step is to combine JavaScript with CSS. I can add a hover effect to a row, and then use JavaScript to add a hover class to the corresponding row in the other child. This is quite simple with jQuery, but I'm looking for a native JavaScript approach.

The main approach I'm thinking of is adding a mouseenter and mouseleave on each row class element. I don't really like this approach because then I'm setting up 2 event listeners on each row element... which seems a bit inefficient. Anyway, when you enter, you grab the row number of what you hovered, then add the hover class to all of these row number elements. When you leave, you just find all elements with hover, and remove accordingly. The corresponding code is as follows:

HTML

<body onload="loaded()">
    <div id="parent">
        <div id="leftChild">
            <div>left child</div>
            <div class="row row1">some content</div>
            <div class="row row2">other content</div>
            <div class="row row3">more content</div>
        </div>
        <div id="rightChild">
            <div>right child</div>
            <div class="row row1">
                <span class="col1">column 1 content</span>
                <span class="col2">column 2 content</span>
                <span class="col3">column 3 content</span>
                <span class="col4">column 4 content</span>
                <span class="col5">column 5 content some really long content to trigger scrolling just for the purpose of this example</span>
            </div>
            <div class="row row2">
                <span class="col1">column 1 content</span>
                <span class="col2">column 2 content</span>
                <span class="col3">column 3 content</span>
                <span class="col4">column 4 content</span>
                <span class="col5">column 5 content some really long content to trigger scrolling just for the purpose of this example</span>
            </div>
            <div class="row row3">
                <span class="col1">column 1 content</span>
                <span class="col2">column 2 content</span>
                <span class="col3">column 3 content</span>
                <span class="col4">column 4 content</span>
                <span class="col5">column 5 content some really long content to trigger scrolling just for the purpose of this example</span>
            </div>
        </div>
    </div>
</body>

JS

function loaded() {
    /*var parent = document.getElementById('parent');
    parent.onmouseenter = function(event) {
        console.log(event.target);
    };
    parent.onmouseleave = function(event) {
        console.log(event.target);
    };*/

    var rows = document.getElementsByClassName('row');
    for (var i = 0; i < rows.length; i++) {
        rows[i].onmouseenter = function(event) {
            var splits = event.target.className.split(" ");
            var elems = document.getElementsByClassName(splits[splits.length - 1]);
            for (var j = 0; j < elems.length; j++) {
                elems[j].className += " hover";
            }
        };

        rows[i].onmouseleave = function(event) {
            var hovers = document.getElementsByClassName('hover');
            var len = hovers.length;
            for (var j = 0; j < len; j++) {
                hovers[0].className = hovers[0].className.replace(/\shover(\s|$)/, '');
            }
        };
    }
}

CSS

.row:hover, .hover {
    background-color: lightblue;
}

.row {
    height: 50px;
    padding: 5px;
    white-space: nowrap;
}

.row > span {
    display: inline-block;
    border: 5px solid black;
}

#leftChild, #rightChild {
    float: left;
}

#rightChild {
    width: 300px;
    overflow: auto;
}

#rightChild .row {
    display: inline-block;
}

jsFiddle: Here

So I'd like to know a few things.

  1. Is this possible with just straight CSS?
  2. If not, how can I make my approach more efficient?
  3. Is it more efficient to have one event handler or multiple event handlers?

I know I'm asking a lot here, but I hate asking multiple questions, especially if I'd have to repeat myself. I'd appreciate any help. Thanks!

Foi útil?

Solução 2

Based on this jsPerf, the straight JavaScript approach that I have there is the fastest, the hybrid approach comes in a close second--and I mean really close to the native JS approach--and the (almost completely) jQuery approach comes in dead last--and it's really slow compared to the other two.

All of these can be seen at this jsFiddle.

JS

// Native JS approach... fastest (according to my jsPerf http://jsperf.com/removeclass-vs-native-js-remove-class/2)
function loaded() { 
    var rows = document.getElementsByClassName('row');
    for (var i = 0; i < rows.length; i++) {
        rows[i].onmouseenter = function(event) {
            var row = this.className.match(/row-[\d]+/);
            var elems = document.getElementsByClassName(row[0]);
            for (var j = 0; j < elems.length; j++) {
                elems[j].className += " hover";
            }
        };

        rows[i].onmouseleave = function(event) {
            var hovers = document.getElementsByClassName('hover');
            var len = hovers.length;
            for (var j = 0; j < len; j++) {
                hovers[0].className = hovers[0].className.replace(/\shover(\s|$)/, '');
            }
        };
    }
}

// jQuery approach (slowest)
/*$(document).ready(function() {
    $('.row').on('mouseenter', function(event) {
        var row = this.className.match(/row-[\d]+/);
        $('.' + row).addClass('hover');
    });

    $('.row').on('mouseleave', function(event) {
        $('.hover').removeClass('hover');
    });
});*/

// Hybrid approach (basically as fast as native JS approach)
/*$(document).ready(function() {
    var rows = document.getElementsByClassName('row');
  for (var i = 0; i < rows.length; i++) {
    rows[i].onmouseenter = function(event) {
      var row = this.className.match(/row-[\d]+/);
      $('.' + row[0]).addClass('hover');
    };

        rows[i].onmouseleave = function(event) {
            $('.hover').removeClass('hover');
        };
    }
});*/

Outras dicas

  1. via css with this structure: no, not before css level 4 is usable.
  2. use this and check on className to append to it for while a new rule
  3. moouse hovering & mouse leaving ?

i get this: via jquery, but for the test I removed the class .row and kept the numbered one

$("[class*='row']").hover(
  function () {
      $('head').append('<style class="'+this.className+'" rel="stylesheet" > .'+this.className+' {background-color:lightblue;} </style>'); 
$(this).mouseleave(function() {  $('style.'+this.className).remove();});
});

http://codepen.io/gcyrillus/pen/bhglr

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top