getting big lag with scroll() - javascript

I'm trying to animate my menu with jquery - simply change padding depending on scroll. It works fine, however takes extremely long to execute (1-10 seconds). Any ideas? I have tried .on('scroll') but no change.
Both on Chrome and Firefox.
Fun thing - if I add alert(v); after 3rd line to be sure on scroll position, it animates well, without any lag ;x
<script>
$(window).scroll(function() {
var v = $(window).scrollTop();
if (v >= 150) {
$('#header').animate({"padding": "15px 0px"},500);
}
else if (v < 150 ) {
$('#header').animate({"padding": "50px 0x"},500);
}
});
</script>
You can see the problem here:
http://monsiorski.com/_projects/robson/

<script>
var isScrolled = false;
$(window).scroll(function() {
var v = $(window).scrollTop();
if (v >= 150 && !isScrolled) {
$('#header').animate({"padding": "15px 0px"},500);
isScrolled = true;
}
else if (v < 150 && isScrolled) {
$('#header').animate({"padding": "50px 0x"},500);
isScrolled = false;
}
});
</script>

This is because of two reasons: A) The scroll event is fired every scrolled pixel, and B) you calculate a lot every time.
A) is fixed very easily, you can limit the amount of events fired by dethrottling or debouncing it. You can set it to fire every 20ms which is still extremely fast, but will save tremendous amounts of calls.
B) Limit the calculations you do. The more often you trigger a function, the more you have to doublecheck a function whether you can make it more lightweigth.
In your case you calculate how much is scrolled from the top, and set the value every call. Instead, you want to also store of you've already passed the threshold. If you've already passed it and the scrollvalue>threshold, there is no need to set it again to the same value. Same goes for scrolling back. Checking a boolean is extremely fast.

Related

Slow Scroll Toggle with no jQuery?

I know its a bit to ask, but is the following possible without using jQuery? I have it running with jQuery now but it seems to be presenting performance issues. If you could help I will be most grateful. I am not lazy, just not very code knowledgable. Took me a while to even get this far.
//
// default speed ist the lowest valid scroll speed.
//
var default_speed = 1;
//
// speed increments defines the increase/decrease of the acceleration
// between current scroll speed and data-scroll-speed
//
var speed_increment = 0.01;
//
// maximum scroll speed of the elements
//
var data_scroll_speed_a = 2; // #sloganenglish
var data_scroll_speed_b = 5; // #image-ul
//
//
//
var increase_speed, decrease_speed, target_speed, current_speed, speed_increments;
$(document).ready(function() {
$(window).on('load resize scroll', function() {
var WindowScrollTop = $(this).scrollTop(),
Div_one_top = $('#image-ul').offset().top,
Div_one_height = $('#image-ul').outerHeight(true),
Window_height = $(this).outerHeight(true);
if (WindowScrollTop + Window_height >= (Div_one_top + Div_one_height)) {
$('#sloganenglish').attr('data-scroll-speed', data_scroll_speed_a).attr('data-current-scroll-speed', default_speed).attr('data-speed-increments', data_scroll_speed_a * speed_increment);
$('#image-ul').attr('data-scroll-speed', data_scroll_speed_b).attr('data-current-scroll-speed', default_speed).attr('data-speed-increments', data_scroll_speed_b * speed_increment);
increase_speed = true;
decrease_speed = false;
} else {
$('#sloganenglish').attr('data-scroll-speed', '1').attr('data-current-scroll-speed', default_speed);
$('#image-ul').attr('data-scroll-speed', '1').attr('data-current-scroll-speed', default_speed);
decrease_speed = true;
increase_speed = false;
}
}).scroll();
});
I don't see any performance issue in your code, although there is space for some optimization. And I don't think jQuery might be the problem.
First thing to notice is the CSS access.
The height attribute is very expensive to access because it causes the browser to process many rendering steps of the pipeline, as you can see in CSS Triggers.
You are retrieving the height of two elements in a scroll event, which means that they will be calculated many times. Is it really necessary?
If your #image-ul element doesn't change its height, maybe you can calculate it outside of the event only once.
In the case of the window height, I believe it won't change in the scroll event. How about to create different handlers, one for the events that need to (re)calculate the window height and another for the events that don't need that calculation?
Another noticeable point is that you set the 'data-current-scroll-speed' and the 'data-speed-increments' attribute always with the same constant value. No change, no unset. Is it really necessary?
Actually, it is not clear what you are really doing. Your performance issue might be somewhere else.

How to disable multiple-scroll effect in osx and fire scroll event once per scroll

I need to organise navigation like jquery.fullPage: when I scroll once - I move to next section.
I tried to use jquery.mousewheel for it, but it fails in MacOS with touchpad or trackpad or APPLE Magic Mouse: It scrolls multiple slides per one scroll.
I tried to use this solution: https://github.com/jquery/jquery-mousewheel/issues/36#issuecomment-67648897
It works fine in Chrome, but it does not in FF and has bugs in Safari.
This is simple demonstration of issue:
http://jsfiddle.net/ss5LueLx/13/
$slider.on('mousewheel', function(event) {
if (event.deltaY>0) {
slider.prevSlide();
} else {
slider.nextSlide();
}
});
I tested this statement in my SeaMonkey browser and it fails.
var delta = e.type == 'mousewheel' ? e.originalEvent.wheelDelta * -1 : 40 * e.originalEvent.detail;
Just in case, I looked at the deltaY and it works: +1 in one direction and -1 in the other, just as you determined in the other two implementations you have.
console.log(e.deltaY); // view console in FireBug
Looking at the event structure in Firebug, I can see that the event type is "mousewheel", and yet I do not see a wheelData field in the originalEvent.
And although there is a detail field, but that one remains at zero.
I would imagine that you are attempting to go to the next item only once you reach +/-3. I would suggest something of the sort to accomplish this feat:
// somewhere, initialize to zero
var current_position = 0;
// on mousewheel events
current_position += e.deltaY;
// you had a x 40 which would indicate that the limit is about 120 / 40
// if 3 is too small, you can always increase it to 5 or event 10...
if(current_position <= -3)
{
slider.nextSlide();
current_position = 0;
}
else if(current_position >= 3)
{
slider.prevSlide();
current_position = 0;
}
Otherwise you could verify that your allowScroll flag works as expected. I do not program objects that way so I am not too sure whether it is correct or not. (I use the prototype syntax which is useful if you want to extend classes.)

Smooth div width change in javascript

I have a simple function which is increasing the width of a div but its not doing it smoothly its kinda of "juddery".
I'm using request animation frame to do this on Chrome.. and I decided not to round the numbers so I could get decimal width increments.. but I can't get it to be smooth at all was wondering how I can improve on my method.
This is my function:
function test(data){
var start = parseInt(data.start);
var length = parseInt(data.length); //total length in minutes to complete
var dif = (new Date().getTime() / 1000) - start; //start holds seconds since epoch
var minutes = dif / 60; //convert seconds past into minutes past
var percentage = (minutes/length) * 100;
if(percentage > 100){ percentage = 100; }
if( percentage != 100 ){
document.getElementById('r').style.width = percentage+'%';
document.getElementById('rt').innerHTML = Math.round(percentage)+'%';
} else if (percentage == 100 ){
document.getElementById('r').style.width = percentage+'%';
document.getElementById('rt').innerHTML = 'COMPLETED';
}
}
My function is called like this:
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback, element){
window.setTimeout(callback, 200 / 100);
};
})();
function Update(){
requestAnimFrame( Update );
test();
}
JSFIddle: http://jsfiddle.net/RmXr9/7/
Any suggestions on ways to improve the smoothness of div width incrementing ?
try using css transitions. You get way smoother animations, but requires you to structure your code a bit differently. An example of a css transition property is this:
transition:300ms linear;
then whatever property you change (the width for example) will make a smooth linear 300 millisecond transition towards it.
so making a smooth width change is as simple as setting up the transition then doing something like this in javascript:
div.style.width="400px";
Here's a quick example i mocked up:
http://jsfiddle.net/nTMsC/1/
here's a nice tutorial to get you started:
http://www.css3.info/preview/css3-transitions/
One of the biggest causes of 'juddery' animations for me has always been frame rate. If your frame rate is too slow, obviously the animation 'judders'. But if it is too fast for the browser to handle, the browser gets confused, and you get a different kind of 'juddery'.
I'd recommend a frame rate of between 13 and 30 milliseconds. JQuery is supposed to use 13ms, but I've found that that is sometimes still too fast. I generally start with 16ms, and experiment from there.
The key is to ensure that you time it so that one frame starts as or after the previous frame is finished. This will depend on the code you process. I notice that you call the next frame before you begin processing the current frame, so it may be possible that you're still getting backed up. Perhaps try:
function Update(){
test();
requestAnimFrame( Update );
}
Your fallback function has a frame rate of 200 / 100, which is 2ms. It is extremely unlikely that your browser can complete the animation in 2ms, so it is likelyto get backed up. requestAnimationFrame uses a maximum frame rate of 16ms.
UPDATE:
The problem you're having, according to your jsfiddle, is that, while you're calculating your percentage often, the changes to the percentage are very small, and they don't translate into a change in the width of the div. This http://jsfiddle.net/RmXr9/13/ should demontrate the changes in the percentage, and show the corrsponding changes in actual width. So, although you do a calculation often (maybe 60 times a second), the actual visual change only happens once every 16 frames or so. So, your actual frame rate is only about 4 frames per second, which makes a 'juddery' animation. Your only options, I'm afraid, are to make the animation run faster (perhaps by decreasing your length variable), or to make the div much longer (or both).
As an aside, I notice you don't have a way to stop the animation at the end, and I've added that into the jsfiddle as well.

Scroll page until some height

how can I scroll the page with javascript until the scrolled height will be some predefined number of pixels?
function pageScroll() {
// Next line is pseudocode:
if (window.ScrolledHeight != SOME_NUM)
window.scrollBy(0,50);
}
scrolldelay = setTimeout('pageScroll()',100);
There should be some logical checking of something. Could you help me, how to get scrolled height?
Could you use a variable outside of the function that would increment the value by 50 pixels everytime pageScroll ran and then check to see if that is equal to or greater than the value you are looking for?
e.g.
var scrollAmount = 0;
function pageScroll() {
window.scrollBy(0,50);
scrollAmount += 50;
if(scrollAmount < 200) {
scrolldelay = setTimeout('pageScroll()',100);
}
}
You could also make the function take in a parameter that you modify if you don't want to use the global variable.
You already had valid answers, but I will add a bit of flavor, it scrolls but slows down before reaching the targeted scroll point:
function pageScroll() {
var delta = min(SOME_NUM-window.ScrolledHeight)>>1,50);
if (delta > 0) {
window.scrollBy(0,delta);
setTimeout('pageScroll()',100);
}
}
A nice side effect is that it should reach exactly SOME_NUM regardless it is a multiple of 50 (your scroll amount) or not.
First off you shouldn't be using setTimeout you should be using setInterval. Basically because it fulfills your purpose better and it is more accurate as far as actually time. Google it if you want more info on that topic. But what you need is the clearInterval method. Also it is faster to use anonymous functions inside of setTimeout and setInterval.
startPageScroll(){
var scrollAmt = 0;
var id=setInterval(function(){
if(scrollAmt</*Whatever your limit is*/){
window.scrollBy(0,50);
scollAmt+=50;
}
else{clearInterval(id);}
},100);
}
There are ways of getting the actual scroll of the page, but you would have to do something like this which is quite a bit more code then just keeping a running total of how far you have moved.
var scroll;
if(window.pageYOffset){
scroll=window.pageYOffset;
}
else{
scroll=document.body.scrollTop;
}
Here is a link that might help http://codepunk.hardwar.org.uk/ajs02.htm.

How can I get the scrollbar position with JavaScript?

I'm trying to detect the position of the browser's scrollbar with JavaScript to decide where in the page the current view is.
My guess is that I have to detect where the thumb on the track is, and then the height of the thumb as a percentage of the total height of the track. Am I over-complicating it, or does JavaScript offer an easier solution than that? What would some code look like?
You can use element.scrollTop and element.scrollLeft to get the vertical and horizontal offset, respectively, that has been scrolled. element can be document.body if you care about the whole page. You can compare it to element.offsetHeight and element.offsetWidth (again, element may be the body) if you need percentages.
I did this for a <div> on Chrome.
element.scrollTop - is the pixels hidden in top due to the scroll. With no scroll its value is 0.
element.scrollHeight - is the pixels of the whole div.
element.clientHeight - is the pixels that you see in your browser.
var a = element.scrollTop;
will be the position.
var b = element.scrollHeight - element.clientHeight;
will be the maximum value for scrollTop.
var c = a / b;
will be the percent of scroll [from 0 to 1].
document.getScroll = function() {
if (window.pageYOffset != undefined) {
return [pageXOffset, pageYOffset];
} else {
var sx, sy, d = document,
r = d.documentElement,
b = d.body;
sx = r.scrollLeft || b.scrollLeft || 0;
sy = r.scrollTop || b.scrollTop || 0;
return [sx, sy];
}
}
returns an array with two integers- [scrollLeft, scrollTop]
It's like this :)
window.addEventListener("scroll", (event) => {
let scroll = this.scrollY;
console.log(scroll)
});
Answer for 2018:
The best way to do things like that is to use the Intersection Observer API.
The Intersection Observer API provides a way to asynchronously observe
changes in the intersection of a target element with an ancestor
element or with a top-level document's viewport.
Historically, detecting visibility of an element, or the relative
visibility of two elements in relation to each other, has been a
difficult task for which solutions have been unreliable and prone to
causing the browser and the sites the user is accessing to become
sluggish. Unfortunately, as the web has matured, the need for this
kind of information has grown. Intersection information is needed for
many reasons, such as:
Lazy-loading of images or other content as a page is scrolled.
Implementing "infinite scrolling" web sites, where more and more content is loaded and rendered as you scroll, so that the user doesn't
have to flip through pages.
Reporting of visibility of advertisements in order to calculate ad revenues.
Deciding whether or not to perform tasks or animation processes based on whether or not the user will see the result.
Implementing intersection detection in the past involved event
handlers and loops calling methods like
Element.getBoundingClientRect() to build up the needed information for
every element affected. Since all this code runs on the main thread,
even one of these can cause performance problems. When a site is
loaded with these tests, things can get downright ugly.
See the following code example:
var options = {
root: document.querySelector('#scrollArea'),
rootMargin: '0px',
threshold: 1.0
}
var observer = new IntersectionObserver(callback, options);
var target = document.querySelector('#listItem');
observer.observe(target);
Most modern browsers support the IntersectionObserver, but you should use the polyfill for backward-compatibility.
If you care for the whole page, you can use this:
document.body.getBoundingClientRect().top
Snippets
The read-only scrollY property of the Window interface returns the
number of pixels that the document is currently scrolled vertically.
window.addEventListener('scroll', function(){console.log(this.scrollY)})
html{height:5000px}
Shorter version using anonymous arrow function (ES6) and avoiding the use of this
window.addEventListener('scroll', () => console.log(scrollY))
html{height:5000px}
Here is the other way to get the scroll position:
const getScrollPosition = (el = window) => ({
x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft,
y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop
});
If you are using jQuery there is a perfect function for you: .scrollTop()
doc here -> http://api.jquery.com/scrollTop/
note: you can use this function to retrieve OR set the position.
see also: http://api.jquery.com/?s=scroll
I think the following function can help to have scroll coordinate values:
const getScrollCoordinate = (el = window) => ({
x: el.pageXOffset || el.scrollLeft,
y: el.pageYOffset || el.scrollTop,
});
I got this idea from this answer with a little change.

Categories

Resources