jQuery hashchange event instantly executes animation with no speed - javascript

I am having trouble with jQuery and wrapping my animation inside hashchange event. When event is triggered animation happens instantly. I need it to be smooth.
jQuery( document ).ready(function() {
jQuery(window).on('hashchange', function() {
jQuery('body').animate({ scrollTop: jQuery(window.location.hash).offset().top}, 'slow');
});
});
Everythings is fine if i don't wrap animation inside a hashchange event...

If you're changing a hash to an anchor that exists, it'll automatically jump to that anchor without waiting for the animation. You can see that this happens by just removing your animation since it still jumps. This can be fixed by using hashes in your URL that don't actually have an element with the corresponding id and changing your selector is scrollTop to accommodate for this.
For example, you could change the id of an "about" section to be about-section and continue to use #about as the hash. Then instead of scrollTop: jQuery(window.location.hash).offset().top, you'd use scrollTop: jQuery(window.location.hash + "-section").offset().top to avoid the automatic jump to the element.

Related

How to prevent spamming of an onclick listener?

This is a new issue for me and I've been unable to find any information about this.
I have a simple onclick listener that performs a smooth scroll to a target on the page. Here is the code below:
$(".nav-scroll").click(function (event) {
var ID = this.hash;
//has a few extra conditionals here specific to the page
$('html,body').animate({ scrollTop: $(ID).offset().top }, 1000);
});
Ultimately, the scenario is that this function takes time to execute. Not much time, but it does add up if someone spams the link on the page and the browser starts queuing the function calls. Now spamming is an extreme case, but even after 3 or 4 consecutive clicks it hinders the user experience while they can't scroll down the page because the page is trying to scroll them back to the target 3 or 4 times.
All my research has been able to turn up is checking if a window has an event listener already like found here: JavaScript - how to check if event already added or listing all the event listeners on an element like here: jQuery find events handlers registered with an object , but nothing to check to see if something is currently running.
Is it possible to prevent this side effect by dumping all previous listener calls on the page mid execution before executing or by another method? Or is this something that is not offered by JavaScript? If so, is there strategies to get around this like checking to see if the function is already executing?
From the conversation in the comments, it sounds like you want the users to be able to click on the element as fast as they want to, and have it interrupt the existing animation and start a new one, rather than queueing them up and causing it to scroll around the page. You can achieve this by simply using stop:
$(".nav-scroll").click(function (event) {
var ID = this.hash;
//has a few extra conditionals here specific to the page
//call stop to kill old animations
$('html,body').stop().animate({ scrollTop: $(ID).offset().top }, 1000);
});
A debouncing approach would prevent them from clicking at all until the animation ends, rather than allowing them to click one element, realise they've clicked in slightly the wrong place and quickly click the right element.
You need this:
var debounce = false;
$(".nav-scroll").click(function (event) {
if (debounce) return;
debounce = true;
var ID = this.hash;
//has a few extra conditionals here specific to the page
$('html,body').animate({ scrollTop: $(ID).offset().top}, 1000, function() {debounce=false;});
});
Basically, it disables the onclick event on fire until it is scrolled up, then enables the event.
You could wrap you onClick function in a throttle which keeps it from executing again while it's scrolling.
Like so:
// lodash is defined here as _
var animationTime = 1000;
var scrollTo = _.throttle(function (event) {
var ID = this.hash;
//has a few extra conditionals here specific to the page
$('html,body').animate({ scrollTop: $(ID).offset().top }, animationTime);
}, animationTime);
$(".nav-scroll").click(scrollTo);
Is this case the first time the users clicks the function gets called but if they click again within the time frame (the 1000ms) the the function does not execute again. Only after the time has passed can the user invoke the function again.
Here you can find the documentation for lodash throttle:
https://lodash.com/docs#throttle

On Scroll fires automatically on page refresh

Im halfway down my page as I have an anchor. I also have a window scroll event:
$(window).scroll(function(){
});
When I refresh the page, the scroll event fires. Is there a way to prevent this on the refresh but still listen for the scroll event when the user scrolls?
I believe your scroll code only fires if you refresh the page and the page is scrolled. That's because the browser will reload the page, and then scroll to the original position.
The solution suggested by Arnelle does not work well, because the scroll event only fires initially if the page was scrolled when you refreshed it.
Hack Alert
What I found that does work is waiting to set the scroll handler. Be careful, I'm using magic numbers that may not work on all connections.
//Scroll the page and then reload just the iframe (right click, reload frame)
//Timeout of 1 was not reliable, 10 seemed to be where I tested it, but again, this is not very elegant.
//This will not fire initially
setTimeout(function(){
$(window).scroll(function(){
console.log('delayed scroll handler');
});
}, 10);
//This will fire initially when reloading the page and re-establishing the scroll position
$(window).scroll(function(){
console.log('regular scroll handler');
});
div {
height: 2000px;
border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
</div>
And yes, I know I bashed Arnelle by saying they patched a problem without understanding, and here I am, suggesting a patch. The only difference is that I think I understand the problem.
My main question to the OP is. Most scroll handlers that I've written work fine if called multiple times, what is the problem with your handler being called an extra time?
You can use a flag variable to detect whether the scroll event is the initial scroll event or not, effectively stopping the callback function's execution for the scroll event on page reload.
var initialScrollEvent = true;
$(window).scroll(function() {
if (!initialScrollEvent) {
// callback function body
}
initialScrollEvent = false;
});

jQuery scrollTop goes to the target then rolls up

I am using jQuery function scrollTop so when an element with a certain class is clicked your location changes. Here's what I have done:
$(document).ready(function (){
$(".paginacion").click(function() {
$(document).scrollTop( $("#galeria").offset().top );
});
});
I am navigating though a pagination menu which is in the middle of the page and I want to go back to that menu when I use the utility (clicking any element with the pagination class).
When I click any of those elements the page scrolls down for an instant but then scrolls back up.
What's wrong?
The <a> tag has a default href anchoring, which jumps to the target id and changes the URL hash/fragment. Just like #Khanh TO's example on the comment.
But if you are really wanting to handle this with jQuery. A good solution would be to first use preventDefault() which cancels the default execution on click event. Then switch to 'window' instead of 'document' when setting scrollTop. Both are going to have the same effect but $(window).scrollTop(value) is supported by all browsers.
$(".paginacion").click(function(e) {
e.preventDefault();
$(window).scrollTop( $("#galeria").offset().top );
});
If you are also looking to animate the scrolling, you just need to replace $(window).scrollTop() with:
$('html, body').animate({scrollTop: $("#galeria").offset().top});
FireFox and IE places the overflow at the html level so in order for animate(scrollTop) to work cross-browsers we need to include 'html'.
See this jsfiddle.

Avoid page back to top after load

I'm using this script
jQuery(document).ready(function($) {
$(".scroll").click(function(event){
event.preventDefault();
$('html,body').animate({scrollTop:$(this.hash).offset().top}, 2000);
});
});
in order to smooth scroll down when my nav elements are clicked... the problem is that if a link is clicked before page finish loading, when it finishes the page will go back to top again.
I thought event.preventDefault(); was to avoid that. Help please.
you should use the document.onLoad event instead.
document.ready is invoked after all of the HTML is brought down into the document and ready for parsing.
onLoad on the other hand is invoked after all images / resources are loaded into the page as well.
If you wait for this event, then you should have desired results. although they won't have any click functionality until then.
Furthermore, preventDefault does not avoid this. All that does is disable the default action of the element you apply it to. so it prevent's whatever the default action would be for your 'scroll' elements

How to stop previous dom element animation in jquery

I am trying to use animation using jquery. When i mouseover the element, it increases the height and when mouseout, returns to original height.
The Problem is when i mouseover from one element to another element, the previous element's animation is still performing. how to stop the animation of the previous element?
Fiddle code:
http://jsfiddle.net/EVZVQ/
Use .stop(): http://api.jquery.com/stop
$('.div_1').mouseover(function(){
$(this).stop().animate({'height':'200px'},2000);
}).mouseout(function(){
$(this).stop().animate({'height':'100px'},2000);
});
Notice you can chain the event binding functions instead of selecting .div_1 twice in a row.
Here is a demo: http://jsfiddle.net/EVZVQ/1/
When .stop() is called on an element, the currently-running animation
(if any) is immediately stopped. If, for instance, an element is being
hidden with .slideUp() when .stop() is called, the element will now
still be displayed, but will be a fraction of its previous height.
Callback functions are not called.
Source: http://api.jquery.com/stop
Update
You can stop all the divs at once like this (but I'm not sure this is the effect you're looking for):
var $divs = $('.div_1');
$divs.mouseover(function(){
$divs.stop().filter(this).animate({'height':'200px'},250);
}).mouseout(function(){
$divs.stop().filter(this).animate({'height':'100px'},250);
});
Here is a demo: http://jsfiddle.net/EVZVQ/2/
This works better.
$('.div_1').mouseover(function(){
$(this).stop(true).animate({'height':'200px'},200);
});
$('.div_1').mouseout(function(){
$(this).stop(true).animate({'height':'100px'},200);
});
http://jsfiddle.net/diode/EVZVQ/7/

Categories