3

I display a bunch of posts from users on a page. I have the main parent div with the class name 'posts' and each post is outputted in a div with class name 'row' inside of it. So there's a whole lot of div.row's inside the div.posts. Each look like this.

<div class="row clearfix">
    <div class="left-column">
        <img src="..." title="" />
    </div>
    <div class="main-column">
        <div class="row-text">Post Text...</div>
        <div class="row-date">Posted Date...</div>
    </div>
    <div class="actions-column">
        <a href="#">Link</a>
        <a href="#">Link 2</a>
        <a href="#">Link 3 etc.</a>
    </div>
</div>

Via CSS the actions-column is set to display:none by default. When a user mouseover's a post (div.row) I want to show the actions-column. My initial way of doing it was by setting a mouseover even for each row and that was taking it's toll on the browser and slowing things down. I did some research and stumbled upon event delegation and decided to give it a try. So far I am able to identify which row is being targeted, however, I cannot figure out how to target it's child-div with the class 'actions-column'.

Code so far...

$(window).load(function(){

    $('.posts').mouseover(function(e){
        var $row, $tgt = $(e.target);

        if ($tgt.hasClass("row")) {
            $row = $tgt;
        } else { 
            if ($tgt.parent().parent().hasClass('row'))
                $row = $tgt.parent().parent();

            else if ($tgt.parent().hasClass('row'))
                $row = tgt.parent();    

            else
                return false;       
        }

        //code here to select div.actions-column and show it

    });

    $('.posts').mouseover(function(e){
        var $row, $tgt = $(e.target);

        if ($tgt.hasClass("row")) {
            $row = $tgt;
        } else { 
            if ($tgt.parent().parent().hasClass('row'))
                $row = $tgt.parent().parent();

            else if ($tgt.parent().hasClass('row'))
                $row = tgt.parent();    

            else
                return false;       
        }

        //code here to select div.actions-column and hide it

    });

});
Paolo Bergantino
  • 480,997
  • 81
  • 517
  • 436
Seth
  • 582
  • 2
  • 5
  • 15

6 Answers6

6

You could use live:

$('div.row').live('mouseover', function() {
    $(this).find('div.actions-column').show();
}).live('mouseout', function() {
    $(this).find('div.actions-column').hide();
});

As the documentation notes:

When you bind a "live" event it will bind to all current and future elements on the page (using event delegation).

A couple more notes:

  • I see you are using the $(window).load() event. You probably want $(document).ready()
  • You should take advantange of jQuery's chaining capabilities instead of querying for $('.posts') twice, you can just append the second call at the end (like I did in my example above) - this is much more efficient and the preferred way of doing multiple things to 1 selector in jQuery.
  • The code you are trying to write to find the next div.row up the HTML tree has already been implemented by jQuery's closest() method.

You could do something like this with it:

$('div.posts').hover(function(e) {
    var row = $(e.target).closest('div.row');
    $row.find('div.actions-column').show();
}, function(e) {
    var row = $(e.target).closest('div.row');
    $row.find('div.actions-column').hide();
});

But this is not necessary because of the live functionality I showed above.

  • Think about the efficiency of your queries. If you only have a single <div> with a class of posts, consider giving it an id attribute. This is by far the most efficient method of selecting elements in a document, and it makes sense to give it to elements that only occur once.
  • Whenever you are querying for an element by class, it is good practice to include the tag name before the class (unless you expect it to be in many different tags, of course) - so instead of looking for .row, make a habit of looking for div.row instead. It's just faster.
Paolo Bergantino
  • 480,997
  • 81
  • 517
  • 436
  • 1
    It's worth noting that the JS best practices on class selectors differs from the CSS best practices, since JS does not have a native class selector. CSS, however, does and there the element tag should be omitted. Reference: http://code.google.com/speed/page-speed/docs/rendering.html – PatrikAkerstrand Jun 15 '09 at 20:52
  • $.live() is a performant waste. $.delegate() should be used instead. – arxpoetica Jul 17 '11 at 02:37
  • Agreed; `live()` is a bad way to do this. `delegate()` is much better (if you select the closest non-destroyed ancestor), and more recently (long after this question was asked and ancestor accepted), `.on()` with delegation syntax has become the preferred method. – Greg Pettit Jan 22 '12 at 18:07
2

I realize this has already been answered, but since jQuery 1.5 adds an explicit "delegate" method, that might be a better solution now. Since this thread will come up in searches, I post this answer for other people searching for it. (I also refactor for further conciseness by using "hover" and "toggle()" instead of mouseenter/mouseleave + show()/hide().)

http://api.jquery.com/delegate/

$('div.row').live('mouseover', function() {
    $(this).find('div.actions-column').show();
}).live('mouseout', function() {
    $(this).find('div.actions-column').hide();
});

with jQuery 1.5 or higher could be written as:

$(".posts").delegate("div.row", "hover", function(){
    $(this).find("div.actions-column").toggle();
});

See it in action:

http://jsfiddle.net/SM6Jv/

enter image description here

Dennis
  • 593
  • 6
  • 11
1

Try using jQuery's live() method. It uses event delegation internally to perform its actions so you won't even have to think about setting up event delegation properly.

$('.row').live('mouseover', function() {
    $(this).find('.actions-column').show();
}).live('mouseout', function() {
    $(this).find('.actions-column').hide();
});
Dan Herbert
  • 99,428
  • 48
  • 189
  • 219
0

Unless you really wanted to do this with jQuery then of course something can be managed with a line of CSS:

div.row:hover div.actions-column { display:block; }
0

?:

$('div.actions-column', $row).show(); // .hide()

UPDATE:

It is better to use jQuery closest([expr]):

var $tgt = $(e.target);
var $row = $tgt.closest('.row');

instead of:

var $row, $tgt = $(e.target);

if ($tgt.hasClass("row")) {
    $row = $tgt;
} else { 
    if ($tgt.parent().parent().hasClass('row'))
        $row = $tgt.parent().parent();
    else if ($tgt.parent().hasClass('row'))
        $row = tgt.parent();    
    else
        return false;           
}
eu-ge-ne
  • 28,023
  • 6
  • 71
  • 62
0

Try:

$row.find (".actions-column").hide ();

And

$row.find (".actions-column").show();
albertein
  • 26,396
  • 5
  • 54
  • 57