Consistently tracking scroll with jQuery - javascript

Using the following piece of code:
(function($){
$(window).on('scroll', function(){
var scroll_top = $(document).scrollTop();
if (scroll_top > 50 && scroll_top < 100) {
console.log(scroll_top);
}
});
})(jQuery)
Viewing the console shows me a range of numbers between 50 & 100 whilst I scroll up and down, as expected.
However, I need a way to log each number between 50 & 100 as I've scrolled. So that if I stop at 55 it would log 51, 52, 53, 54, 55.
I understand that this is because browsers run javascript and render the page at different speeds but is it at all possible?

var scroll_pos_at_mousedown;
var aa = function(){
$(window).on('scroll mouseup mousedown', function(e){
var scroll_top = $(document).scrollTop();
if(e.type == 'mousedown') {
var scroll_top1 = $(document).scrollTop();
if(scroll_top1 <= 50) {
scroll_pos_at_mousedown = 50;
} else if(scroll_top1 > 50 || scroll_top1 <= 500) {
scroll_pos_at_mousedown = scroll_top1;
} else {
scroll_pos_at_mousedown = 500;
}
console.log("scroll_top is "+scroll_top1);
}
console.log("current_scroll_top is " + scroll_pos_at_mousedown);
if (scroll_top > 50 && scroll_top < 500) {
if(e.type == "mouseup") {
var scroll_pos_at_mouseup = $(document).scrollTop();
console.log('scroll_pos_at_mouseup is '+scroll_pos_at_mouseup);
if(scroll_pos_at_mousedown<=500) {
for(i=scroll_pos_at_mousedown;i<=scroll_pos_at_mouseup;i++)
console.log(i);
}else {
for(i=scroll_pos_at_mouseup;i<=500;i++)
console.log(i);
}
}
}
});
};aa();
I have written this function keeping event mousedown and mouseup in mind. You can add keyup and keydown event as well and add it for up and down arrow keyCode. Hope this helps :)

Related

Javascript animate on scroll position

I am trying to get a scrolling animation like here (notice the circle figure fading in when you scroll down):
http://demo.atticthemes.com/skoty/
This is what I have sofar, but it keeps hanging somehow:
http://jsfiddle.net/v4zjgwL6/
var timer;
var triggerHeight = $("#bar").offset().top;
var headerAvatar = $(".header-avatar-wrapper");
$(window).scroll(function() {
if(timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(function() {
var y = $(window).scrollTop();
if(y > triggerHeight - 220) {
headerAvatar.css("visibility", "visible");
headerAvatar.animate({opacity: 1}, 200);
} else {
headerAvatar.animate({opacity: 0}, 200);
headerAvatar.css("visibility", "hidden");
}
}, 10);
});
You don't need to use a timer, the way you have implemented it causes performance drops.
I would suggest to use css classes instead:
var triggerHeight = $("#bar").offset().top;
var headerAvatar = $(".header-avatar-wrapper");
$(window).scroll(function() {
var y = $(window).scrollTop();
if (y > triggerHeight - 220 && !headerAvatar.hasClass("visible")) {
headerAvatar.addClass("visible");
} else if(y <= triggerHeight - 220 && headerAvatar.hasClass("visible")) {
headerAvatar.removeClass("visible");
}
});
I have also added this class in CSS:
.header-avatar-wrapper.visible{
opacity: 1;
visibility: visible;
}
JSFiddle demo
Or alternatively, use jQuery's .fadeIn() and fadeOut() functions:
var triggerHeight = $("#bar").offset().top;
var headerAvatar = $(".header-avatar-wrapper");
$(window).scroll(function() {
var y = $(window).scrollTop();
if (y > triggerHeight - 220 && headerAvatar.css("display") == "none") {
headerAvatar.fadeIn();
} else if(y <= triggerHeight - 220 && headerAvatar.css("display") == "block") {
headerAvatar.fadeOut();
}
});
In CSS I removed the opacity and visibility properties from .header-avatar-wrapper and added display: none; instead.
JSFiddle demo
Looks like you're only handling the cases where you need to change state (shown or hide the element) and not the cases where nothing should change. This causes you to continuously re-show (re-animate) the thing, which makes it flicker.
It's early and I have not yet had coffee, but something like this should fix you up. :)
var timer;
var triggerHeight = $("#bar").offset().top;
var headerAvatar = $(".header-avatar-wrapper");
var shown; // NEW
$(window).scroll(function() {
if(timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(function() {
var y = $(window).scrollTop();
var shouldShow = y > triggerHeight - 220; // CHANGED
if(!shown && shouldShow) { // CHANGED
shown = true; // NEW
headerAvatar.css("visibility", "visible");
headerAvatar.animate({opacity: 1}, 200);
} else if (shown && !shouldShow) { // CHANGED
shown = false; // NEW
headerAvatar.animate({opacity: 0}, 200);
headerAvatar.css("visibility", "hidden");
}
}, 10); });
Proof: http://jsfiddle.net/bvaughn/oL85oj41/

Trying to turn off requestAnimation function when resizing browser

I have the following code and I am trying to turn off the function when window resize is run, currently it just keeps running on window.resize.
function headerParallax(x) {
if (x ==="true"){
$(window).scroll(function() {
// Store scrollTop in variable
var scrollPos = $(window).scrollTop();
// var viewportHeight = $(window).height();
console.log(scrollPos + 'bgbottle1');
var bouncePos = ((-1 * (scrollPos - 75) * 1.5) + scrollPos).toFixed(2);
var bouncePos1 = ((-1 * (scrollPos - 150) * 1.25) + scrollPos).toFixed(2);
$(".bottle1").css({ 'background-position': "right " + bouncePos + 'px'});
if (scrollPos > 150){
$(".bottle2").css({ 'background-position': "left " + bouncePos1 + 'px'});
}
});
}else if(x === "false"){
alert("no");
}
}
$(window).resize(function(){
if ($(window).width() < 1200){
window.requestAnimationFrame(headerParallax("false"));
}
});
if ($(window).width() > 1200){
window.requestAnimationFrame(headerParallax("true"));
}
You can try something like this:
var _preflag = -1;
var _unbindScroll = function(){};
// it will be fired only once (when flag is changed)
function headerParallax(flag){
if (flag === _preflag){
return;
}
_preflag = flag;
if (flag){
// TODO adjust the UI for true
window.requestAnimationFrame(theCalculatedValue);
_unbindScroll(); // It's duplicate work to unbind scroll here, but there's no harm calling it :)
$(window).on('scroll', _onscroll);
// update the ubind scroll so that anyone can ubind it safely
_unbindScroll = function(){
$(window).off('scroll', _onscroll);
_unbindScroll = function(){};
};
} else {
// TODO adjust the UI for false
window.requestAnimationFrame(theCalculatedValue);
_unbindScroll(); // unbind scrolling, this is what you want, right?
}
function _onscroll(){
// TODO
}
}
function resize(){
// this will be fired multipe times, need to check it in sub functions to take it once
headerParallax($(window).width() < 1200);
}
$(window).resize(resize);
resize();

Force "overscrolling" at Chrome/Mac with javascript

When you get to the limit of document, you can keep scrolling and can see an background behing the document before it bounces back (overscrolling).
How can I force the window to overscroll like this with javascript?
This is not the ultimate solution since I think the animation is imperfect and it's really only for desktops, but it can at least get you started. What I have done is increase the height of the body for animation on scroll.
$(document).on('scroll mousewheel', function (e) {
//Check for mousewheel scrolling down (or not used at all)
if (!e.originalEvent || !e.originalEvent.wheelDeltaY
|| e.originalEvent.wheelDeltaY < 0) {
if ($(window).height() + $(this).scrollTop() == $(this).height()) {
//Prevent simultaneous triggering of the animation
if (!$("body").data('bouncing')) {
$("body").height(function (_, h) { return h + 15; })
.data('bouncing', true);
$("body, html").animate({
'scrollTop': '+=15'
}, 125).animate({
'scrollTop': '-=15'
}, {duration: 125, complete: function () {
$(this).height(function (_, h) { return h - 15; })
.data('bouncing', false);
}});
}
}
}
}).on('keydown', function (e) {
//The "down" arrow; still bounces when pressed at the bottom of the page
if (e.which == '40') {
$(this).trigger('scroll');
}
});
I've been playing with this version that imitates the effect using a div, that slides in and out of view at the bottom of the page. If you have a high res monitor, you may need to increase the height of the main div to test it.
<div id="main" style="background:#f5f5f5;height:1000px"></div>
<div id="overscroll" style="background:#666666;height:120px"></div>
<script type="text/javascript">
var $doc = $(document);
$doc.ready(function () {
var $wnd = $(window),
$oscroll = $('#overscroll'),
block = false;
$wnd.bind('scroll', function () {
if (!block) {
block = true;
var scrollTop = $wnd.scrollTop(),
wndHeight = $wnd.height(),
docHeight = $doc.height();
try {
if (scrollTop + (wndHeight + 120) > docHeight) {
$oscroll.slideUp('slow');
}
else if ($oscroll.css('display') === 'none'
&& (scrollTop + (wndHeight + 120) < docHeight)) {
$oscroll.slideDown();
}
} finally {
block = false;
}
}
});
});
</script>

Scrolling child div scrolls the window, how do I stop that?

I have a div, with a scroll bar, When it reaches the end, my page starts scrolling. Is there anyway I can stop this behavior ?
You can inactivate the scrolling of the whole page by doing something like this:
<div onmouseover="document.body.style.overflow='hidden';" onmouseout="document.body.style.overflow='auto';"></div>
Found the solution.
http://jsbin.com/itajok
This is what I needed.
And this is the code.
http://jsbin.com/itajok/edit#javascript,html
Uses a jQuery Plug-in.
Update due to deprecation notice
From jquery-mousewheel:
The old behavior of adding three arguments (delta, deltaX, and deltaY)
to the event handler is now deprecated and will be removed in later
releases.
Then, event.deltaY must now be used:
var toolbox = $('#toolbox'),
height = toolbox.height(),
scrollHeight = toolbox.get(0).scrollHeight;
toolbox.off("mousewheel").on("mousewheel", function (event) {
var blockScrolling = this.scrollTop === scrollHeight - height && event.deltaY < 0 || this.scrollTop === 0 && event.deltaY > 0;
return !blockScrolling;
});
Demo
The selected solution is a work of art. Thought it was worthy of a plugin....
$.fn.scrollGuard = function() {
return this
.on( 'wheel', function ( e ) {
var event = e.originalEvent;
var d = event.wheelDelta || -event.detail;
this.scrollTop += ( d < 0 ? 1 : -1 ) * 30;
e.preventDefault();
});
};
This has been an ongoing inconvenience for me and this solution is so clean compared to other hacks I've seen. Curious to know how more about how it works and how widely supported it would be, but cheers to Jeevan and whoever originally came up with this. BTW - stackoverflow answer editor needs this!
UPDATE
I believe this is better in that it doesn't try to manipulate the DOM at all, only prevents bubbling conditionally...
$.fn.scrollGuard2 = function() {
return this
.on( 'wheel', function ( e ) {
var $this = $(this);
if (e.originalEvent.deltaY < 0) {
/* scrolling up */
return ($this.scrollTop() > 0);
} else {
/* scrolling down */
return ($this.scrollTop() + $this.innerHeight() < $this[0].scrollHeight);
}
})
;
};
Works great in chrome and much simpler than other solutions... let me know how it fares elsewhere...
FIDDLE
You could use a mouseover event on the div to disable the body scrollbar and then a mouseout event to activate it again?
E.g. The HTML
<div onmouseover="disableBodyScroll();" onmouseout="enableBodyScroll();">
content
</div>
And then the javascript like so:
var body = document.getElementsByTagName('body')[0];
function disableBodyScroll() {
body.style.overflowY = 'hidden';
}
function enableBodyScroll() {
body.style.overflowY = 'auto';
}
As answered here, most modern browsers now support the overscroll-behavior: none; CSS property, that prevents scroll chaining. And that's it, just one line!
Here's a cross-browser way to do this on the Y axis, it works on desktop and mobile. Tested on OSX and iOS.
var scrollArea = this.querySelector(".scroll-area");
scrollArea.addEventListener("wheel", function() {
var scrollTop = this.scrollTop;
var maxScroll = this.scrollHeight - this.offsetHeight;
var deltaY = event.deltaY;
if ( (scrollTop >= maxScroll && deltaY > 0) || (scrollTop === 0 && deltaY < 0) ) {
event.preventDefault();
}
}, {passive:false});
scrollArea.addEventListener("touchstart", function(event) {
this.previousClientY = event.touches[0].clientY;
}, {passive:false});
scrollArea.addEventListener("touchmove", function(event) {
var scrollTop = this.scrollTop;
var maxScroll = this.scrollHeight - this.offsetHeight;
var currentClientY = event.touches[0].clientY;
var deltaY = this.previousClientY - currentClientY;
if ( (scrollTop >= maxScroll && deltaY > 0) || (scrollTop === 0 && deltaY < 0) ) {
event.preventDefault();
}
this.previousClientY = currentClientY;
}, {passive:false});
I wrote resolving for this issue
var div;
div = document.getElementsByClassName('selector')[0];
div.addEventListener('mousewheel', function(e) {
if (div.clientHeight + div.scrollTop + e.deltaY >= div.scrollHeight) {
e.preventDefault();
div.scrollTop = div.scrollHeight;
} else if (div.scrollTop + e.deltaY <= 0) {
e.preventDefault();
div.scrollTop = 0;
}
}, false);
If I understand your question correctly, then you want to prevent scrolling of the main content when the mouse is over a div (let's say a sidebar). For that, the sidebar may not be a child of the scrolling container of the main content (which was the browser window), to prevent the scroll event from bubbling up to its parent.
This possibly requires some markup changes in the following manner:
<div id="wrapper">
<div id="content">
</div>
</div>
<div id="sidebar">
</div>
See it's working in this sample fiddle and compare that with this sample fiddle which has a slightly different mouse leave behavior of the sidebar.
See also scroll only one particular div with browser's main scrollbar.
this disables the scrolling on the window if you enter the selector element.
works like charms.
elements = $(".selector");
elements.on('mouseenter', function() {
window.currentScrollTop = $(window).scrollTop();
window.currentScrollLeft = $(window).scrollTop();
$(window).on("scroll.prevent", function() {
$(window).scrollTop(window.currentScrollTop);
$(window).scrollLeft(window.currentScrollLeft);
});
});
elements.on('mouseleave', function() {
$(window).off("scroll.prevent");
});
You can inactivate the scrolling of the whole page by doing something like this but display the scrollbar!
<div onmouseover="document.body.style.overflow='hidden'; document.body.style.position='fixed';" onmouseout="document.body.style.overflow='auto'; document.body.style.position='relative';"></div>
$this.find('.scrollingDiv').on('mousewheel DOMMouseScroll', function (e) {
var delta = -e.originalEvent.wheelDelta || e.originalEvent.detail;
var scrollTop = this.scrollTop;
if((delta < 0 && scrollTop === 0) || (delta > 0 && this.scrollHeight - this.clientHeight - scrollTop === 0)) {
e.preventDefault();
}
});
Based on ceed's answer, here is a version that allows nesting scroll guarded elements. Only the element the mouse is over will scroll, and it scrolls quite smoothly. This version is also re-entrant. It can be used multiple times on the same element and will correctly remove and reinstall the handlers.
jQuery.fn.scrollGuard = function() {
this
.addClass('scroll-guarding')
.off('.scrollGuard').on('mouseenter.scrollGuard', function() {
var $g = $(this).parent().closest('.scroll-guarding');
$g = $g.length ? $g : $(window);
$g[0].myCst = $g.scrollTop();
$g[0].myCsl = $g.scrollLeft();
$g.off("scroll.prevent").on("scroll.prevent", function() {
$g.scrollTop($g[0].myCst);
$g.scrollLeft($g[0].myCsl);
});
})
.on('mouseleave.scrollGuard', function() {
var $g = $(this).parent().closest('.scroll-guarding');
$g = $g.length ? $g : $(window);
$g.off("scroll.prevent");
});
};
One easy way to use is to add a class, such as scroll-guard, to all the elements in the page that you allow scrolling on. Then use $('.scroll-guard').scrollGuard() to guard them.
If you apply an overflow: hidden style it should go away
edit: actually I read your question wrong, that will only hide the scroll bar but I don't think that's what you are looking for.
I couldn't get any of the answers to work in Chrome and Firefox, so I came up with this amalgamation:
$someElement.on('mousewheel DOMMouseScroll', scrollProtection);
function scrollProtection(event) {
var $this = $(this);
event = event.originalEvent;
var direction = (event.wheelDelta * -1) || (event.detail);
if (direction < 0) {
if ($this.scrollTop() <= 0) {
return false;
}
} else {
if ($this.scrollTop() + $this.innerHeight() >= $this[0].scrollHeight) {
return false;
}
}
}

JQuery with css3 keydown keyCode = 37 and 39

I have tested both ways. jquery animation and css3 transition, and css3 is a little bit faster. But i have a problem with the following code:
$(document).keydown(function(e){
if (e.keyCode == 39) {
var DocHeight = $(document).height();
$('.container').css("margin-top","-="+DocHeight)
}
});
if i hit twice on keyCode 39 (arrow to the right) than my transition is outer space. Does anyone has an solution for this thing?
outer space
maybe not the correct word. But the problem is. if i hit twice the arrow key i'll get the last request, in other words... animation is started, and another animation start from the position that i don't want.
example: hit #1 margin-top is at 0px and goes to 1024px. but when i hit it twice the margin-top is at 23px, and it stops at 1047px.
This is not what i want. It has to stop at 1024px.
I hope so.
Try this out:
$(document).keydown(function(e) {
var DocHeight = $(document).height();
if (DocHeight > 1024) {
$('.container').css("margin-top", "1024px")
} else {
if (e.keyCode == 39) {
$('.container').css("margin-top", "-=" + DocHeight)
}
}
});
This code just checks if DocHeight is above 1024 or not.
Find an Demo Here: http://jsfiddle.net/shawn31313/fRYwM/
I use $('.container').css("margin-top", "+=" + DocHeight) for the example but using it with the - will work too.
Edit:
(I know you don't need it):
I edited the code so it works twice as good:
$(document).ready(function() {
check();
});
$(document).keydown(function(e) {
var DocHeight = $(document).height();
if (DocHeight > 1024) {
$('.container').css("margin-top", "1024px")
} else {
if (e.keyCode == 39) {
if (DocHeight > 1024) {
$('.container').css("margin-top", "1024px")
}
$('.container').css("margin-top", "+=" + DocHeight)
}
}
});
$(document).keyup(function(e) {
var DocHeight = $(document).height();
if (DocHeight > 1024) {
$('.container').css("margin-top", "1024px")
}
});
function check() {
if (DocHeight > 1024) {
$('.container').css("margin-top", "1024px")
}
check();
}
The demo for this is: http://jsfiddle.net/shawn31313/fRYwM/1/
Try
var mTop = 0;
$(document).keydown(function(e){
if (e.keyCode == 39) {
var DocHeight = $(document).height();
mTop = mTop - parseInt(DocHeight);
$('.container').css("margin-top", mTop);
}
});
This just keeps on going, if you just want it to animate once and stop, try something like:
var mTop = 0;
$(document).keydown(function(e){
if (e.keyCode == 39) {
var WinHeight = $(window).height();
mTop = parseInt(WinHeight);
$('.container').css("margin-top", -mTop);
}
});
Using document height is a bad idea when moving stuff with margins, also just adding the document height to the css each time with "-=" will cause problems when you hit the button before the animation is finished and it adds to a value that is somewhere inbetween, you should do the math in a variabe instead, and use that variable for setting the css for consistency.
If you use jquery animate then you can use .is(":animated") to only start a new animation if one isn't already in progress.
var $container = $('.container');
if (e.keyCode == 39 && !$container.is(":animated")) {
var DocHeight = $(document).height();
$container.animate(...)
}
This will only start the animation if one is not already in progress.

Categories

Resources