gen_Eric
gen_Eric

Reputation: 227240

Scroll to element on page, and then run callback

I am trying to use jQuery's .animate to scroll to an element on a page, and then execute a callback.

After searching around, I found this function:

function scrollToElement(selector, callback){
    var animation = {scrollTop: $(selector).offset().top};
    $('html,body').animate(animation, 'slow', 'swing', callback);
}

This correctly scrolls to the element defined by 'selector', but callback is called twice (because $('html,body') contains 2 elements).

I tried changing

$('html,body').animate

to:

$(document).animate

and:

$(window).animate

but, neither of those do anything.

I also tried changing the function to this:

$('html').animate(animation, 'slow', 'swing', function(){
    $('body').animate(animation, 'slow', 'swing', callback);
});

but, this made the browser run the 1st animation and then the 2nd, so I had wait for both to run before the callback was ran (I dont't want that).

I figured out that $('body').scrollTop() only works in Chrome, and $('html').scrollTop() only works in Firefox.

So, is there a way (without needing to download a jQuery plugin) for me to scroll to a specific element in both Chrome and Firefox (I don't care about IE), and have a callback executed (once)?

EDIT:

I made a crude fix by making a boolean to check if the callback ran already, and if it was, don't run it again.

function scrollToElement(selector, callback){
    var animation = {scrollTop: $(selector).offset().top};
    var callback_running = false;
    $('html,body').animate(animation, 'slow', 'swing', function(){
        if(typeof callback == 'function' && !callback_running){
            callback_running = true;
            callback();
        }
    });
}

Upvotes: 3

Views: 5236

Answers (5)

malihu
malihu

Reputation: 786

You should avoid animating both html and body elements. Your page animation will work correctly on every modern or old browser and the callback will run once (as it should) by the addition of a simple condition in your function.

function scrollToElement(selector, callback){
    var scrollElem='html';
    //animate body for webkit browsers that don't support html animation
    if($.browser.webkit){ 
        scrollElem='body';
    }
    var animation = {scrollTop: $(selector).offset().top};
    $(scrollElem).animate(animation, 'slow', 'swing', callback);
}

Only webkit doesn't support "html" animation, so you change the "scrollElem" variable accordingly. In addition, scrolling a single element (html or body) works much better on older browsers (e.g. previous versions of Opera).

Upvotes: 0

Zathrus Writer
Zathrus Writer

Reputation: 4331

how about using a DIV that stretches over the full document body and do the animation on that DIV instead? you can't be sure how many more browser issues you can find otherwise (i.e. how many more browsers would not animate HTML nor BODY for instance)

Upvotes: 0

Alexey Lebedev
Alexey Lebedev

Reputation: 12197

I think this should work too

function scrollToElement(selector, callback){
    var animation = {scrollTop: $(selector).offset().top};
    $('html,body').animate(animation, 'slow', 'swing', function() {
        if (typeof callback == 'function') {
            callback();
        }
        callback = null;
    });
}

Upvotes: 3

lonesomeday
lonesomeday

Reputation: 237855

If you are using jQuery 1.5 (or can upgrade to it), you can use the new $.Deferred syntax.

$.fn.scrollToElement = function(selector, callback) {
    var def = new $.Deferred(),
        el = this;

    $('html, body').animate({scrollTop: $(selector).offset().top}, 'slow', 'swing', def.resolve);

    if (callback) {
        def.promise().done(function(){
            callback.call(el);
        });
    }
};

$('html, body').scrollToElement('#foo', function() {
    alert('done scrolling');
});

Because a deferred object can only be resolved once, you can't have more than one call to the callback.

Upvotes: 1

epascarello
epascarello

Reputation: 207501

Have you tried using

$(document.body).animate( ... )

Upvotes: 0

Related Questions