Post Archive

› October 16, 2005

A (slightly) better technique for "Back to Top" links.

  • Reported by Dave

Until recently, I never had the occasion to employ links that scroll the browser viewport back to the top of the page. When a client requested them however, I had to look up how to make them. I didn't like the options I found:

  • use an empty, named anchor somewhere near the top of the page (<a name="top"></a>),
    OR
  • use an id attribute on some element near the top of the page (id="top")

Combined with a link whose href is a fragment identifier (<a href="#top">Back to top</a>), these constructs produce the desired result. But the overhead—at least to my delicate sensibilities—is undesirable:

  • Extra markup cruft
  • Unsightly fragment on the end of the URL after the link is clicked. Unlike other fragments, #top is useless when the URL is bookmarked.

A bit of JavaScript in an unobtrusive fashion solves the problem:

<a href="#" onclick="window.scrollTo(0,0); return false">Back to Top</a>

Browsers with JavaScript turned on move to the top of the page without relying on extra tags or leaving a fragment in the address bar. Others fall back to the default link behavior. Notice the fragment is still there in the href, but it is now empty. I found that an empty fragment is interpreted by most browsers as link to the top of the page (even Lynx!). Opera is a notable exception.

Not being one to leave well enough alone, I just had to take this idea to its logical conclusion, namely, adding a "smooth scrolling" behavior. Here is the function:

function backToTop() {
    var x1 = x2 = x3 = 0;
    var y1 = y2 = y3 = 0;

    if (document.documentElement) {
        x1 = document.documentElement.scrollLeft || 0;
        y1 = document.documentElement.scrollTop || 0;
    }

    if (document.body) {
        x2 = document.body.scrollLeft || 0;
        y2 = document.body.scrollTop || 0;
    }

    x3 = window.scrollX || 0;
    y3 = window.scrollY || 0;

    var x = Math.max(x1, Math.max(x2, x3));
    var y = Math.max(y1, Math.max(y2, y3));

    window.scrollTo(Math.floor(x / 2), Math.floor(y / 2));

    if (x > 0 || y > 0) {
        window.setTimeout("backToTop()", 25);
    }
}

And here is the modified link:

<a href="#" onclick="backToTop(); return false">Back to Top</a>

You can see the effect here. Overkill? Perhaps. But I like simple uses of JavaScript such as this that make the web experience a bit more enjoyable.

Comments

1. October 16, 2005 12:39 AM

Quote this comment

Justin Posted…

To be truly unobtrusive you'd scan your page links on page load for ones that just linked to "#" and add the onclick handler then instead of declaring them inline.

2. October 16, 2005 01:36 AM

Quote this comment

David Lindquist Posted…

Except then you have this problem. I've never understood the aversion to (little bits of) inline JavaScript.

3. October 16, 2005 02:24 AM

Quote this comment

David Lindquist Posted…

OK, I give:

window.onload = function() {
    if (!document.getElementsByTagName)
        return;

    var a, i = 0;
    while ((a = document.getElementsByTagName("a")[i++])) {
        if ("#" == a.getAttribute("href")) {
            a.onclick = function() { backToTop(); return false; }
        }
    }
}

Not the best way to attach the onload, but you get the idea.

4. October 16, 2005 06:10 AM

Quote this comment

Jim Posted…

Um, so basically this breaks for anybody using Opera who has Javascript disabled? Because you don't like to see #top in your bookmarks? You have your priorities wrong. href="#" is commonly implemented by browsers as "back to top", but it's non-standard and liable to break. By all means, use smooth scrolling, but don't actively break things simply to make URLs look a little prettier.

5. October 16, 2005 06:30 AM

Quote this comment

fredmac Posted…

Yessss... . Internet experience look like smooth. Good hint...

6. October 16, 2005 06:35 AM

Quote this comment

fredmac Posted…

Anyway, It run with Opera 8.5 (Mac OS 10.3.9).

7. October 16, 2005 08:53 AM

Quote this comment

Jacob Stetser Posted…

Jim,

There are published standards and de facto standards. Sometimes, when a de facto standard is not actively detrimental to use, newcomers and/or "outlying" players should adopt the behavior in common use.

I realize that the authors of Opera enjoy being pedantic about standards, but conforming only to the published standards really isn't enough.

8. October 16, 2005 09:05 AM

Quote this comment

Lon Posted…

Scanning is bad; it is likely to fail if your content is dynamic.

In my opinion the easiest way to do this is to attach an onclick handler on the document. The handler has to check whether the click was inside an anchor having a hash for its href. Easy.

9. October 16, 2005 10:23 AM

Quote this comment

Jim Posted…

Jacob,
Whether Opera is correct or not in not implementing this behaviour is another matter entirely. The answer is irrelevant to the topic at hand; if Opera are wrong to not implement this, it doesn't mean that the code is any less fragile, or any fewer people will see the links break.

10. October 16, 2005 02:19 PM

Quote this comment

Erik Arvidsson Posted…

This breaks the history. If a user clicks on the link I expect to be able to hit back to get back to where I was reading the page.

Some simple ideas:

  • Catch the bubbling click event on the document (no need to add event handlers on each individual element
  • Check the element clicked and if it looks like a link to the top
  • Insert the top anchor using script if not already in there
  • Set the location and do the scrolling
  • 11. October 16, 2005 03:13 PM

    Quote this comment

    Posted…

    Pages not working for the 7% with JavaScript off is bad.

    12. October 16, 2005 03:26 PM

    Quote this comment

    Giorgio Martini Posted…

    Pages not working for the 7% with JavaScript off is bad.

    They just stop working in Opera users with JS disabled. And not the pages: just the 'jump to top' links. Please :)

    13. October 17, 2005 01:22 AM

    Quote this comment

    Perun Posted…

    Opera 8.50 on my PC is supporting href="#" as "back to top".

    14. October 17, 2005 06:09 AM

    Quote this comment

    frequency decoder Posted…

    Hi guys, I've written a similar script that attaches a 'tweened' scroll mechanism to any link whose href is a fragment identifier.

    With JavaScript turned off, the link still works albeit, without the animated scroll. With JavaScript turned on, the page should scroll smoothly to the position of the fragment identifier contained within the href.

    This version is a reworking of a script originally provided by squid-fingers:

    http://squidfingers.com/code/dhtml/?id=scrollwin

    I've just made it unobtrusive and changed the attach method.

    A demo can be found on http://www.frequency-decoder.com/ (just click any of the up-arrows to initiate the autoscroll). Those of you interested enough can view the js here (the object fdScroller is what your after):

    http://www.frequency-decoder.com/js/fd-generic.js

    I quite like Eriks idea of catching the event bubble though!

    15. October 17, 2005 05:57 PM

    Quote this comment

    kirkaracha Posted…

    I'd recommend saying "Top of page" instead of "Back to top," since they might have gotten to the bottom of the page via an external link, and they might be expecting "back" to mean something other than the top of the page.

    16. October 20, 2005 03:44 PM

    Quote this comment

    djQuickTip Posted…

    I'd recommend just saying "To Top" instead of "Top of Page". The word "page" is redundant. If not page, where else will you be going to the top of?

    17. October 20, 2005 07:05 PM

    Quote this comment

    Andy Posted…

    In usability testing we've conducted, people have associated the word 'top' with 'beginning' (as in either the homepage or first page of a section of a website). Our recommendation for the link text is 'Page top'.

    Also watch out for #top as a destination anchor name, prefer #pageTop to avoid potential conflicts with de facto standards.

    Using JavaScript seems a little heavy handed, instead use server-side includes for top-level and footer navigation (with appropriate destination and source anchors).

    For more on anchors, naming, etc. see anchors (Motive Glossary).

    18. October 20, 2005 09:21 PM

    Quote this comment

    Nate Posted…

    Andy, the usability testing you mention is interesting, "Page top" sounds quite sensible.

    I would however argue that server-side includes as a technology is neither related to the functionality described here, nor a replacement for the functionality offered by this Javascript. I'd also suggest that when a very short client-side script degrades properly it's the opposite of "heavy handed".

    19. October 21, 2005 09:04 AM

    Quote this comment

    Giorgio Martini Posted…

    @nate: The second part of andy's post is just outright spam. ;o)

    20. October 25, 2005 01:36 PM

    Quote this comment

    Giordano Posted…

    Before doing any of this, you might want to fix the float issues on this site. The "top" of this page, as viewed in IE (!) is pushed down below the end of the right sidebar. The top is not the top. hmm... :>

    21. October 25, 2005 01:41 PM

    Quote this comment

    Nate Posted…

    Giordano - I can't replicate the problem you're seeing.. what browser version and platform? Also, there are no float issues, because there are no floats! just a whole page margin.

    22. October 25, 2005 02:23 PM

    Quote this comment

    Giordano Posted…

    I was looking at the page on IE6 / WinXP Home SP2. I'm at dlp at silverthreaded dot com and can send you a screenshot if you email me. Looks fine in Firefox but may be prey to a "box-model" issue in IE, at least from where I'm sitting. Sorry to be off topic... Cheers.

    23. November 22, 2005 05:02 PM

    Quote this comment

    Zach Inglis Posted…

    As far as I am aware, that's not unobtrusive Javascript.

    Unobtrusive Javascript would involve the code <a href="<?=$post_url?>" class='backtotop'>Back to Top</a> and your javascript would have (onload)

    document.getElementByClass('backtotop').onclick="window.scrollTo(0,0); return false"

    24. November 22, 2005 07:03 PM

    Quote this comment

    David Lindquist Posted…

    Zach, the pedantic nitpickers already beat you to the punch, as can be seen in the first post of this thred. :)

    I was using the term "unobtrusive" as an alias for "graceful degradation".

    25. December 1, 2005 01:27 AM

    Quote this comment

    Shadoefax Posted…

    Firefox users can install a nifty "Back to Top" extension that pretty much eliminates the need for Back to Top links. https://addons.mozilla.org/extensions/moreinfo.php?id=658

    26. December 12, 2005 02:59 AM

    Quote this comment

    Joan Posted…

    Thank you so much for this tip on Back to the top, I've been going mad editing this site as some pages have no scroll bar and the scrolling is soooo sloooowww...arrggghh!! You have saved my sanity! And most likely anyone visiting my site will thank you too. Thanks again, Joan

    27. May 7, 2006 02:57 PM

    Quote this comment

    Mike Hutchinson Posted…

    Very nice solution. I was getting a lot of problems with browsers not actually taking me to the real "top", there were always just a couple of scrollable lines left for some reason and your solution cleared the problem.

    Opera threw out another problem. I had a javascript link to refresh the page but Opera then scrolls you back to the line you were on after the refresh, even though you clearly see the browser take you to the very top of the page first(!). Your solution came to the rescue with a small modification to include an optional page reload statement. I am therefore using your code to get me to the top of the page and only then issuing the page refresh via javascript. As I am already at the top of the page that is where Opera returns me. Tacky but it works. Thanks again for sharing your very neat bit of code.