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.
Related
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.
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.
I want to make a javascript slideshow using a for loop
Javascript code
var Image_slide = new Array("img1.jpg", "img2.jpg", "img3.jpg");// image container
var Img_Lenght = Image_slide.length; // container length - 1
function slide(){
for (var i = 0; i < Img_Lenght; i++) {
Image_slide[i];
};
document.slideshow.src = Image_slide[i];
}
function auto(){
setInterval("slide()", 1000);
}
window.onload = auto;
html code
<img src="img1.jpg" name="slideshow">
i can't figure out what is the problem of this code it just run img3 continuously without looping img1 and it also skip img2 from the loop
The better option to solve this than to use a for loop is to simply skip the for loop all-together. Using a for loop is really too complicated and there's a far simpler solution.
Rather than using a for loop, simply assign the slides directly and keep track of positioning:
var Image_slide = new Array("img1.jpg", "img2.jpg", "img3.jpg");// image container
var Img_Length = Image_slide.length; // container length - 1
var Img_current = 0
function slide() {
if(Img_current >= Img_Length) {
Img_current = 0;
}
document.slideshow.src = Image_slide[Img_current];
Img_current++;
}
function auto(){
setInterval(slide, 1000);
}
window.onload = auto;
Interval should already run anyway. The loop inside the auto is redundant and simply messes it up. You only need to get one array element each time, not the whole loop. Looping through it every time will only return the last result.
You need to keep track of your position and reset the position to 0 once you reach the max length.
I'd also recommend at least 3 seconds for the interval instead of 1 second. One second I think is a bit too fast.
Here's an example of the correct solution on JSFiddle: http://jsfiddle.net/LUX9P/
NOW, that said, the question is actually asking how to make it work with a for loop. I've written up a potential solution to the problem (untested so I can't guarantee it will work), but I HIGHLY ADVISE NOT TO DO IT THIS WAY. It shouldn't be TOO bad overall, its just far more complicated and the solution above is so simple, this solution really isn't worth it.
var Image_slide = new Array("img1.jpg", "img2.jpg", "img3.jpg");// image container
var Img_Length = Image_slide.length; // container length - 1
function slide(){
delay = 0;
start = false;
for (var i = 0; i < Img_Length; i++) {
if(start && delay < 1000) {
delay += 1;
i--;
}
else {
document.slideshow.src = Image_slide[i];
delay = 0;
}
start = true;
}
}
function auto(){
setInterval("slide()", 1000);
}
window.onload = auto;
I cannot guarantee this will work, but essentially, the code I updated in slide() initializes a delay variable and a start variable. When the loop is run through once, it automatically activates start and always sets the first value in source.
Once start has been set, every consecutive time it will increment the delay variable until delay hits 1000, and it will decrement the i variable so that the for loop doesn't increment i over the cap (the length of the array). Basically, it sets i back by one so that the increment in for puts it back to where it should be, preventing it from moving on to the next variable until it finally processes the current entry.
This should, in theory, work. You may need to increase the delay significantly though; that 1000 should not actually equal one second; it'll likely go far faster than that. But I may be mistaken; it might run in one second, I haven't had a chance to try it out yet.
Clearly, the complexity of this is quite high, its just not worth it. My first option should be used instead.
Here is the problem, I've got a tree structure of html blocks, global container is of a fixed width(X) and height(Y). When i click one of the blocks on a level, all other blocks shrink to some size, while the clicked one gets enlarged to the leftover space, and the sublevels show up on it's place.
For all the shrinking i'm using default animate function with easing effect, when shrinking 1 level, to avoid enlargement bugs i have to do something like this:
$tabs.not($obj).animate({height:32<<$obj.getVerUp().length+"px"},{duration:300,
step:function() {
$obj.height(function(){
var sum = 0;
$tabs.not($obj).each(function(){
sum += $(this).height();
});
return $obj.getCont().height()-sum+"px";
});
}
});
$tabs are all the tabs of current level, $obj - is the one tab that i want to enlarge
The main problem is:
When i open up a tab that is on a deep level, i have to animate all the tabs of higher levels to shrink a little bit more, thus the $obj X and Y would change, so the current animation has to use new values, but if i call 3 different animations on different levels i'm bound to get a bug, when one of the animations on a deeper level finishes 1 step earlier, while the one on the level above, would enlarge the object by 5-10 more pixels and that space wouldn't be used up.
The second problem is that there has to be about 50 object animating with easing at the same time, which is a little bit overkill.
And the last problem is when i call step callback on animation as shown above, i have a strange feeling that it calls the step separately for each animation of the $tabs collection, while i need 1 step for all the tabs in the list (to avoid unnecessary scripts)
There might be some other way to fix all that, but i have yet to discover all jQuery functions, so from what i see the only way is to simulate easing, and do everything in one single animation.
I don't really want to use setInterval and determining when do i need to clear it plus calculating each of the easing values, if there is a simple way doing it.
Does jQuery has some sort of empty animation easing, e.g.
$().css("height":starth+"px").animate({height:endh},{duration:300,
step:function(fn) {
// all the animation actions here via fn end value
}
});
Thanks in advance.
What I need - is not a completely working solution in code, just some enlightenment in those subjects:
Is there a legal way to call one step function for a collection of animated elements, or, maybe, it does call step once when I use one .animate on collection.
I'd be really appreciated if someone would shed some light over how does jquery handle multiple .animate, would they be used in one global function that works on .setInterval? or would they be having massive number of those .setIntervals that are equivalent to setTimeout (which most browsers can't handle in large amounts);
Is there a way to simulate 'animate' easing, some function name maybe, or a special trick to achieve that (the only thing I see is a hidden element or 'window' property to change maybe)
Or some directed pushes with functions I should study, that could help me achieve my goals
Guess i pretty much found the answer to my questions:
http://james.padolsey.com/javascript/fun-with-jquerys-animate/
Here's the empty animation from the link above with 1 step function with desired values, going to post the result later on if it all works out.
var from = {property: 0};
var to = {property: 100};
jQuery(from).animate(to, {
duration: 100,
step: function() {
console.log( 'Currently # ' + this.property );
}
});
Yes it all worked great, no desynch, and a good speed, since only 1 animate, found making one universal function for the animation - waste of resourses, so it is pretty specific, but still, here it is:
animate: function($obj) {
var T = this;
...
T.arr = new Array();
// gathering the array
$obj.each(function(i){
var size;
T.arr[i] = {obj:$(this), rest:$(this).getSibl(), cont:$(this).getCont()}
if($(this).hasClass("vert"))
{
size = "height";
T.arr[i].to = yto;
}
else
{
size = "width";
T.arr[i].to = xto;
T.arr[i].children = $(this).getChld();
}
T.arr[i].rest.each(function(){
$(this).attr("from",$(this)[size]());
});
});
// animating prop
jQuery({prop:0}).animate({prop:1}, {
duration: 300,
step: function() {
var i;
var P = this;
var newval;
var sum;
var size;
for(i = 0; i < T.arr.length; i++)
{
size = T.arr[i].obj.hasClass("vert") ? "height":"width";
sum = 0;
T.arr[i].rest.each(function(){
// new value of width/height, determined by the animation percentage
newval = parseInt($(this).attr("from")) + (T.arr[i].to-$(this).attr("from"))*P.prop;
$(this)[size](newval);
sum += newval;
});
T.arr[i].obj[size](T.arr[i].cont[size]()-sum);
}
}
});
},
I'm using OpenX at work, and one of my boss requirements is a expandable banner. For that (and made a horrible simplification of the whole story) I made this script.
function retro(){
var acs = jQuery('#trial_center').height() - 5;
jQuery('#trial_center').css('height', acs + 'px');
}
jQuery(document).ready(function(){
jQuery("#trial_center").mouseover(function(){
setTimeout("jQuery('#trial_center').css('height', '500px')", 1000);
})
jQuery("#trial_center").mouseleave(function(){
var c = 89;
while (c > 0) {
setTimeout("retro()", 1000);
c--;
}
})
});
The problem I have is in the mouseleave event: the original idea was to made this loop several times (89 times), and each time, decrease the height of the banner until it get his original size. Why this? Because my boss want an "effect", and this effect must be in sync with the customer's flash.
The problem is that instead of decrease his size progressively, apparently the script made all the operations an "after" the sum of setTimeout calls, updated the page. So, the result is exactly as the banner shrinks one time from the expanded size to the original size.
I don't know what is wrong with this, or if exists other more intelligent solution.
Any help will be very appreciate.
Thanks in advance
Your loop setting the timeout is just setting 89 timers for one second later than the loop runs, and the loop will run in milliseconds — so they'll all fire about a second later. That doesn't sound like what you want to do.
Two options for you:
1. Use animate
jQuery's animate function seems like it does what you want. You can tell jQuery to animate the size change, and you tell it how long to take to do so:
jQuery('#trial_center').animate({
height: "500px" // Or whatever the desired ending height is
}, 1000);
That will animate changing the height of the container from whatever it is at the point that code runs to 500px, across the course of 1,000 milliseconds (one second). Obviously you can change the duration to whatever you like.
2. Set up the timer loop manually
If for whatever reason you don't want to use animate, you can do this manually (of course you can; jQuery can't do anything you can't do yourself, it just makes things easier). Here's how to set up a timer loop:
jQuery("#trial_center").mouseleave(function(){
var c = 89;
// Do the first one right now, which will schedule the next
iteration();
// Our function here lives on until all the iterations are
// complete
function iteration() {
// Do one
retro();
// Schedule this next unless we're done
if (--c > 0 {
setTimeout(iteration, 100); // 100ms = 1/10th second
}
}
});
That works because iteration is a closure over c (amongst other things). Don't worry about the term "closure" if it's unfamiliar, closures are not complicated.
Separately: You're using mouseover to set the height of the trial_center element a second later; you probably wanted mouseneter rather than mouseover. mouseover repeats as the mouse moves across it.
Off-topic:
It's best not to use strings with setTimeout; just pass it a function reference instead. For example, instead of
setTimeout("retro()", 1000);
you'd use
setTimeout(retro, 1000); // No quotes, and no ()
And for the other place you're using, instead of
setTimeout("jQuery('#trial_center').css('height', '500px')", 1000);
you'd use
setTimeout(function() {
jQuery('#trial_center').css('height', '500px');
}, 1000);