This function is working fine in Chrome but in I.E the css changes are slow, so is visible to the user when it shouldn't. I am thinking the problem could be with the scroll event or something but I can't solve it... Anyone can help me please?
var freezeLeft = settings.left > 0 || settings.leftClass !== null;
var freezeRight = settings.right > 0 || settings.rightClass !== null;
parent.scroll(function () {
var scrollWidth = parent[0].scrollWidth;
var clientWidth = parent[0].clientWidth;
var left = parent.scrollLeft;
if (freezeLeft) {
settings.leftColumns.css("left", left);
}
if (freezeRight) {
settings.rightColumns.css("right", scrollWidth - clientWidth - left);
}
}.bind(table));
}
After watching this two part tutorial on (here's part two) I've got parallax scrolling up and running. Where the clip starts he introduces cross browser compatibility using Paul Irish's requestAnimationFrame and that's what I can't get to work. He just pastes the code right into the code and it works but I can't get it to run in any other browser except Chrome. Although, when pasted something is happening to the images so I suppose it does something...
Any idea what I'm doing wrong? One suggestion was moving the requestAnimationFrame before the other code but that didn't change anything. I've set up a JSFiddle here so please help yourself. Any pointer is helpful.
Here's my code:
(function () {
var lastTime = 0;
var vendors = ['webkit', 'moz'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame =
window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function (callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function () { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function (id) {
clearTimeout(id);
};
}());
(function ($) {
var $container = $(".parallax");
var $divs = $container.find("div.parallax-background");
var thingBeingScroll = document.body;
var liHeight = $divs.eq(0).closest("li").height();
var diffHeight = $divs.eq(0).height() - liHeight;
var len = $divs.length;
var i, div, li, offset, scroll, top, transform;
var offsets = $divs.get().map(function (div, d) {
return $(div).offset();
});
var render = function () {
top = thingBeingScroll.scrollTop;
for (i = 0; i < len; i++) {
div = $divs[i];
offset = top - offsets[i].top;
scroll = ~~((offset / liHeight * diffHeight) * 3);
transform = 'translate3d(0px,' + scroll + 'px,0px)';
div.style.webkitTransform = transform;
div.style.MozTransform = transform;
div.style.msTransform = transform;
div.style.OTransform = transform;
div.style.transform = transform;
}
};
(function loop() {
requestAnimationFrame(loop);
render();
})();
})(jQuery);
Well apart from jQuery not getting loaded into jsFiddle properly, I think your problem was with scrollTop support. Try this updated fiddle which uses the jquery shim for scrollTop and the window property intead;
var $thingBeingScroll = $(window);
and
top = $thingBeingScroll.scrollTop();
But now it looks like you have the same problem I'm currently having. Namely, the scroll is jumpy on IE and FF compared to Chrome.
It's as if the smooth scrolling on FF and IE (which chrome doesn't have) is somehow moving the background slab on scroll before we get a chance to update it. It also issues an array of scroll changes which means that after you let go of the scroll bar, it then has to redraw the positions starting back at the begining and working its way back to current position. I believe that's what causes the jerkiness.
I believe requestAnimationFrame will stack up requests, so it may be that we need to cancel any previous outstanding ones if we've a more recent one and/or use higher resolution updates like mousemove.
Here's my JS code..
<script>
var sticky = document.querySelector('.sticky');
var origOffsetY = sticky.offsetTop;
function onScroll(e) {
window.scrollY >= origOffsetY ? sticky.classList.add('fixed') :
sticky.classList.remove('fixed');
}
document.addEventListener('scroll', onScroll);
</script>
It's used to let a div stays in place even when the user scrolls down.
It doesn't work in IE10 (which has querySelector, classList, and addEventListener, so it's not that).
IE10 doesn't support scrollY. You have to use scrollTop on document.documentElement:
var sticky = document.querySelector('.sticky');
var origOffsetY = sticky.offsetTop;
var hasScrollY = 'scrollY' in window;
function onScroll(e) {
var y = hasScrollY ? window.scrollY : document.documentElement.scrollTop;
y >= origOffsetY ? sticky.classList.add('fixed') : sticky.classList.remove('fixed');
}
document.addEventListener('scroll', onScroll);
Live Example | Live Source
(You may not need the check, it's possible all of your target browsers support document.documentElement.scrollTop and you could just always use 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;
}
}
}
I have code to get x-y coordinates when the browser scrolls:
left1 = window.event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
top1 = window.event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
This is working in IE7, but not in Mozilla Firefox 3.5.19. How can I get it working in Firefox?
The following JS works in IE 8 and firefox 3.6.17
function getScrollingPosition()
{
var position = [0, 0];
if (typeof window.pageYOffset != 'undefined')
{
position = [
window.pageXOffset,
window.pageYOffset
];
}
else if (typeof document.documentElement.scrollTop
!= 'undefined' && document.documentElement.scrollTop > 0)
{
position = [
document.documentElement.scrollLeft,
document.documentElement.scrollTop
];
}
else if (typeof document.body.scrollTop != 'undefined')
{
position = [
document.body.scrollLeft,
document.body.scrollTop
];
}
return position;
}
This article also may help.
http://www.softcomplex.com/docs/get_window_size_and_scrollbar_position.html
Remember that the click event is handled differently in Mozilla than it is in internet explorer. Also they hold different ways of attaining the position of the cursor location. This is a very easy google search to get the specifics on either.
var IE = document.all ? true : false; // check to see if you're using IE
if (IE) //do if internet explorer
{
cursorX = event.clientX + document.body.scrollLeft;
cursorY = event.clientY + document.body.scrollTop;
}
else //do for all other browsers
{
cursorX = (window.Event) ? e.pageX : event.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
cursorY = (window.Event) ? e.pageY : event.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
}
Also note that you should have something like the following in your initialization code:
if (window.Event) {
if (window.captureEvents) { //doesn't run if IE
document.captureEvents(Event.MOUSEMOVE);
}
}
document.onmousemove = refreshCursorXY;
On the click side of things as I said there is differences in what value the click is attributed between browsers. For example, this check should happen in your click event (that you send e, the event, to). e will not be sent by I.E. so we do it like so:
//internet explorer doesn't pass object event, so check it...
if (e == null)
{
e = window.event;
}
//1 = internet explorer || 0 = firefox
if ((e.button == 1 && window.event != null || e.button == 0))
And again, there are many differences between IE and other browsers, thus there is much documentation. Google searches can do wonders if you require further information on the subject.
Only IE puts the event information in the global window object. All other browsers follow the W3C recommendation that event information be passed to the listener callback registered on an element. You will also need to use element.addEventListener() instead of element.attachEvent(). See the addEventListener() documentation on the Mozilla Developer Network.