0

EDIT (12/26/2012)

I found the following code which does exactly what I want, except now when a page's URL has a trailing slash (e.g. example.com/page/), the page doesn't scroll. Works fine if the page's URL ends with '.php' or '.html', etc. Any thoughts on how to get the following script to work with the trailing slash in a URL?

jQuery('a[href*=#]').bind('click', function(e) {
    // Get the target
    var target = jQuery(this).attr("href"); 

    // prevent the "normal" behaviour which would be a "hard" jump
    e.preventDefault(); 

    // perform animated scrolling by getting top-position of target-
    // element and set it as scroll target
    jQuery('html, body').stop().animate({
        scrollTop: jQuery(target).offset().top 
    }, 500, function() {
        location.hash = target;  //attach the hash (#jumptarget) to the pageurl
    });

    return false;
});

I've been using a script successfully for the last couple of years, but have recently run into some issues with it. Basically what the script does is scroll the page to a specific point. This happens with link anchors. For example, if one link is:

<a href="#anchor">anchor link</a>

The page will smoothly scroll to that anchor on the page:

<a name="anchor"></a>

Or:

<a id="anchor"></a>

The issue that occurs arises when some other JS is being used in the page which requires a link to be formatted as such:

<a href="#">other link</a>

When this "other link" is clicked, the page will smoothly scroll, BUT to the top or bottom of the page where there is NO anchor.

What should happen when this "other link" is clicked? The other JS action should occur (which it does), but the smooth page scrolling script should not occur.

Here's a working example from where I got this script:

http://www.dezinerfolio.com/wp-content/uploads/smoothscrolldemo/df_smooth_scroll.html

Here's the JS in full:

Scroller = {
    // control the speed of the scroller.
    // dont change it here directly, please use Scroller.speed=50;
    speed: 10,

    // returns the Y position of the div
    gy: function (d) {
        gy = d.offsetTop
        if (d.offsetParent) while (d = d.offsetParent) gy += d.offsetTop
        return gy
    },

    // returns the current scroll position
    scrollTop: function (){
        body = document.body
        d = document.documentElement

        if (body && body.scrollTop) return body.scrollTop
        if (d && d.scrollTop) return d.scrollTop
        if (window.pageYOffset) return window.pageYOffset

        return 0
    },

    // attach an event for an element
    // (element, type, function)
    add: function(event, body, d) {
        if (event.addEventListener) return event.addEventListener(body, d,false)
        if (event.attachEvent) return event.attachEvent('on'+body, d)
    },

    // kill an event of an element
    end: function(e){
        if (window.event) {
            window.event.cancelBubble = true
            window.event.returnValue = false

            return;
        }

        if (e.preventDefault && e.stopPropagation) {
          e.preventDefault()
          e.stopPropagation()
        }
    },

    // move the scroll bar to the particular div.
    scroll: function(d){
        i = window.innerHeight || document.documentElement.clientHeight;
        h = document.body.scrollHeight;
        a = Scroller.scrollTop()

        if (d>a)
            if(h-d>i)
                a += Math.ceil((d-a)/Scroller.speed)
            else
                a += Math.ceil((d-a-(h-d))/Scroller.speed)
        else
            a = a + (d-a)/Scroller.speed;

        window.scrollTo(0,a)

        if (a==d || Scroller.offsetTop==a)
            clearInterval(Scroller.interval)

        Scroller.offsetTop = a
    },

    // initializer that adds the renderer to the onload function of the window
    init: function(){
        Scroller.add(window,'load', Scroller.render)
    },

    // this method extracts all the anchors and validates then as # and attaches the events.
    render: function(){
        a = document.getElementsByTagName('a');

        Scroller.end(this);

        window.onscroll

        for (i=0;i<a.length;i++) {
            l = a[i];

            if (l.href && l.href.indexOf('#') != -1 && ((l.pathname==location.pathname) || ('/'+l.pathname==location.pathname)) ){
                Scroller.add(l,'click',Scroller.end)

                l.onclick = function(){
                    Scroller.end(this);

                    l = this.hash.substr(1);
                    a = document.getElementsByTagName('a');

                    for (i=0;i<a.length;i++) {
                       if (a[i].name == l){
                            clearInterval(Scroller.interval);

                            Scroller.interval = setInterval('Scroller.scroll('+Scroller.gy(a[i])+')',10);
                        }
                    }
                }
            }
        }
    }
}

// invoke the initializer of the scroller
Scroller.init();

I would think that there is a way to write the script so that if the href is equal to just the hash mark # without any text after the hash, that the scroller wouldn't be triggered.

Does anyone have any better ideas?

Thanks in advance!

Jared Farrish
  • 48,585
  • 17
  • 95
  • 104
Adam Baney
  • 57
  • 1
  • 2
  • 11
  • Welcome to SO, Adam. First of all, I would avoid that code you've found. It's got all kinds of issues and looks (to me) to have been written either at or by someone who is stuck in a time period when Javascript code was not very well written. You'll see I went through and tried to format it so it was easier to read, but jeez there's a mish-mash of absurdities and coding styles going on with it. Let me see if I can find a pure JS smooth scrolling script that's more up-to-date with "modern" ways of coding in Javascript. – Jared Farrish Dec 27 '12 at 05:58
  • (And on a side note, [this is really common in jQuery](https://github.com/kswedberg/jquery-smooth-scroll). If you're already using it or another similar library/framework. ) – Jared Farrish Dec 27 '12 at 06:10
  • Thanks, @Jared Farrish, for your help! I also got other opinions that the script I was using was old. :) – Adam Baney Jan 22 '13 at 01:44

2 Answers2

0

I can't help you with your jQuery function, but there are two simple solutions to your original script. The first is a small modification to tell the script to ignore the special case where an anchor's URL is only the hash tag.

In the render function, change the line:

if (l.href 
    && l.href.indexOf('#') != -1 
    && (l.pathname == location.pathname
        || '/' + l.pathname == location.pathname)
) {

To:

if (l.href
    && l.href != '#' // <<< Added this conditional >>>
    && l.href.indexOf('#') != -1
    && (l.pathname == location.pathname
        || '/' + l.pathname == location.pathname)
){

This will tell the script to ignore the special case, but won't prevent the browser from reacting normally to the link, so the browser may still jump to the top of the page. The special case you've mentioned is almost always used in javascript constructions to provide an anchor tag with an href attribute, because some older browsers would ignore the tag without one. The '#' was used as the URL to prevent the link from leaving the page.

Instead of the '#', you could use an empty javascript call in your link, like so:

<a href="javascript:;">other link</a>

This will avoid your issues with the scrollers completely.

Jared Farrish
  • 48,585
  • 17
  • 95
  • 104
Gareth Cornish
  • 4,357
  • 1
  • 19
  • 22
  • I reformatted the conditional block so it was more obvious what was modified/added/removed. Note, I'm neither criticizing nor promoting the use of `href="javascript:;"`, but there's, uhh, [an interesting question with many *opinionated* answers](http://stackoverflow.com/questions/134845/href-attribute-for-javascript-links-or-javascriptvoid0) on the subject. Personally, I'd probably just remove the `href` attribute when I attached the event listeners, for reasons of graceful degradation. But that's just me. – Jared Farrish Dec 27 '12 at 07:00
  • Sorry I didn't respond sooner. For some reason, I'm not getting email notifications from SO. I did find an answer to this issue. I'll post the code here soon... – Adam Baney Jan 22 '13 at 01:45
0

Thanks again, Jarred, for your help! I did come across a script that does just what I want. Here's the better script I found:

jQuery('a[href*=#]').bind('click', function(e) {
    e.preventDefault(); //prevent the "normal" behaviour which would be a "hard" jump
    var target = jQuery(this).attr("href"); //Get the target
    // perform animated scrolling by getting top-position of target-element and set it as scroll target
    jQuery('html, body').stop().animate({ scrollTop: jQuery(target).offset().top }, 1000, function() {
        location.hash = target;  //attach the hash (#jumptarget) to the pageurl
    });
    return false;
});
andr
  • 15,970
  • 10
  • 45
  • 59
Adam Baney
  • 57
  • 1
  • 2
  • 11