Search code examples
jqueryevent-delegation

jQuery Event Delegation + Child Targeting Help


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

    });

});

Solution

  • 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.