I have a feature on my website where the logo scrolls vertically depending on the users position on the site.
You can see it working on Chrome here
However it does not work on Safari, which includes mobile and tablet.
The scroll position does not seem to change at all in the console.
// logo positioning
let logos, logoHeight, barTopMargin;
let viewportHeight;
window.addEventListener('load', init);
window.addEventListener('resize', setSizes);
document.addEventListener('scroll', update);
function init(lockUpdate) {
logos = document.querySelectorAll('.scroll-text');
setSizes(lockUpdate);
}
function update() {
// ensure initialization and prevent recursive call
if (!logos) init(true);
//*************************************************
/**************************************************
THIS LINE MUST BE HERE.
**************************************************/
let maxScrollDist = document.documentElement.scrollHeight - viewportHeight;
//*************************************************
let currentScrollPos = document.documentElement.scrollTop;
let newTop;
let middle = currentScrollPos + viewportHeight/2;
let middleY = maxScrollDist/2;
if (middle >= (maxScrollDist+viewportHeight)/2) {
let p = (middleY - Math.floor(middle - (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = viewportHeight/2 - logoHeight/2;
newTop += (100-p)*(viewportHeight/2)/100;
newTop -= (100-p)*(barTopMargin +logoHeight/2)/100;
newTop = Math.max(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
} else {
let p = (middleY - Math.floor(-middle + (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = barTopMargin*(100-p)/100+(viewportHeight/2 - (logoHeight/2)*p/100 )*p/100;
newTop = Math.min(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
}
logos.forEach(function(el) {
el.style.top = newTop + "px";
});
}
function setSizes(lockUpdate) {
logoHeight = logos[0].offsetHeight;
barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
viewportHeight = window.innerHeight;
if (lockUpdate === true) return;
update();
}
I have a feature on my website where the logo scrolls vertically depending on the users position on the site.
You can see it working on Chrome here
However it does not work on Safari, which includes mobile and tablet.
The scroll position does not seem to change at all in the console.
// logo positioning
let logos, logoHeight, barTopMargin;
let viewportHeight;
window.addEventListener('load', init);
window.addEventListener('resize', setSizes);
document.addEventListener('scroll', update);
function init(lockUpdate) {
logos = document.querySelectorAll('.scroll-text');
setSizes(lockUpdate);
}
function update() {
// ensure initialization and prevent recursive call
if (!logos) init(true);
//*************************************************
/**************************************************
THIS LINE MUST BE HERE.
**************************************************/
let maxScrollDist = document.documentElement.scrollHeight - viewportHeight;
//*************************************************
let currentScrollPos = document.documentElement.scrollTop;
let newTop;
let middle = currentScrollPos + viewportHeight/2;
let middleY = maxScrollDist/2;
if (middle >= (maxScrollDist+viewportHeight)/2) {
let p = (middleY - Math.floor(middle - (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = viewportHeight/2 - logoHeight/2;
newTop += (100-p)*(viewportHeight/2)/100;
newTop -= (100-p)*(barTopMargin +logoHeight/2)/100;
newTop = Math.max(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
} else {
let p = (middleY - Math.floor(-middle + (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = barTopMargin*(100-p)/100+(viewportHeight/2 - (logoHeight/2)*p/100 )*p/100;
newTop = Math.min(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
}
logos.forEach(function(el) {
el.style.top = newTop + "px";
});
}
function setSizes(lockUpdate) {
logoHeight = logos[0].offsetHeight;
barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
viewportHeight = window.innerHeight;
if (lockUpdate === true) return;
update();
}
Share
Improve this question
edited Nov 17, 2019 at 23:59
n1stre
6,0964 gold badges23 silver badges42 bronze badges
asked Nov 4, 2019 at 12:22
Mr ToadMr Toad
2562 gold badges14 silver badges46 bronze badges
3
- Possible duplicate of stackoverflow./questions/1830080/… – Prabhjot Singh Kainth Commented Nov 15, 2019 at 15:55
- Have a look at some of these suggestions: github./nuxt/nuxt.js/issues/2512 – Brett Gregson Commented Nov 15, 2019 at 15:55
- Thanks Brett - I did actually look at this, but unfortunately I don't understand JS or the syntax enough to know HOW to apply this information. – Mr Toad Commented Nov 20, 2019 at 12:48
1 Answer
Reset to default 6 +100The reason behind this behavior is laid inside the difference in scrolling implementation between the browsers. For example Chrome calculates page scrolling based on the <html>
, while Safari does the same on the <body>
.
Chrome:
Safari:
Considering this info it would reasonable to assume that in Safari document.documentElement
is pletely unaware of page global scrolling value.
And to fix this issue you could define a helper func that uses document.scrollingElement
with fallback to getBoundingClientRect
on document.documentElement
:
function getScrollingElement() {
if (document.scrollingElement) {
return document.scrollingElement;
}
const docElement = document.documentElement;
const docElementRect = docElement.getBoundingClientRect();
return {
scrollHeight: Math.ceil(docElementRect.height),
scrollTop: Math.abs(docElementRect.top)
}
}
and use it in your update
func:
function update() {
// ensure initialization and prevent recursive call
if (!logos) init(true);
//*************************************************
/**************************************************
THIS LINE MUST BE HERE.
**************************************************/
let maxScrollDist = getScrollingElement().scrollHeight - viewportHeight;
//*************************************************
let currentScrollPos = getScrollingElement().scrollTop;
// ...
}
function getScrollingElement() {
// ...
}
Full code:
// logo positioning
let logos, logoHeight, barTopMargin;
let viewportHeight;
window.addEventListener('load', init);
window.addEventListener('resize', setSizes);
document.addEventListener('scroll', update);
function init(lockUpdate) {
logos = document.querySelectorAll('.scroll-text');
setSizes(lockUpdate);
}
function update() {
// ensure initialization and prevent recursive call
if (!logos) init(true);
//*************************************************
/**************************************************
THIS LINE MUST BE HERE.
**************************************************/
let maxScrollDist = getScrollingElement().scrollHeight - viewportHeight;
//*************************************************
let currentScrollPos = getScrollingElement().scrollTop;
let newTop;
let middle = currentScrollPos + viewportHeight/2;
let middleY = maxScrollDist/2;
if (middle >= (maxScrollDist+viewportHeight)/2) {
let p = (middleY - Math.floor(middle - (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = viewportHeight/2 - logoHeight/2;
newTop += (100-p)*(viewportHeight/2)/100;
newTop -= (100-p)*(barTopMargin +logoHeight/2)/100;
newTop = Math.max(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
} else {
let p = (middleY - Math.floor(-middle + (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = barTopMargin*(100-p)/100+(viewportHeight/2 - (logoHeight/2)*p/100 )*p/100;
newTop = Math.min(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
}
logos.forEach(function(el) {
el.style.top = newTop + "px";
});
}
function getScrollingElement() {
if (document.scrollingElement) {
return document.scrollingElement;
}
const docElement = document.documentElement;
const docElementRect = docElement.getBoundingClientRect();
return {
scrollHeight: Math.ceil(docElementRect.height),
scrollTop: Math.abs(docElementRect.top)
}
}
function setSizes(lockUpdate) {
logoHeight = logos[0].offsetHeight;
barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
viewportHeight = window.innerHeight;
if (lockUpdate === true) return;
update();
}
Hope this helps.