I'm using a smooth scroll script for a site I'm currently working on, and I've got a really annoying problem what I've experienced before with the same script. It works nice and smoothly but when I click on one of the navigation points what should lead me to the div(or a) I'm trying to target, it shows me the targeting area for like 0.1 seconds, and then it starts to scroll. It doesn't happen everytime, but often enough to be annoying. How could I prevent this? Here is the script I'm talking about:
$(window).load(function(){
$(".contactLink").click(function(){
if ($("#contactForm").is(":hidden")){
$("#contactForm").slideDown("slow");
}
else{
$("#contactForm").slideUp("slow");
}
});
});
function closeForm(){
$("#messageSent").show("slow");
setTimeout('$("#messageSent").hide();$("#contactForm").slideUp("slow")', 2000);
}
$(window).load(function() {
function filterPath(string) {
return string
.replace(/^\//,'')
.replace(/(index|default).[a-zA-Z]{3,4}$/,'')
.replace(/\/$/,'');
}
$('a[href*=#]').each(function() {
if ( filterPath(location.pathname) == filterPath(this.pathname)
&& location.hostname == this.hostname
&& this.hash.replace(/#/,'') ) {
var $targetId = $(this.hash), $targetAnchor = $('[name=' + this.hash.slice(1) +']');
var $target = $targetId.length ? $targetId : $targetAnchor.length ? $targetAnchor : true;
if ($target) {
var targetOffset = $target.offset().top - 110;
$(this).click(function() {
$('html, body').animate({scrollTop: targetOffset}, 1400);
var d = document.createElement("div");
d.style.height = "101%";
d.style.overflow = "hidden";
document.body.appendChild(d);
window.scrollTo(0,scrollToM);
setTimeout(function() {
d.parentNode.removeChild(d);
}, 10);
return false;
});
}
}
});
});
setTimeout(function() {
d.parentNode.removeChild(d);
}, 10);
return false;
});
move the return false out of the setTimeOut
Found the solution:
$(this).click(function(e) {
e.preventDefault();
Now it rolls fine.
Related
I'm trying to make a module called ScrollToAnchor that has a function called goToTarget that I will be able to call like ScrollToAnchor.goToTarget(target);
However it says that
ScrollToAnchor.goToTarget is not a function
I think ScrollToAnchor is of type jQuery how I have it because of the $. Here is the code:
var ScrollToAnchor = $(function() {
var headerHeight = 70;
$('a[href*="#"]:not([href="#"])').click(function() {
if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) {
if (goToTarget(this.hash))
return false;
}
});
/*$('input[data-target]').click(function() {
if (gotoTarget($(this).data("target")))
return false;
})*/
var goToTarget = function(targetName) {
var target = $(targetName);
target = target.length ? target : $('[name="' + targetName.slice(1) + '"]');
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top - headerHeight
}, 1000);
return true;
}
return false;
}
return {
goToTarget: goToTarget
}
});
What am I doing wrong? If i remove the $ from var ScrollToAnchor = $(function () { then the jQuery inside ScrollToAnchor doesn't work.
But if I leave the $ there then it thinks ScrollToAnchor is type jQuery and ScrollToAnchor.goToTarget is not a function.
The $(function() {...}) is a short hand of $( document ).ready( handler ).
So the result of $(function() {...}) is a jQuery result set containing the document as element.
You are looking for event delegation:
$(document).on('click', 'a[href*="#"]:not([href="#"])', function() {
if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) {
if (goToTarget(this.hash))
return false;
}
});
This will ensure that the click event will be use for all a element no matter when they have been added to the DOM and allows your to make your goToTarget available in the global scope in an easy way. Your final code will then look this way:
var ScrollToAnchor = (function() {
var headerHeight = 70;
// event handler with delegation
$(document).on('click', 'a[href*="#"]:not([href="#"])', function() {
if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) {
if (goToTarget(this.hash))
return false;
}
});
function goToTarget(targetName) {
var target = $(targetName);
target = target.length ? target : $('[name="' + targetName.slice(1) + '"]');
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top - headerHeight
}, 1000);
return true;
}
return false;
}
return {
goToTarget: goToTarget
}
}());
Using event delegation there is no need to wrap you whole code into a $(function() {...}) and your ScrollToAnchor is public available.
Turn ScrollToAnchor into a normal function. This function will be in the global scope:
window.ScrollToAnchorFactory = function () {
var headerHeight = 70;
$('a[href*="#"]:not([href="#"])').click(function () {
if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) {
if (goToTarget(this.hash))
return false;
}
});
/*$('input[data-target]').click(function() {
if (gotoTarget($(this).data("target")))
return false;
})*/
var goToTarget = function (targetName) {
var target = $(targetName);
target = target.length ? target : $('[name="' + targetName.slice(1) + '"]');
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top - headerHeight
}, 1000);
return true;
}
return false;
}
return {
goToTarget: goToTarget
}
};
You could also use a module.export instead of putting this in the global scope, if you'd like to create a commonJS module out of this code. That would allow you to require() it into other files (using Browserify to compile).
If you do decide to just keep ScrollToAnchorFactory in the global scope, wherever you need to use scrollToAnchor.goToTarget (in the same file or a different one)...
$(function() {
var scrollToAnchor = window.ScrollToAnchorFactory();
// you can now use scrollToAnchor.goToTarget(target)
});
You'll want jQuery's DOM ready function wrapped around this part, so that ScrollToAnchorFactory doesn't try to init before your anchors are fully formed in the DOM.
I'm trying to make a browser scroll to a point on a page if the page is scrolled down. I'm using jQuery .bind() to bind html to mousewheel. Here's my code:
"use strict";
var scrollpoint = 1;
$(document).ready(function(){
console.log(scrollpoint);
$("#divacon").hide();
$("#divbcon").hide();
$('#div-a').waypoint(function() {
$("#divacon").fadeIn();
var scrollpoint = 2;
console.log("waypoint a reached");
},{context:"#container"});
$('#div-b').waypoint(function() {
$("#divbcon").fadeIn();
console.log("waypoint b reached");
},{context:"#container"});
and
$('html').bind('mousewheel', function(e){
var flag = true;
if(flag) {
if(e.originalEvent.wheelDelta < 0) {
if((scrollpoint == 1)) {
var target = $('#div-a');
target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
var targetoffset = target.offset().top;
console.log(scrollpoint);
$('#container').animate(
{scrollTop: targetoffset},
400,
function(){
var scrollpoint = 2;
console.log(scrollpoint);
}
);
}
else if((scrollpoint = 2)){
//scroll down
console.log('2');
}
else{
//scroll down
console.log('Down');
}
}else {
//scroll up
console.log('Up');
}
//prevent page fom scrolling
flag = false;
}
});
What's happening is that my if statement is being called after the first time, even when flag = false;. What am I doing wrong? The site can be found live at http://wilsonbiggs.com/sandy
I think it has to be
var flag = true; //put the flag out side mouse wheel bind.
$('html').bind('mousewheel', function(e){
if(flag) {
Otherwise each time your even triggers you set flag to true and the following if condition will be satisfied always.
I use the following script for a smooth scroll effect minus an amount of pixel on one page. The problem is, i click on one anchor link, the scroll effects works as it should but then i scroll back to the top of the page where the links are and click on another page. It doesnt work. I just copied the script from a webpage my javascript is very bad.
Thx for your help.
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 - 70;
$(this).click(function(event) {
if(event != 'undefined') {
event.preventDefault();}
$(scrollElem).animate({scrollTop: targetOffset}, 400, function(e) {
e.preventDefault();
location.hash = target;
});
});
}
}
});
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;
}
}
}
}
There's an error in the code.
Where it says:
$(scrollElem).animate({scrollTop: targetOffset}, 400, function(e) {
e.preventDefault();
location.hash = target;
});
You are calling preventDefault() on nothing. Leave it as this and it will work:
$(scrollElem).animate({scrollTop: targetOffset}, 400, function() {
location.hash = target;
});
The error was at the beggining of the code. Instead of using an each loop, you should add a clickevent to the anchor elements. By using the each loop, you only add the click event once, and thus on the second click the event is undefined and the code runs into an error.
Here's your code rewritten as a click event:
$('a[href*=#]').click(function(e){
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 - 70;
$(scrollElem).animate({scrollTop: targetOffset}, 400, function() {
if( e != 'undefined' ) {
e.preventDefault();
}
location.hash = target;
});
}
}
});
Hope this helps ;-)
I have a page that I'm building and I would like to make it that when I scroll (up or down) the page scrolls to the next div (each div is 100% the height of the window). And gets "fixed" there until you scroll again. An example of what I'm trying to accomplish can be seen here:
http://testdays.hondamoto.ch/
You will notice that when you scroll down, it automatically moves you to the next "div".
What I've tried:
Using the jQuery .scroll event combined with:
function updatePosition() {
if(canScroll) {
var pageName;
canScroll = false;
var st = $(window).scrollTop();
if (st > lastScrollTop){
// downscroll code
if(pageNumber < 7) {
pageNumber++;
}
pageName = '#' + getPageToScrollTo().id;
$('body').animate({ scrollTop: $(pageName).offset().top }, 2000, function() {
canScroll = true;
});
} else {
// upscroll code
if(pageNumber > 0) {
pageNumber--;
}
pageName = '#' + getPageToScrollTo().id;
$('body').animate({ scrollTop: $(pageName).offset().top }, 2000, function() {
canScroll = true;
});
}
lastScrollTop = st;
}
}
But the scroll event was getting called when the page was scrolling (animating), AND when the user scrolled. I only need it to be called when the user scrolls.
Then I added:
var throttled = _.throttle(updatePosition, 3000);
$(document).scroll(throttled);
From the Underscore.js library - but it still did the same.
Finally, I browsed here a bit and found:
Call Scroll only when user scrolls, not when animate()
But I was unable to implement that solution. Is there anyone that knows of any libraries or methods to get this working?
EDIT:
Solution based on Basic's answer:
function nextPage() {
canScroll = false;
if(pageNumber < 7) {
pageNumber++;
}
pageName = getPageToScrollTo();
$('html, body').stop().animate({ scrollTop: $(pageName).offset().top }, 1000, function() {
canScroll = true;
});
}
function prevPage() {
canScroll = false;
if(pageNumber > 0) {
pageNumber--;
}
pageName = getPageToScrollTo();
$('html, body').stop().animate({ scrollTop: $(pageName).offset().top }, 1000, function() {
canScroll = true;
});
}
//--Bind mouseWheel
$(window).on(mousewheelevt, function(event) {
event.preventDefault();
if(canScroll){
if(mousewheelevt == "mousewheel") {
if (event.originalEvent.wheelDelta >= 0) {
prevPage();
} else {
nextPage();
}
} else if(mousewheelevt == "DOMMouseScroll") {
if (event.originalEvent.detail >= 0) {
nextPage();
} else {
prevPage();
}
}
}
});
Ok...
The relevant code for the Honda site can be found in http://testdays.hondamoto.ch/js/script_2.js. It seems to be doing some calculations to locate the top of the div then scroll to it. There are handlers for different types of scrolling.
Specifically, the movement is handled by function navigation(target)
the key bits is here...
$('html,body').stop().animate({
scrollTop: $(target).offset().top + newMargin
}, 1000,'easeInOutExpo',function(){
//Lots of "page"-specific stuff
}
});
There are handlers for the scroll types...
$('body').bind('touchstart', function(event) {
//if(currentNav!=3){
// jQuery clones events, but only with a limited number of properties for perf reasons. Need the original event to get 'touches'
var e = event.originalEvent;
scrollStartPos = e.touches[0].pageY;
//}
});
//--Bind mouseWheel
$('*').bind('mousewheel', function(event, delta) {
event.preventDefault();
//trace('class : '+$(this).attr('class') + ' id : '+$(this).attr('id'));
if(!busy && !lockScrollModel && !lockScrollMap){
if(delta<0){
nextPage();
}else{
prevPage();
}
}
});
You'll note that the navigate() function sets a busy flag which is unset when scrolling completes - which is how it suppresses all new scroll events during a scroll. Try changing the direction of scroll while the page is already scrolling and you'll notice user input is being ignored too.
I'm having an odd problem on Safari (Mac OS only, Windows works fine), where my smooth scroll is not scrolling. It just jumps to the link, but works in all other browsers, even on windows Safari.
My jQuery is
<script type="text/javascript">
(function($){
$.extend({
smoothAnchors : function(speed, easing, redirect){
speed = speed || "slow";
easing = easing || null;
redirect = (redirect === true || redirect == null) ? true : false;
$("a").each(function(i){
var url = $(this).attr("href");
if(url){
if(url.indexOf("#") != -1 && url.indexOf("#") == 0){
var aParts = url.split("#",2);
var anchor = $("a[name='"+aParts[1]+"']");
if(anchor){
$(this).click(function(){
if($(document).height()-anchor.offset().top >= $(window).height()
|| anchor.offset().top > $(window).height()
|| $(document).width()-anchor.offset().left >= $(window).width()
|| anchor.offset().left > $(window).width()){
$('html, body').animate({
scrollTop: anchor.offset().top,
scrollLeft: anchor.offset().left
}, speed, easing, function(){
if(redirect){
window.location = url
}
});
}
return false;
});
}
}
}
});
}
});
})(jQuery);
</script>
and my HTML looks like this
<nav id="nav">
<ul id="navigation">
<li> ABOUT</li>
<a name="About"></a>
If anybody knows what the issue, please let me know!
Much appreciated.
Works great for me!
(function($) {
$.fn.SmoothAnchors = function() {
function scrollBodyTo(destination, hash) {
// Change the hash first, then do the scrolling. This retains the standard functionality of the back/forward buttons.
var scrollmem = $(document).scrollTop();
window.location.hash = hash;
$(document).scrollTop(scrollmem);
$("html,body").animate({
scrollTop: destination
}, 200);
}
if (typeof $().on == "function") {
$(document).on('click', 'a[href^="#"]', function() {
var href = $(this).attr("href");
if ($(href).length == 0) {
var nameSelector = "[name=" + href.replace("#", "") + "]";
if (href == "#") {
scrollBodyTo(0, href);
}
else if ($(nameSelector).length != 0) {
scrollBodyTo($(nameSelector).offset().top, href);
}
else {
// fine, we'll just follow the original link. gosh.
window.location = href;
}
}
else {
scrollBodyTo($(href).offset().top, href);
}
return false;
});
}
else {
$('a[href^="#"]').click(function() {
var href = $(this).attr("href");
if ($(href).length == 0) {
var nameSelector = "[name=" + href.replace("#", "") + "]";
if (href == "#") {
scrollBodyTo(0, href);
}
else if ($(nameSelector).length != 0) {
scrollBodyTo($(nameSelector).offset().top, href);
}
else {
// fine, we'll just follow the original link. gosh.
window.location = href;
}
}
else {
scrollBodyTo($(href).offset().top, href);
}
return false;
});
}
};
})(jQuery);
$(document).ready(function() {
$().SmoothAnchors();
});
https://github.com/alextrob/SmoothAnchors