Search code examples
jqueryscrollsmoothingtargets

jQuery Smooth Scroll scrolls to wrong target when live (works on test page)


I've been searching for a solution without success for quite a while now, maybe someone here might be able to help me! :)

I'm new to jQuery so it's possible that it's something simple I've forgotten, but the scroll works fine when i test the page in my browser, it just doesn't work when I upload the page to my server. As soon as it's live, the links stop working or sometimes scroll an inch or so but not to the right place.

I can't upload all code here, so here's the live page: http://www.carolinaekstrom.se/

The link "om mig" is supposed to stop at the picture of me, "kontakt" at "hör gärna av er!", and "portfolio", well at the portfolio :)

Here's my js:

$(document).ready(function() {
 function filterPath(string) {
return string
.replace(/^\//,'')
.replace(/(index|default).[a-zA-Z]{3,4}$/,'')
.replace(/\/$/,'');
}
var locationPath = filterPath(location.pathname);
var scrollElem = scrollableElement('html', 'body');

$('a[href*=#]').each(function() {
var thisPath = filterPath(this.pathname) || locationPath;
if (  locationPath == thisPath
&& (location.hostname == this.hostname || !this.hostname)
&& this.hash.replace(/#/,'') ) {
  var $target = $(this.hash), target = this.hash;
  if (target) {
    var targetOffset = $target.offset().top -60;
    $(this).click(function(event) {
      event.preventDefault();
      $(scrollElem).animate({scrollTop: targetOffset}, 700, function() {

      });
    });
  }
}
});

// use the first element that is "scrollable"
function scrollableElement(els) {
for (var i = 0, argLength = arguments.length; i <argLength; i++) {
  var el = arguments[i],
      $scrollElement = $(el);
  if ($scrollElement.scrollTop()> 0) {
    return el;
  } else {
    $scrollElement.scrollTop(1);
    var isScrollable = $scrollElement.scrollTop()> 0;
    $scrollElement.scrollTop(0);
    if (isScrollable) {
      return el;
    }
  }
}
return [];
}

});

Here's the menu:

<div class="menu">
<div id="nav">
 <div id="content2">
     <a href="#screen1"><img style="float:left; max-width:33.416%;"    src="menu_01.png" ></a>
     <img style="float:left; max-width:34.416%;" src="menu_02.png" >
     <a href="#screen2"><img style="float:left; max-width:9.833%;" src="menu_03.png" ></a>
     <a href="#screen3"><img style="float:left; max-width:9.6%;" src="menu_05.jpg" ></a>
     <a href="#screen4"><img style="float:left; max-width:11.416%;" src="menu_04.png" ></a>
 </div>

Here's my "pages":

<div id="pagewrap1">
<a id="screen1"></a>
<a id="pad1"></a>
<a id="mobile1"></a>
<div id="content">
<div id="box1"><img src="carocontent.png" /></div>
</div>
</div>

<div id="pagewrap2">
<a id="screen2"></a>
<a id="pad2"></a>
<a id="mobile2"></a>
<div id="content">
<div id="box2"><img src="jag.jpg" />


 </div>
 </div>
 </div>




<div id="pagewrap3">
<a id="screen3"></a>
<a id="pad3"></a>
<a id="mobile3"></a>
<div id="content">
<div id="box3">

<br/><br/><br/><br/><br/>
<div class="text"><div align="center"><b>HÖR GÄRNA AV ER!</b></div></div>
<div class="between"><img src="between.png" /></div>
<div id="contactphone" class="between"><img src="between.png" /></div>


 <div id="contactweb">
  <a href="tel:+46768727892"><img style="max-width:8%;" src="contact_01.png" /></a>
  <a href="mailto:carolina_ekstrom@hotmail.com"><img style="max-width:8%;" src="contact_02.png" /></a>
  <a href="https://www.facebook.com/carolina.ekstrom.395" target="_blank"><img style="max-width:8%;" src="contact_03.png" /></a>
   </div>


   </div>

   </div>
   </div>
    </div>

I'll be very grateful for your help! :)


Solution

  • The reason it isn't working properly is that the images are not yet fully loaded when document ready event fires. As a note document ready is when the DOM/html is ready for JS to execute however it does not guarantee the resources are loaded.

    As a result the targetOffset that you initially had calculated is invalid after all the images are loaded.

    I would recommend following a delegation pattern like this (which would require some refactoring on your part): http://jsfiddle.net/peterdotjs/tao135x9/19/

    $('body').on('click', 'nav a', function(evt){
        evt.preventDefault();
        var $this = $(this);
        $('html, body').animate({
            scrollTop: $($this.attr('href')).offset().top - navHeight 
        },700);
    });
    

    In my example, the targetOffset is calculated on the fly of where to scroll.

    As a quick fix if you change your code to run on the window.load event it should work as well.

    The window.load event fires when all resources are loaded - so you can assume that the calculations will be accurate.

    I'm not sure why you are using "screen1", "screen2", etc. rather than using an id that references the place you want to scroll to typically you would match the href id attribute or similar in the nav to the id of the element scrolled to.

    Hope this helps.