As a way to learn more about jQuery, I'm trying to make a function that will scroll to the bottom of the page, back to the top and then start again in an infinite loop.
so far I have got this
var scroll = $(document).scrollTop();
setInterval(function(){
scroll += 5;
$(document).scrollTop(scroll);
}, 10);
This will scroll to the bottom of the page.
When it hits the bottom it needs to set a flag, maybe var direction = 1
where 1 is down and 0 is up.
Now I need to add something like
if(direction){
// scroll down
} else {
// scroll up
}
So I the IF would constantly check the value of direction and increment the scroll var by either += 5 or -=5
Does that sound logical or is there a much simpler way to achieve this?
EDIT
Here is a working version of somethinghere's suggestion
https://jsfiddle.net/Panomosh/evkxou3e/
You should use jQuerys callback to create an infinitely recursing function. Just add a function after the declaration in your animate function, and then call the same function, but invert the passed boolean.
Also, animate will do this incrementing very nice and smoothly for you!
function scrollTopOrBottom(top){
if(top) $("body, html").animate({scrollTop: 0}, function(){
scrollTopOrBottom(false);
});
else $("body, html").animate({scrollTop: $("document").height()}, function(){
scrollTopOrBottom(true);
});
}
scrollTopOrBottom(false);
Update: As mentioned, this could create a * * ahem * * Stack Overflow, but the question is not concerned with that at this point. If you want to be safe, use a stop() after each $("body, html") statement to stop the call from proceeding if another one is in progress, therefore deferring overflow.
Note: You could also wrap the if statement in {} but since it's one statement I find it a bit more legible, more sentence-like than code-like. But thats preference.
You could set the value of direction to be either 1 to scroll down or -1 to scroll up.
Then when changing the value of scroll you can do something like:
scroll += 5 * direction;
Related
So I have two sections of content near the top of my page and I’d like for users who have scrolled down to near the top of the second section to get “scroll snapped” to the top of the second one once they have stopped scrolling.
I think it should be possible using jQuery but I haven’t been able to figure it out. Here are my examples:
Without my attempt: http://codepen.io/jifarris/pen/gaVgBp
With my broken attempt: http://codepen.io/jifarris/pen/gaVgQp
Basically I can’t figure out how to make it try scrolling to the spot only once, after scrolling has stopped. It’s kind of just freaking out.
I love how the recently introduced scroll snap points CSS feature handles scroll snapping and I’d almost prefer to use it – for the browsers that support it, at least – but it seems like it only works for items that take up 100% of the viewport height or width, and it seems like it’s for scrolling within an element, not the page itself.
The top section has a fixed height, so this really can be handled with pixel numbers.
And for reference, here’s the heart of the code from my attempt:
$(function() {
$(document).on('scroll', function() {
var top = $(document).scrollTop();
if (top > 255 && top < 455) {
$('html, body').animate({scrollTop: '356'}, 500);
$('body').addClass('hotzone');
} else {
$('body').removeClass('hotzone');
}
});
});
KQI's answer contains most of the steps required to create a well functioning section-scroll for use in your application/webpage.
However, if you'd just want to experiment yourself, developing your script further, the first thing you'll have to do is add a timeout handler. Otherwise your logic, and therefor scrollAnimation, will trigger every single pixel scrolled and create a buggy bouncing effect.
I have provided a working example based on your script here:
http://codepen.io/anon/pen/QjepRZ?editors=001
$(function() {
var timeout;
$(document).on('scroll', function() {
clearTimeout(timeout);
timeout = setTimeout(function() {
var top = $(document).scrollTop();
if (top > 255 && top < 455) {
$('body').animate({
scrollTop: '356'
}, 500);
$('body').addClass('hotzone');
} else {
$('body').removeClass('hotzone');
}
}, 50);
});
});
Good luck!
All right, there are couple of things you gonna have to deal with to get a good result: which are performance, call stack queue, easing.
Performance wise you should drop jQuery animate and use VelocityJs which gives a smoother transition, better frame per second (fps) to avoid screen glitches especially on mobiles.
Call stack: you should wrap whatever logic you have to animate the scrolltop with 'debounce' function, set the delay for let say 500mm and check the scrolling behavior. Just so you know, the 'scroll' listener your using is firing on each pixel change and your script will go crazy and erratic. (It is just gonna be a moment of so many calc at the same time. Debounce will fix that for you)
Easing: make the transition looks cool not just dry snappy movement.
Remember, 'easing' with Velocity starts with 'mina.' i.e.
'Mina.easingFnName'
Finally, your logic could be right, i am in my phone now cannot debug it but try to simplify it and work with a single problem at once, be like i.e.
If ( top > 380 ) // debounce(...)
I'm trying to fire an event when scrolling down to a certain point/value and execute again when scroll back up and down again.
I tried:
$(window).scroll(function(){
var sTop = $(this).scrollTop();
if(sTop == 300){
//execute event - failed
}
});
The above does not fire anything with the both mousewheel and dragging the scrollbar. but when I changed the condition to greater than or equal to, the event is being executed but multiple times when scrolling further down.
if(sTop >= 300){
//execute event - success
}
I also tried changing the condition value and some times the event is being fired.
if(sTop == 333 /* or 431, 658, etc. */){
//execute event - sometimes success
}
Here is my test fiddle.
Could it be that scroll() function sometimes skips a value if you scroll fast?
Can someone explain the issue and help me with a workaround on how to trigger an event when the $(window).scrollTop() == 'value'. Thanks
You're facing this issue because there are no guarantees that scrolling will be done in increments of 1. For example, some scroll wheels increment in values of 30. This means that you'd get a scroll event on 30, 60, 90, etc.
The best way to test if scrolling has reached your limit is to see if it has gone past the point you're wanting, then resetting the scroll to the precise point you're wanting. So you'd do:
if(sTop >= 300){
$(window).scrollTop(300);
}
Here's an updated link to your JSFiddle with a working example: http://jsfiddle.net/bC3Cu/4/
If you take a look at unit of increment in scrollable areas / divs in javascript? it describes how to set it to where you can override the scroll increment. Using that method, you should be able to predict how many units the user has scrolled without preventing them from scrolling further down the page.
Hello StackOverflow Community,
what I am trying to achieve is a header that can be moved with the mouse.
You klick into the header and drag the mouse and the elements inside the header will move with different speeds.
I achieved the parallaxing part but the performance is not really good. It is partially a bit laggy while dragging the backgrounds.
My question now is: what can be changed in the code to get a performance boost?
That's the part of the code that takes care of parallaxing. On every mousemove a each loop is executed which I think is the reason for the performance beeing so laggy:
var dragging = false;
var clickMouseX;
//Our object for the layers
//each layer has a different scrolling speed
var movingObjects = {
'#header-l1' : {'speed': 1},
'#header-l2' : {'speed': 1.4},
'#header-l3' : {'speed': 1.85},
'#header-l4' : {'speed': 2.2},
};
$('#header-wrapper').mousedown(function(e){
dragging = true;
//Get initial mouse position when clicked
clickMouseX = e.pageX;
$(this).mousemove(function(mme){
//execute only if mousedown
if(dragging){
//iterate through all layers which have to be parallaxed
$.each(movingObjects, function(el, opt){
var element = $(el);
//get difference of initial mouse position and current mouse position
var diff = clickMouseX - mme.pageX;
//scroll-position left speed 1
if(diff < 0) diff = -1;
//scroll position right speed 1
if(diff >= 0) diff = 1;
//get current position of layer
currLeft = parseInt(element.css('left'));
//get current layer width
elWidth = element.width();
//if right border is reached don't scroll further
if(currLeft < -(elWidth - 810)){
element.css('left', -(elWidth - 810));
}
//so do with left border
if(currLeft > 0){
element.css('left', 0);
}
//parallax it! Subtract the scroll position speed multiplied by the speed of the desired
//layer from the current left property
element.css('left', parseInt(element.css('left')) - diff*opt.speed);
});
}
});
/* Cursor */
$(this).css('cursor', 'pointer');
return false;
});
I also put a fiddle up:
http://jsfiddle.net/yWGDz/
Thanks in advance,
Thomas
P.S. maybe someone even finds out why layer two and three have the same scroll speed while having different speeds defined.
I worked at this a bit, and came up with this: http://jsfiddle.net/amqER/2/
This works a lot faster than the original (especially in firefox, where it performs a whole lot better, chrome it's still pretty slow). I also changed up some of the logic in your code, to make it make more sense.
A list of things that I did:
Minify your pngs
2 of your png files were over 2 megs, so I threw them into a png compressor (tinypng) and that reduced the size a lot. This helps with loading time and overall snappiness.
Re-use values as much as possible
In your original code, you wrote to and then subsequently read from the css left property a couple times in your code. Doing this is going to make it a lot slower. Instead, I kept an left property, and would only touch $.css when I absolutely needed to. Likewise for reading each element's width each update.
Also, like I said, I modified your logic to (I think) make more sense, given what you were trying to accomplish. It calculates a new diff each update, and tries to move according to that. Also, it doesn't try to keep moving once one of the images falls off (which yours does if you move all the way to the right, and it looks really weird). You can also look at this: http://jsfiddle.net/amqER/5/, which maybe is more like the control scheme you wanted.
Just some quick performance tips.
Try not to use $(this).mousemove instead save $(this) into a variable and use that.
var th = $(this);
th.mousemove...
Try to avoid using $.each. This is probably the part that's slowing your code down.
You can replace it with a for loop, but I would suggest, in this case, sending in each element one by one.
var parallax = function(img){
};
parallax(img1);
parallax(img2);
instantly-increase-your-jquery-performance
Whilst Xymostech's answer does greatly improve upon the original poster's original code; the performance is hardly improved for me in Chrome.
Whilst inspecting the page FPS, the solution posted here runs at 15FPS for me on a Retina MacBook Pro.
I made a very simple change to the code, altering it to use translate3d properties instead of left. Now, it runs at 55-60 FPS for me. I'd call that a massive performance boost.
If 'show paint rectangles' are turned on in Chrome, you'll see the previously posted solution is continually painting changes to the dom whilst the parallax is in motion. With the translate3d solution, there's simply zero painting done the whole time the parallax is in motion.
http://jsfiddle.net/LG47e/
Is there a way in javascript to bind an event handler to a horizontal scroll as opposed to the generic scroll event which is fired when the user scrolls horizontally and vertically? I want to trigger an event only when the user scrolls horizontally.
I searched around for an answer to this question, but couldn't seem to find anything.
Thanks!
P.S. My apologies if I'm using some terminology incorrectly. I'm fairly new to javascript.
UPDATE
Thanks so much for all your answers! In summary, it looks like you are all saying that this isn't supported in javascript, but I that I can accomplish the functionality with something like this (using jQuery) (jsFiddle):
var oldScrollTop = $(window).scrollTop();
$(window).bind('scroll', function () {
if (oldScrollTop == $(window).scrollTop())
//scrolled horizontally
else {
//scrolled vertically
oldScrollTop = $(window).scrollTop();
}
});
That's all I needed to know. Thanks again!
Answering from my phone, so unable to provide code at the moment.
What you'll need to do is subscribe to the scroll event. There isn't a specific one for vertical/horizontal.
Next, you'll need to get some measurements about the current display area. You'll need to measure the window.clientHeight and window.clientWidth.
Next, get window.top and window.left. This will tell you where position of the viewport is, ie if it's greater than 0 then scroll bars have been used.
It's pretty simple math from here to get what you need. If no one else has provided a code example in the next few hours I'll try to do so.
Edit:
A bit further information.
You must capture the scroll event. You also need to store the initial window.top and window.left properties somewhere. Whenever the scroll event happens, do a simple check to see if the current top/left values differ from the stores value.
At this point, if either are different you can trigger your own custom events to indicate vertical or horizontal scrolling. If you are using jQuery, this is very easy. If you are writing js without library assistance, it's easy too but a little more involved.
Do some searches for event dispatching in js.
You can then write any other code you want to subscribe to your custom events without needing to tie them together with method calls.
I wrote a jQuery plugin for you that lets you attach functions to the scrollh event.
See it in action at jsfiddle.net.
/* Enable "scrollh" event jQuery plugin */
(function ($) {
$.fn.enableHScroll = function() {
function handler(el) {
var lastPos = el
.on('scroll', function() {
var newPos = $(this).scrollLeft();
if (newPos !== lastPos) {
$(this).trigger('scrollh', newPos - lastPos);
lastPos = newPos;
}
})
.scrollLeft();
}
return this.each(function() {
var el = $(this);
if (!el.data('hScrollEnabled')) {
el.data('hScrollEnabled', true);
handler(el);
}
});
}
}(jQuery));
It's this easy to use:
$('#container')
.enableHScroll()
.on('scrollh', function(obj, offset) {
$('#info').val(offset);
});
Please note that scroll events come very fast. Even if you click in the scrollbar to jump to a new position, many scroll events are generated. You may want to adjust this code to wait a short time and accumulate all the changes in position during that time before firing the hscroll event.
You can use the same scroll event, but within your handler use the scrollLeft function to see if the scrollbar moved horizontally from the last time the event was fired. If the scrollbar did not move then just return from your handler. Otherwise update your variable to the new position and take action.
You can check if the the x value of the page changes and ignore your y value.
If the x value changes: There is your horizontal scroll.
With page-load, store the initial scrollbar positions for both in two variables (presumably both will be 0). Next, whenever a scroll event occurs, find the scrollleft and scrolltop properties. If the scrollleft property's value is different and scrolltop's value is same as compared to their earlier values, that's a horizontal scroll. Then set the values of the variables to the new scroll values.
No, there is no special event for scroll horizontal (it is for global scroll), but you can try to check the position of content by property .scrollLeft and if it's different from the previous value it means that the user scrolled content horizontally.
This is a followup question for this:
Scrollpane on the bottom, css is hacky, javascript is hard
I ended up doing the scrolling in the same way explained in the accepted answer.
Now there is a request that one item is selected somehow (eg. as an url parameter or by some javascript calls) I should scroll the pane to the item with the corresponding ID in the scrollpane. Like a link to an anchor () would work!
I want to make a javascript call like this
function scrollTo(id) {
$('#middle').magicallyScrollThatItemWouldBeVisible(itemid);
}
But this is not in jQuery (or at least I don't know of it). So is there a way to make it?
I'll post a simple jsFiddle here:
http://jsfiddle.net/ruisoftware/U6QdQ/4/
Help me write that scrollTo function!
A .animate would be fine too.
UPDATE: If it was not clear I would like it to only align to the left or right side of the panel, it it was overflowed on that side (so the minimum possible amount of scrolling happens)
It's not jQuery, just JavaScript, and I've actually never used it all, so I'm not sure how you would have to mess with it to get it to work in this situation, but there is a scrollIntoView function:
yourElement.scrollIntoView();
Since the elements have a fixed width, you can count the number of elements by using .index() + 1, and animate to this value (after subtracting the container's width).
If you want the element to be centered, use - Math.round(middle.width()/100)*50.
Fiddle: http://jsfiddle.net/U6QdQ/17/
//This code should be run on load / DOMReady
(function($){ //Run on load / DOMReady
$.fn.magicScrollTo = function(){
var middle = $("#middle");
var currentScrollLeft = middle.scrollLeft();
var width = middle.width();
var newScrollLeft = this.offset().left + currentScrollLeft - middle.offset().left;
if(newScrollLeft >= currentScrollLeft && newScrollLeft <= currentScrollLeft + width - this.outerWidth()) return;
if(newScrollLeft > currentScrollLeft){ //If the element is at the right side
newScrollLeft = newScrollLeft - width + this.outerWidth();
}
middle.animate({
scrollLeft: newScrollLeft,
}, 'fast')
}
})(jQuery);
Usage:
//Select the 4rd element, and scroll to it (eq is zero-based):
$('.item').eq(3).magicScrollTo();
Something along these lines would be a good start:
http://jsfiddle.net/vHjJ4/
This will bring the target into the centre of the carousel. I think you will have to add in some extra checks to make sure that it didn't scroll to far, for example if you targeted the first or last element...unless this is built into the scroll function (it might be).
I'm not sure I understand your question exactly, but it sounds like you're asking how to scroll horizontally to the selected item in the bottom pane. If so, try something like this:
//get the position of the element relative to the parent ("middle")
var pos = $("#itemid").position();
if (pos){
$("#middle").scrollLeft(pos.left);
}
From here, you can use the width of middle to center the item if needed.