33

By Default, if I have anchors in my website, then the URL on the address bar is changed, when I click on a link (ie. www.mysite.com/#anchor)

Is it possible to change the URL in the address bar instantly when I scroll to an anchor? Or have a long document with multiple anchors and the url changes on address bar, when I hit a new anchor?

r1987
  • 507
  • 1
  • 7
  • 21

6 Answers6

41

Try using this jquery plugin: Scrollorama. It has tons of cool features and you can use window.location.hash to update your browsers hash.

Alternatively, you can add a "scroll" event to check when an anchor is reached.

Here is a working fiddle to illustrate the event: http://jsfiddle.net/gugahoi/2ZjWP/8/ Example:

$(function () {
    var currentHash = "#initial_hash"
    $(document).scroll(function () {
        $('.anchor_tags').each(function () {
            var top = window.pageYOffset;
            var distance = top - $(this).offset().top;
            var hash = $(this).attr('href');
            // 30 is an arbitrary padding choice, 
            // if you want a precise check then use distance===0
            if (distance < 30 && distance > -30 && currentHash != hash) {
                window.location.hash = (hash);
                currentHash = hash;
            }
        });
    });
});
Gustavo Hoirisch
  • 1,637
  • 12
  • 19
  • 3
    Amazing! Using a forked version of this for my project. However, we realised that using this caused a 'scroll jump' everytime the hash was changed. So, we used historyAPI as it fixed our problem. if(history.pushState) { history.pushState(null, null, "#"+hash); } else { window.location.hash = '#myhash'; } – Prashant Apr 10 '15 at 11:58
  • Link doesn't work anymore, project updated, new version here: https://scrollmagic.io/ – Binyomin Jan 27 '22 at 00:09
4

you can use HTML 5 pushstate to change the URL in the address bar

window.history.pushstate


  1. https://developer.mozilla.org/en-US/docs/DOM/Manipulating_the_browser_history
  2. How can I use window.history.pushState 'safely'
Community
  • 1
  • 1
user2031802
  • 744
  • 8
  • 7
2
  1. Bind a handler to jquery scroll event.
  2. Check if an anchor is currently visible on-screen with this jquery script.
  3. Use pushstate or set location (probably will cause jumps)
palindrom
  • 18,033
  • 1
  • 21
  • 37
2

You can bind to the jQuery scroll event (http://api.jquery.com/scroll/) and on each call of the callback called, check how far on the document the user has scrolled by checking this value: .scrollTop (http://api.jquery.com/scrollTop/) and set the anchor by manipulating te location.hash object (http://www.w3schools.com/jsref/prop_loc_hash.asp).

It would be something like this:

// Checks if the passed element is visible on the screen after scrolling
// source: http://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling
function isScrolledIntoView(elem) {
  var docViewTop = $(window).scrollTop();
  var docViewBottom = docViewTop + $(window).height();

  var elemTop = $(elem).offset().top;
  var elemBottom = elemTop + $(elem).height();

  return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}

$('#document').scroll(function(e) {
  var anchors = $('.anchor');
  for (var i = 0; i < anchors.length; ++i) {
    if (isScrolledIntoView(anchors[i])){
      var href = $(anchors[i]).attr('href');
      location.hash = href.slice(href.indexOf('#') + 1);
      break;
    }
  }
});

You could be more precise, if you sort the anchors after selecting them, so that the first visible anchor will be set always.

Georgi Atsev
  • 2,775
  • 2
  • 16
  • 18
  • Hi george, thanks for sharing this. Could you please post an example html markup that works with the code above? – Advanced Oct 10 '13 at 11:10
  • Sure. Here is an example: http://jsfiddle.net/7d5qU/10/. It should work, but I couldn't manage to prove it, since jsfiddle hides the inner url... – Georgi Atsev Oct 11 '13 at 11:53
2

Plain js version While researching how to update the URL based off positions of HTML section elements on the screen, I kept finding this thread so I hope this is a good place to post this.

This function loops over the HTML section elements.

        function updateFragId() {
        var len = sections.length;
          for (var i = 0; i < len; i++) {
            var id = sections[i].id;

Collects the Y scroll position relative to the viewport.

            var rect = sections[i].getBoundingClientRect().y;

convert the two arrays into an object

            var pageData = {id:id, rect:rect};

set a range for the code to trigger in between. Here it will trigger when the top of the section element is between -200px to 400px

            if (pageData.rect > -200 && pageData.rect < 400) {

only run once by making sure the pageData.id and location.hash dont already match. This stops it from flooding your browser with events.

        if (pageData.rect > -100 && pageData.rect < 100) {
            if (pageData.id !== location.hash.substr(1)) {
                fragmentId = pageData.id;
                setActiveLink(fragmentId);
              } else {
                return;
            }
          }
        }
      }

    window.addEventListener('scroll', updateFragId);

I use a debouncer on this block of code with another block to set the active link. But this is just how to track the # anchors.

Jef
  • 21
  • 2
  • 1
    Code-only answers are generally frowned upon on this site. Could you please edit your answer to include some comments or explanation of your code? Explanations should answer questions like: What does it do? How does it do it? Where does it go? How does it solve OP's problem? – mypetlion Sep 27 '19 at 20:48
  • Thanks for the feedback. Updated. – Jef Sep 27 '19 at 22:27
1

I think you need to do something like this. Not tried in action

var myElements = $("div.anchor"); // You target anchors
$(window).scroll(function(e) {
    var scrollTop = $(window).scrollTop();
    myElements.each(function(el,i) {
        if ($(this).offset().top > scrollTop && $(myElements[i+1]).offset().top < scrollTop) {
             location.hash = this.id;
        }
    });
});
Chamilyan
  • 9,347
  • 10
  • 38
  • 67
artnikpro
  • 5,487
  • 4
  • 38
  • 40