Dont scroll the body when mouse is over child element - javascript

I'm trying to stop my main page from scrolling when the user hits the bottom of two specific sections of a page using the following code, but it's stopping the mouse wheel from working over those divs at all.
var scroller = document.querySelector('#Filters');
scroller.addEventListener('wheel', listener);
function listener(event)
{
var elem = event.currentTarget;
if ((event.deltaY < 0 && elem.scrollTop === 0) ||
(event.deltaY > 0 && elem.offsetHeight + elem.scrollTop >= elem.scrollHeight))
{
event.preventDefault();
}
else if ((event.deltaX < 0 && elem.scrollLeft === 0) ||
(event.deltaX > 0 && elem.offsetWidth + elem.scrollLeft >= elem.scrollWidth))
{
event.preventDefault();
}
}

You can use a more efficient solution than recalculating the scroll in js, disabling the body scrolling when scrolling the child element.
HTML:
<div onmouseover="disableScroll();" onmouseout="enableScroll();">
content
</div>
JS:
var body = document.getElementsByTagName('body')[0];
function enableScroll() {
body.style.overflowY = 'auto';
}
function disableScroll() {
body.style.overflowY = 'hidden';
}

Related

Javascript function is not loading on page load

I have an animation on my page and the animation only works when I scroll, but I want to run the animation on page load.
The function code is as following:
window.addEventListener("scroll", function () {
var scroll = window.pageYOffset || document.documentElement.scrollTop;
var elTop = el.getBoundingClientRect().top + scroll - document.documentElement.clientHeight;
var elBottom = el.getBoundingClientRect().bottom + scroll;
if ((el.getAttribute('data-effect') === 'false') && (scroll > elTop) && (scroll < elBottom)) {
el.setAttribute('data-effect', 'true');
if (!notVisible()) {
initVisualAnimation();
}
}
}
window.addEventListener('load', (event) => {
console.log('page is fully loaded');
});
You have added scroll event .. Call the code on page load
You can make a function, and call it on page load.
<script type="text/javascript">
function Animation_Scrolling(){
var scroll = window.pageYOffset || document.documentElement.scrollTop;
var elTop = el.getBoundingClientRect().top + scroll -document.documentElement.clientHeight;
var elBottom = el.getBoundingClientRect().bottom + scroll;
if ((el.getAttribute('data-effect') === 'false') && (scroll > elTop) && (scroll < elBottom)) {
el.setAttribute('data-effect', 'true');
if (!notVisible()) {
initVisualAnimation();
}
}
}
window.onload = Animation_Scrolling;
</script>

How to remove jank when setting an element to a fixed position using JavaScript

I have a webpage that when scrolled down, the text freezes when it reaches the last paragraph of text but the images keep on scrolling. I've got the implementation working but there is a lot of jank when scrolling with a mouse wheel, not so much if I click and drag the scroll bar.
Are there any optimizations I can make to this code to make work as intended or is there a different way to accomplish the same task?
window.addEventListener('scroll', function (e) {
window.requestAnimationFrame(keepTextStationary);
//keepTextStationary(); // Less janky, but still horrible
});
function keepTextStationary() {
var textRect = writtenContent.getBoundingClientRect();
var imageRec = images.getBoundingClientRect();
if (textRect.bottom < window.innerHeight && document.documentElement.scrollTop > 0) {
writtenContent.style.position = 'relative';
writtenContent.style.bottom = (225 - document.documentElement.scrollTop) + 'px';
if (imagesTop === undefined) {
imagesTop = imageRec.y;
}
} else {
writtenContent.style.bottom = (225 - document.documentElement.scrollTop) + 'px';
}
if (imageRec.y >= imagesTop) {
writtenContent.style.position = '';
}
}
Here is the site so you can see the problem.
https://bowerbankninow.azurewebsites.net/exhibitions/oscar-perry-the-pheasant
You are causing layout trashing every time you call getBoundingClientRect. Try debouncing your scroll events:
var lastScrollY = 0;
var ticking = false;
function keepTextStationary() {
var textRect = writtenContent.getBoundingClientRect();
var imageRec = images.getBoundingClientRect();
if (textRect.bottom < window.innerHeight && lastScrollY > 0) {
writtenContent.style.position = 'relative';
writtenContent.style.bottom = (225 - lastScrollY) + 'px';
if (imagesTop === undefined) {
imagesTop = imageRec.y;
}
} else {
writtenContent.style.bottom = (225 - lastScrollY) + 'px';
}
if (imageRec.y >= imagesTop) {
writtenContent.style.position = '';
}
ticking = false;
}
function onScroll() {
lastScrollY = document.documentElement.scrollTop;
requestTick();
}
function requestTick() {
if (!ticking) {
requestAnimationFrame(keepTextStationary);
ticking = true;
}
}
window.addEventListener('scroll', onScroll );
See this article for in-depth explanation: https://www.html5rocks.com/en/tutorials/speed/animations/
You dont.
Relocations / styling in javascript take place after the CSS has been loaded. Bad practise. What you can do, is make it animated to make it look less horrible.
Why is pure CSS not an option ?

IF scroll up - call function

I wanna call my_function1 when user scroll up (in the full-screen height page) or call my_function2 when the user scrolls down.
When I open a page with this code, it will always immediately run function in else {} (in this code example "slide_down();". What am I doing wrong, please?
var position = $(window).scrollTop();
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll > position) {
slide_up();
} else {
slide_down();
}
position = scroll;
});
Here is the code that detects if the user has scrolled up or down and is mobile friendly too.
console.log() is acting up in code snippets as the log is called for every pixel scrolled.
var lastScrollTop = 0;
// element should be replaced with the actual target element on which you have applied scroll, use window in case of no target element.
window.addEventListener("scroll", function(){
var st = window.pageYOffset || document.documentElement.scrollTop;
if (st > lastScrollTop){
// Do scroll down code
console.log("Down");
} else {
// Do scroll up code
console.log("Up");
}
lastScrollTop = st <= 0 ? 0 : st; // For Mobile or negative scrolling
}, false);
div {
height: 300px;
border: 1px solid;
}
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
This is working in my scenario (detecting mouse wheel in full-screen page)
// Detect IE version
var iev=0;
var ieold = (/MSIE (\d+\.\d+);/.test(navigator.userAgent));
var trident = !!navigator.userAgent.match(/Trident\/7.0/);
var rv=navigator.userAgent.indexOf("rv:11.0");
if (ieold) iev=new Number(RegExp.$1);
if (navigator.appVersion.indexOf("MSIE 10") != -1) iev=10;
if (trident&&rv!=-1) iev=11;
// Firefox or IE 11
if(typeof InstallTrigger !== 'undefined' || iev == 11) {
var lastScrollTop = 0;
$(window).on('scroll', function() {
st = $(this).scrollTop();
if(st < lastScrollTop) {
console.log('Up');
}
else if(st > lastScrollTop) {
console.log('Down');
}
lastScrollTop = st;
});
}
// Other browsers
else {
$('body').on('mousewheel', function(e){
if(e.originalEvent.wheelDelta > 0) {
console.log('Up');
}
else if(e.originalEvent.wheelDelta < 0) {
console.log('Down');
}
});
}

Add class to element if at top of page and user scrolls up

I'm trying to create a splash page similar to http://kinkinurbanthai.com/ but I need to re-activate the splash if the user is at the top of the page, and does an additional scroll up to 'request' or activate the splash again.
I have the code working up to the point of reactivating the splash element; but I can't get it to wait for the additional 'scroll up' request.
Here is the code so far that works, minus the locking b/c that isn't doing what I need it to atm. Code: http://jsfiddle.net/teejudp3/2/
// Does stuff on load and scrolls
//-------------------------------//
$(window).on("load scroll",function(e){
var $window = $(window);
if ( $window.scrollTop() <= 0 ) {
$('.splashwrapper').removeClass('remove');
}
// hide/show splash screen
//=========================//
// chrome/FF
$('.splashwrapper').bind('DOMMouseScroll', function(e){
// get scroll direction
var direction = (function () {
var delta = (e.type === 'DOMMouseScroll' ?
e.originalEvent.detail * -40 :
e.originalEvent.wheelDelta);
return delta > 0 ? 0 : 1;
}());
if( direction === 0 ) {
// scroll up
} else {
// scroll down
$('.splashwrapper').addClass('remove');
}
//prevent page fom scrolling
return false;
});
//IE, Opera, Safari
$('.splashwrapper').bind('mousewheel', function(e){
// get scroll direction
var direction = (function () {
var delta = (e.type === 'DOMMouseScroll' ?
e.originalEvent.detail * -40 :
e.originalEvent.wheelDelta);
return delta > 0 ? 0 : 1;
}());
if( direction === 0 ) {
// scroll up
} else {
// scroll down
$('.splashwrapper').addClass('remove');
}
//prevent page fom scrolling
return false;
});
// touch device - uses touchSwipe.js
$(".splashwrapper").swipe({
swipeUp:function() {
$('.splashwrapper').addClass('remove');
}
});
});
I've tried to stop the users scroll if they go to <= 1 scroll top position but this doesn't seem to give the wanted affect.
// temporarily lock users scroll position
var $window = $(window), previousScrollTop = 1, scrollLock = false;
$window.scroll(function(event) {
if(scrollLock) {
$window.scrollTop(previousScrollTop);
}
previousScrollTop = $window.scrollTop();
});
// if at 1px from top, stop scroll from going up one time
if ( $window.scrollTop() <= 1 ) {
scrollLock = true;
$window.scrollTop( 1, 0);
setTimeout(function() {
scrollLock = false;
}, 1000);
}
Got this working by re-working the stop-point section - aka I was overthinking it.
Updated fiddle: http://jsfiddle.net/teejudp3/6/
var $window = $(window);
if ( $window.scrollTop() === 0 && $('.splashwrapper').hasClass('ready') ) {
$window.scrollTop(1);
$('.splashwrapper').removeClass('ready').removeClass('remove');
}
if ( $window.scrollTop() === 0 && !$('.splashwrapper').hasClass('ready') ) {
$window.scrollTop(1);
setTimeout(function() {
$('.splashwrapper').addClass('ready');
}, 500);
}

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;
}
}
}

Categories

Resources