Okay i have a full page scroll project , everything works fine and beautiful on firefox except this Chrome..I have an element which translate on wheel event, so there is no actual scrolling.The element's CSS is
#trasnlateMe {
height: calc(var(--vh) * 400);
width: 100%;
overflow: hidden;
position: relative;
transition: transform 1s cubic-bezier(0.5, 0, 0.5, 1);
}
and it has inline styling of
style="transform:translateY(0);"
on my js at beginning of code I have this code also.
let container = document.querySelector("#trasnlateMe");
container.style.transform = "translateY(0)";
basically when you scroll i get the direction through a flag and translate the page by this function.
const fullPageScroll = () => {
containerHight = window.innerHeight * foldQuantity;
yTrasnlationOfContainer = parseInt(
container.style.transform.replace("translateY(", "")
);
if (
(scrollDirection === "down" &&
Math.abs(yTrasnlationOfContainer) <
containerHight - containerHight / foldQuantity) ||
(swipeDirection === "up" &&
Math.abs(yTrasnlationOfContainer) <
containerHight - containerHight / foldQuantity)
) {
yTrasnlationOfContainer = yTrasnlationOfContainer - window.innerHeight;
}
if (
(scrollDirection === "up" && yTrasnlationOfContainer < 0) ||
(swipeDirection === "down" && yTrasnlationOfContainer < 0)
) {
yTrasnlationOfContainer = yTrasnlationOfContainer + window.innerHeight;
}
if (holder === false && hambergurMenu.style.display !== "none") {
holder = true;
swipeDirection = null;
container.style.transform = `translateY(${yTrasnlationOfContainer}px)`;
for (let i = 1; i <= foldQuantity; i++) {
if (yTrasnlationOfContainer === -(window.innerHeight * (i - 1))) {
currentSection = i - 1;
}
fillRelatedCircle();
setTimeout(() => {
holder = false;
}, 1000);
}
}
};
//Handling wheel event of the whole page
container.addEventListener("wheel", event => {
if (event.deltaY > 0) {
scrollDirection = "down";
}
if (event.deltaY < 0) {
scrollDirection = "up";
}
event.stopPropagation();
fullPageScroll();
});
so everything works lovingly on firefox but chrome decided not to set the ytrasnlation of #trasnlateMe to 0, the abnormal thing is that it does on paper, when you look at the code after refresh it looks like this >> style="transform:translateY(0); but it stays at the same section which was before the refreshing on the viewport and the whole scrolling GETS BROKEN, IT COUNTS THE SECTION WHICH WAS BEFORE REFRESHING ON the VIEWPORT AS THE FIRST SECTION.
Please help this is so annoying.
Just NOTE: when I close the tab and open the html file again everything works beautifully on Chrome, even if you don't have an answer for this bug would you tell me what can I do to mimic that same behavior on reloading? That everything be like the page is opened right now.That would fix it too.
I had similar problem, when replaceing srolling with translate on wheel event.
What helped me, was giving html and body:
overflow: hidden;
height: 100%;
Okay I just gave body
position: fixed;
top: 0;
left: 0;
right: 0;
and everything got fixed on Chrome, after coding for some time now I can really say Firefox is the best browser and chrome is the worst!
Related
I am working on a university project which requires me to design a website including dynamic Javascript content. This unit is exclusively about Javascript; no Jquery or anything else is allowed until next unit.
What I am trying to accomplish is for the images in the gallery to scroll in when they are scrolled into the viewport. If any part of the image is visible, the script should begin increasing the opacity proportionally to how much of the image has been scrolled in. I've tried a few different things from different tutorial and answers in the stack, but nothing works. The code might work, but it doesn't activate on scroll. Here's the code if anyone can help:
var elementPosition = window.pageYOffset;
function isInViewport(img) {
var relct = img.getBoundingClientRect();
return rect.bottom > 0 &&
rect.right > 0 &&
rect.left < (window.innerWidth || document.documentElement.clientWidth) &&
rect.top < (window.innerHeight || document.documentElement.clientHeight);
}
function fadeIn() {
var imgList = document.getElementsByTagName("IMG");
var i;
for (i = 0; i < imgList.length; i++) {
var img = imgList[i];
if (isInViewport(img)) {
if (elementPosition < 200) {
opacity = 1 - (elementPosition / 200));
}
else {
opacity = 1;
}
}
else {
opacity = 0;
}
}
}
window.addEventListener('scroll', fadeIn());
Lots of typos were fouling your code -- I'd recommend using an application that will either tidy your code and/or flag errors as you write (personally, I use codepen.io). Also, opacity is set by: element.style.opacity.
var imgList = document.getElementsByTagName("IMG");
function isInViewport(img) {
var rect = img.getBoundingClientRect();
return (
rect.bottom > 0 &&
rect.right > 0 &&
rect.left < (window.innerWidth || document.documentElement.clientWidth) &&
rect.top < (window.innerHeight || document.documentElement.clientHeight)
);
}
function fadeIn() {
for (i = 0; i < imgList.length; i++) {
if (isInViewport(imgList[i])) {
imgList[i].style.opacity = 1;
}
}
}
window.addEventListener("scroll", fadeIn);
img {display: block; transition: all 1s; opacity: 0;}
<img src="http://www.fillmurray.com/300/600">
<img src="http://www.fillmurray.com/300/600">
<img src="http://www.fillmurray.com/300/600">
<img src="http://www.fillmurray.com/300/600">
I'm trying to use this Smart Scroll https://bootstrap-menu.com/detail-smart-hide.html on bootstrap it's working perfectly for desktop but on mobile, the navbar is glitchy when scrolling down, then going back to the top. Once back to the top, the navbar hides again. I've tried the suggestion from Bootstrap 4 Smart Scroll but it didn't work.
js
if ($('.smart-scroll').length > 0) { // check if element exists
var last_scroll_top = 0;
$(window).scroll(function() {
var scroll_top = $(window).scrollTop();
if(scroll_top > 1) { $(".smart-scroll").addClass("scrolled-up");} else {$(".smart-scroll").removeClass("scrolled-up");}
if(scroll_top < last_scroll_top) {
$('.smart-scroll').removeClass('scrolled-down').addClass('scrolled-up');
}
else {
$('.smart-scroll').removeClass('scrolled-up').addClass('scrolled-down');
}
last_scroll_top = scroll_top;
});
}
css
.smart-scroll{
position: fixed !important;
right: 0;
left: 0;
z-index: 1030!important;
transition: all 0.3s ease-in-out;
}
.scrolled-down{
transform:translateY(-100%) !important;
}
.scrolled-up{
transform:translateY(0) !important;
}
My template navbar is using custom JS to make nav bar fixed to the top instead of using fixed-top.
function loadWindowEvents() {
$(window)
.on("scroll", function() {
loadSkills();
});
$(window)
.on('scroll', function() {
if ($(window)
.scrollTop() >= 100) {
$('.menu-wrap')
.addClass('fixed');
} else {
$('.menu-wrap')
.removeClass('fixed');
}
});
}
Testing the original code this glitch is in the code itself, can anyone help resolve this and test it on a iPhone safari?
I've found the following JS someone posted where they have it working perfectly if someone can help me apply to my code.
window.location.hash.startsWith("#nav") && (window.location.hash = "", a());
let c = 0;
window.addEventListener("scroll", () => {
const t = Math.abs(parseInt(document.documentElement.getBoundingClientRect().top));
if (t <= n + 1 || t <= i.getBoundingClientRect().top + 1) return e.classList.remove("is-scrolled"), e.classList.remove("is-pinned"), void e.classList.remove("is-transition-enabled");
t > n + 2 * e.offsetHeight && (e.classList.add("is-scrolled"), e.classList.contains("is-transition-enabled") || g(600).then(() => e.classList.add("is-transition-enabled"))), Math.abs(t - c) < 5 || (t < c ? e.classList.add("is-pinned") : e.classList.remove("is-pinned"), c = t)
}), t.addEventListener("click", async t => {
t.preventDefault(), e.classList.contains("is-open") ? h() : a()
});
The answer from bootstrap 4 smartscroll was correct.
just a type error in the last line of the .js
lastScrollTop = st; should be -> lastScrollTop = scroll_top;
Can you recommend a JS library that actually provides edge swipe functionality when working with bare-bones HTML & CSS?
I've searched all over and haven't found a source of truth for that problem.
I've seen lots and lots of libraries enabling swipe gestures but not edge swipe.
My last attempt was using Hammer.js which I've tried implementing as:
var swipe = new Hammer(document);
// detect swipe and call to a function
swipe.on('swiperight swipeleft', function (e) {
e.preventDefault();
var endPoint = e.pointers[0].pageX;
var distance = e.distance;
var origin = endPoint - distance;
//swipe right to open nav
if (origin <= 15 && e.type == 'swiperight') {
// open main menu
$('#navigation-menu').animate({
left: '0'
});
} else {
// close/hide menu(s)
$('#navigation-menu').animate({
left: '-100%'
});
}
});
Further, if not using any library, how can I implement a mobile edge swipe to show and hide content, (in my case it'd be a navigation menu) with vanilla JS?
At this point I'm open to either solution/direction.
Here is a solution, you can set thresholdStart, End, Milliseconds. You may want to tidy up the code, and port it for touch events (I used mouse events for testing in my browser more easily).
Use:
swipeEdgeFromLeft function and swipeEdgeFromRight function.
var div = document.body;
var mouse = {
isDown: false,
inLeft: false,
inRight: false,
downTimestamp: null
};
var width, thresholdStart, thresholdEnd, thresholdMilliseconds;
function resize(){
width = window.innerWidth;
thresholdStart = 0.1*width;//within 10% of screen width
thresholdEnd = 0.13*width;//beyond 13% of screen width
thresholdMilliseconds = 500;//must be done in 500 milliseconds
}
document.addEventListener("resize", resize, false);
resize();//initialize
div.addEventListener('mousedown'/*'touchstart'*/, function(e){
var x = e./*touches[0].*/pageX;
mouse.isDown = true;
mouse.downTimestamp = performance.now();
if(x < thresholdStart){
mouse.inLeft = true;
} else if(x > width-thresholdStart){
mouse.inRight = true;
}
});
div.addEventListener('mousemove'/*'touchmove'*/, function(e){
var x = e./*touches[0].*/pageX;
if(mouse.inLeft && x > thresholdEnd){
mouse.inLeft = false;
if(performance.now() - mouse.downTimestamp < thresholdMilliseconds){
swipeEdgeFromLeft();
}
} else if(mouse.inRight && x < width-thresholdEnd){
mouse.inRight = false;
if(performance.now() - mouse.downTimestamp < thresholdMilliseconds){
swipeEdgeFromRight();
}
}
});
div.addEventListener('mouseup'/*'touchend'*/, function(e){
//var x = e./*changedTouches[0].*/pageX;
mouse.isDown = false;
mouse.inLeft = false;
mouse.inRight = false;
mouse.downTimestamp = null;
});
function swipeEdgeFromLeft(){
console.log("edge swipe from left");
}
function swipeEdgeFromRight(){
console.log("edge swipe from right");
}
body {
max-width: 100vw;
height: 100vh;
}
.bar {
height: 100vh;
background-color: rgba(0,0,0,0.4);
position: fixed;
pointer-events: none;
}
#left-inner-threshold {
width: calc(0.1 * 100vw);
left: 0;
}
#right-inner-threshold {
width: calc(0.1 * 100vw);
right: 0;
}
#left-outer-threshold {
width: calc(0.13 * 100vw);
left: 0;
}
#right-outer-threshold {
width: calc(0.13 * 100vw);
right: 0;
}
<div id="left-inner-threshold" class="bar"></div>
<div id="left-outer-threshold" class="bar"></div>
<div id="right-inner-threshold" class="bar"></div>
<div id="right-outer-threshold" class="bar"></div>
Here's a solution to your existing code using Hammer.js v2.0.8
The explanation for how to achieve the edge swipe can be found here answered by #jovinbm.
$(document).ready(function () {
const swipe = new Hammer(document);
function getStartPosition(e) {
const delta_x = e.deltaX;
const delta_y = e.deltaY;
const final_x = e.srcEvent.pageX || e.srcEvent.screenX || 0;
const final_y = e.srcEvent.pageY || e.srcEvent.screenY || 0;
return {
x: final_x - delta_x,
y: final_y - delta_y
}
};
swipe.on('swiperight swipeleft', function (e) {
e.preventDefault();
const { x } = getStartPosition(e);
console.log(x);
//swipe right to open nav /* note the condition here */
if (e.type == 'swiperight' && x >= 0 && x <= 50) {
// open menu
$('#navigation').animate({
left: '0'
});
//swiping left should slide out nav and/or sub-nav
} else {
// close/hide menu
$('#navigation, #task-menu').animate({
left: '-100%'
});
}
});
});
Here's a pen showing it in action:
For swipes, only the final pointerup event is included as the srcEvent in the event object passed to your handler (see http://hammerjs.github.io/api/). The initial pointerdown event that carries the details of the initial position of where the swipe event started is not provided in the hammer event object. Fortunately, you can use the srcEvent in the event object to get the starting position of the event initial pointerdown event.
const getStartPosition = (e) => {
const delta_x = e.deltaX;
const delta_y = e.deltaY;
const final_x = e.srcEvent.pageX || e.srcEvent.screenX || 0;
const final_y = e.srcEvent.pageY || e.srcEvent.screenY || 0;
return {
x: final_x - delta_x,
y: final_y - delta_y
};
};
const handleSwipe = (e) => {
const {x} = getStartPosition(e);
if (x >= 0 && x <= 50) {
// handle swipe from left edge e.t.c
}
else {
// handle other case
}
};
The srcEvent is just a normal javascript event that inherits properties from UIEvent hence the pageX/pageY api above. This will probably not work in other browsers since some of them are not standardized
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 am trying to scroll by highlighting text and dragging down. Now, as you are probably aware, this is standard, default behavior for a standard overflow: auto element, however I am trying to do it with some fancy scrollbars courtesy of jQuery jScrollPane by Kelvin Luck.
I have created a fiddle here: DEMO
basically as you can see, highlighting and scrolling works in the top box (the default overflow: auto box) but in the second it doesn't and, to compound matters, once you reach the bottom it INVERTS your selection!
So, my question(s) is(are) this(these): is there a way to fix this? If so, how?
UPDATE
I have been working on this quite a bit and have found a slight solution using setTimeout()
however, it doesn't work as intended and if anybody is willing to help I have forked it to a new fiddle here: jsFiddle
the code itself is:
pane = $('#scrolldiv2');
pane.jScrollPane({animateEase: 'linear'});
api = pane.data('jsp');
$('#scrolldiv2').on('mousedown', function() {
$(this).off().on('mousemove', function(e) {
rel = $(this).relativePosition();
py = e.pageY - rel.y;
$t = $(this);
if (py >= $(this).height() - 20) {
scroll = setTimeout(scrollBy, 400, 20);
}
else if (py < 20) {
scroll = setTimeout(scrollBy, 400, -20);
}
else {
clearTimeout(scroll);
}
})
}).on('mouseup', function() {
$(this).off('mousemove');
clearTimeout(scroll);
})
var scrollBy = function(v) {
if (api.getContentPositionY < 20 & v == -20) {
api.scrollByY(v + api.getContentPositionY);
clearTimeout(scroll);
} else if (((api.getContentHeight - $t.height()) - api.getContentPositionY) < 20 & v == 20) {
api.scrollByY((api.getContentHeight - $t.height()) - api.getContentPositionY);
clearTimeout(scroll);
} else {
api.scrollByY(v, true)
scroll = setTimeout(scrollBy, 400, v)
}
}
$.fn.extend({
relativePosition: function() {
var t = this.get(0),
x, y;
if (t.offsetParent) {
x = t.offsetLeft;
y = t.offsetTop;
while ((t = t.offsetParent)) {
x += t.offsetLeft;
y += t.offsetTop;
}
}
return {
x: x,
y: y
}
},
})
You just have to scroll down/up depending on how close the mouse is to the end of the div; is not as good as the native solution but it gets the job done ( http://jsfiddle.net/PWYpu/25/ )
$('#scrolldiv2').jScrollPane();
var topScroll = $('#scrolldiv2').offset().top,
endScroll = topScroll + $('#scrolldiv2').height(),
f = ($('#scrolldiv2').height() / $('#scrolldiv2 .jspPane').height())*5 ,
selection = false,
_prevY;
$(document).mousemove(function(e){
var mY;
var delta = _prevY - e.pageY;
if((e.pageY < endScroll && (mY = ((e.pageY - endScroll + 80)/f)) > 0) ||
(e.pageY > topScroll && (mY = (e.pageY - (topScroll + 80))/f) < 0)){
if(selection && (delta > 10 || delta < -10) )
$('#scrolldiv2').data('jsp').scrollByY(mY, false) ;
}
})
$('#scrolldiv2').mousedown(function(e){_prevY = e.pageY; selection = true ;})
$(window).mouseup(function(){selection = false ;})
BTW, the reason it inverts the selection is because it reached the end of the document, just put some white space down there and problem solved.
I really hate to say it, I know it's an issue even I ran into with the update to this plugin, but in the old plugin (seen here) it works just fine with basic call. So I just reverted my copy.