dieterkp
dieterkp

Reputation: 13

How do I make an AJAX call only once on JQuery.hover()?

I would like to display the results of an AJAX call in a div when the user hovers over it (i.e. replace the div contents with the results of the AJAX call). The basic functionality is to replace a dot on the page with a circle that contains content from the server when the user hovers over the dot, and show the dot again when they stop hovering. There may be many dots on the page, each with different circle content on hover.

I have it working, but I notice that the AJAX call is getting repeatedly called while the user is hovering over the given div. How can I prevent repeated calls?

I have tried various usages of .one and .unbind('mouseenter', 'mouseleave') but haven't gotten it to work right yet.

Here's what I have right now (the hover behavior works right for the user but causes repeated calls to the backend).

<div class="box1" s='<%=value1 %>' t='<%=value2 %>'>
   <div id="circle" style="display: none;">
   </div>
   <div class="dot" id="dot">
      <img src="orangeDot.png" />
   </div>
</div>

and the script:

<script type='text/javascript'>
    $(document).ready(function () {
        $(".box1").hover(function () {
            var source = $(this).attr('s');
            var target = $(this).attr('t');
            $('#dot').attr("style", "display: none;");
            $('#circle').hide().load('/GetCircle?s=' + source + '&t=' + target).show();
        }, function () {
            $('#circle' + divid).attr("style", "display: none;");
            $('#dot' + divid).attr("style", "display: inline-block;");
        });
    });
</script>

Upvotes: 1

Views: 8405

Answers (5)

roselan
roselan

Reputation: 3775

adding a loaded attribute on the fly is the best I found.

<script type='text/javascript'>
    $(document).ready(function () {
        $(".box1").hover(function () {
            var source = $(this).attr('s');
            var target = $(this).attr('t');
            var $circle = $('#circle' + divid);
            $('#dot').attr("style", "display: none;");
            if ( $circle.data('loaded') == 'yes' ) {
                $circle.show();
            }
            else {
                $.get('/GetCircle', {s: source, t: target}, function (data) {
                    $circle.data('loaded', 'yes');
                    $circle.html(data); // note: "$circle.get(0).innerHTML = data" is faster
                    $circle.show(); // of course, the 3 methods can be chained 
                }};
            }, function () {
                $('#circle' + divid).hide(); // was ".attr("style", "display: none;");"
                $('#dot' + divid).attr("style", "display: inline-block;");
            }):
        });
    });
</script>

note: add and removing classes (previously defined in your CSS) is faster. example: $(obj).removeClass('invisible').addClass('inlineBlock')

Upvotes: 1

JAAulde
JAAulde

Reputation: 19560

Based on your response to my question in the comment to your OP, I would adjust your entire sample as posted below (untested). The general idea is to do nothing if the ajax is awaiting response (try to avoid race condition), to get content and use it if it hasn't been flagged as having already been done, or just adjust the showing/hiding. Some adjustment for your preferences and integration may be necessary.

<div class="box" data-s="<%=value1 %>" data-t="<%=value2 %>">
    <div class="circle" style="display: none;">
    </div>
    <div class="dot">
        <img src="orangeDot.png" alt="orange dot" />
    </div>
</div>

<script type='text/javascript'>
    $( function()
    {
        var IN_FLIGHT = 'requestinflight',
            RETRIEVED = 'contentretrieved';

        $( '.box' ).hover(
            function()
            {
                var $box, $circle, $dot;

                $box = $( this );

                if( ! $box.data( IN_FLIGHT ) )
                {
                    $dot = $box.find( '.dot' );
                    $circle = $box.find( '.circle' );

                    if( ! $box.data( RETRIEVED ) )
                    {
                        $box.data( IN_FLIGHT, true );
                        $.ajax( {
                            url: '/GetCircle',
                            data: {
                                s: $box.data( 's' ),
                                t: $box.data( 't' )
                            }
                            dataType: 'html',
                            success: function( html )
                            {
                                $box.data( RETRIEVED, true );
                                $circle.html( html ).show();
                                $dot.hide();
                            },
                            complete: function()
                            {
                                $box.data( IN_FLIGHT, false );
                            }
                        } );
                    }
                    else
                    {
                        $circle.show();
                        $dot.hide();
                    }
                }
            },
            function()
            {
                var $box = $( this );
                if( ! $box.data( IN_FLIGHT ) )
                {
                    $box.find( '.circle' ).hide();
                    $box.find( '.dot' ).show();
                }
            }
        );
    } );
</script>

Upvotes: 1

uadnal
uadnal

Reputation: 11425

You should consider using mouseover instead of hover. You could do some actions when it's moused over, such as unbinding it from the action. And you could bind it again on a mouseout.

The idea is to unbind the mouseover action when the mouseover function is called. This removes the mouseover function from the element that was hovered. I didn't test this...I apologize if it isn't exactly correct.

$(ele).mouseover(function() {
   $(this).unbind('mouseover');
});

Upvotes: 1

Matt Stein
Matt Stein

Reputation: 3053

Why not just have a boolean flag like var ajaxed = false and switch it to true after the request is completed once? Then just wrap your hover action in a check for ajaxed.

Upvotes: 4

wesbos
wesbos

Reputation: 26317

use jQuery one('hover', function() {...});

http://api.jquery.com/one/

 <script type='text/javascript'>
        $(document).ready(function () {
            $(".box1").one('hover',function () {
                var source = $(this).attr('s');
                var target = $(this).attr('t');
                $('#dot').attr("style", "display: none;");
                $('#circle').hide().load('/GetCircle?s=' + source + '&t=' + target).show();
            }, function () {
                $('#circle' + divid).attr("style", "display: none;");
                $('#dot' + divid).attr("style", "display: inline-block;");
            });
        });
    </script>

Upvotes: 0

Related Questions