【问题标题】:Why does ScrollTop position not work on mobile Safari?为什么 ScrollTop 位置在移动 Safari 上不起作用?
【发布时间】:2019-11-04 12:22:20
【问题描述】:

我的网站上有一项功能,徽标会根据用户在网站上的位置垂直滚动。

你可以看到它在 Chrome here 上运行

但它不适用于 Safari,包括手机和平板电脑。

滚动位置在控制台中似乎根本没有改变。

// 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();
}

【问题讨论】:

标签: javascript safari scrolltop


【解决方案1】:

这种行为背后的原因在于浏览器之间滚动实现的差异。例如,Chrome 根据<html> 计算页面滚动,而 Safari 在<body> 上执行相同的操作。

铬:

Safari:

考虑到这些信息,可以合理地假设在 Safari 中 document.documentElement 完全不知道页面全局滚动值。

要解决此问题,您可以定义一个辅助函数,它使用 document.scrollingElement 并在 document.documentElement 上回退到 getBoundingClientRect

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

并在您的 update 函数中使用它:

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() {
  // ...
}

完整代码:

// 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();
}

希望这会有所帮助。

【讨论】:

  • 您好 - 非常感谢您抽出宝贵时间。我曾尝试在这两个函数之后立即插入:let currentScrollPos = getScrollingElement().scrollTop; 在两组函数之间,但它不起作用。很抱歉,如果很明显,但我不明白在我当前的代码中究竟在哪里插入它。谢谢
  • 刚刚测试过了。如果您只是在您的function update 之后定义function getScrollingElement,则可以正常工作。更新了一个更清晰的答案
  • 这非常有效。我已经授予你赏金 - 非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-27
  • 1970-01-01
  • 2012-09-26
  • 2019-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多