js scrollIntoView 滑动问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js scrollIntoView 滑动问题相关的知识,希望对你有一定的参考价值。

参考技术A 最近写了一个效果,点击事件中控制当前页面滚动到指定位置。用到scrollIntoView方法:

查阅了文档 发现scrollIntoView() 方法是让当前的元素滚动到浏览器窗口的可视区域内。之前的设计中,用img 标签绝对定位了背景图,如果样式对html 或者 body 设置了高度,导致内容向上移动,底部图片超出。

将图片设置成css方式引入,将父元素相对定位一下

scrollIntoView()方法的参数是一个对象,属性中的block 定义了滑动之后的对其方式,修改成:

scrollIntoView 失效调研与替换方案

问题背景

今天需要做一个点击icon滑动到文章评论区的功能,采用了scrollIntoView,发现在移动端偶现失效了。

代码如下:

 commentRef.current.scrollIntoView(
     behavior: 'smooth',
 );

分析

思考1 是否由于浏览器bug导致

这篇博文描述是由于滑动过程中进行了原生事件的监听就会阻断事件继续执行。

因此替换成 scollTo,发现滑动有改善,但是还是有定位不准的问题。

根据这个回答得出可以采用 requestAnimationFrame 来进行动态实现 scrollIntoView

const requestAnimationFrame = window.requestAnimationFrame ||
          window.mozRequestAnimationFrame ||
          window.webkitRequestAnimationFrame ||
          window.msRequestAnimationFrame;

const step = () => 
    const footer = footerCommentRef.current;
    const footerStaticTop = footer.offsetTop;
    const footterBottom = footer.getBoundingClientRect().bottom;

    if (container.scrollTop >= (footerStaticTop)
    || ((footterBottom + bottomPaddingHeight) < (window.innerHeight)) ) 
        return;
    
    const restScrollValue = footerStaticTop - container.scrollTop;
    const scrollValue = restScrollValue > scrollLen ? scrollLen : restScrollValue;

    container.scrollBy(
        0,
        scrollValue,
    );

    requestAnimationFrame(step);

;

requestAnimationFrame(step);

思考2 是否是兼容性问题

上面的方法都试了一遍,发现还是有问题,因此思考是否是兼容性方面出了问题。
重新从 scrollIntoView 着手,采取这里的兼容方案,安装 smoothscroll-polyfill

安装 smoothscroll-polyfill 后报了一个 promise.then is not a function 的错误,成功把qa环境搞挂了。

查阅 caniuse 得这三个方法在Safari的兼容性并不好,
路子走到头了,问题还是没有解决,因此反思是否其他问题导致的。

思考3 是否是其他原因导致的

调研到最后发现是由于图片在落地页的加载过程中没占位/占位不准确导致了滚动不精确的问题。因此再对上面的代码进行改良。

首先需要对所有的落地页图片进行监听,查看其是否完成了加载。

const isAllImageLoaded: Promise<any> = function(
    container: HTMLElement
) 
    const images: HTMLImageElement[] = container.querySelectorAll('img');
    if (images.length === 0) 
        return true;
    
    const promiseImage = [];
    for (let item of images) 
        promiseImage.push(new Promise(resolve => 
            item.loaded = function() 
                resolve();
            ;
        ));
    
    return Promise.all(promiseImage);
;

除了需要对图片加载状态进行监听外,还需要注意以下几点:

  • 当用户操作滚轮或者操作手机滑动时需要停止滚动。
  • 根据文章的长度去判断滚动次数而不是固定长度的滚动,避免长文章滚动时间过长。
  • 记录上一次滚动的位置,若两次滚动位置相同,说明已无法继续滚动,需要停止滚动。
    最终代码如下:
const CHANGESCROLLEVENT = [
    'wheel',
    'touchmove',
    'touchstart',
];
/**
 *  @note: 滚动到评论区公共方法
 *  @param target 目标 dam
 *  @param container container dam
 */
export const scrollIntoComment = async function(
    target: HTMLElement,
    container: HTMLElement
) 
    let isUserScroll = false;

    let requestAnimationFrame = window.requestAnimationFrame ||
          window.mozRequestAnimationFrame ||
          window.webkitRequestAnimationFrame ||
          window.msRequestAnimationFrame;

    CHANGESCROLLEVENT.forEach(item => 
        container.addEventListener(item, () => 
            isUserScroll = true;
        );
    );


    const step = () => 

        const targetTop = target.offsetTop; // 这个值可能会变 所以放里面
        const scrollLen = targetTop / 15;
        const curScrollTop = container.scrollTop;
        if (curScrollTop >= targetTop
        || isUserScroll) 
            return;
        
        const restScrollValue = targetTop - curScrollTop;
        const scrollValue = restScrollValue > scrollLen ? scrollLen : restScrollValue;

        container.scrollBy(
            0,
            scrollValue,
        );

        if (curScrollTop !== container.scrollTop) 
            requestAnimationFrame(step);
        

    ;


    // 先滚动在加载
    requestAnimationFrame(step);

    await isAllImageLoaded(container);

    await requestAnimationFrame(step);
;

更多

[stackoverflow]Javascript - window.scroll( behavior: ‘smooth’ ) not working in Safari

以上是关于js scrollIntoView 滑动问题的主要内容,如果未能解决你的问题,请参考以下文章

scrollIntoView 失效调研与替换方案

scrollIntoView 失效调研与替换方案

scrollIntoView 失效调研与替换方案

scrollIntoView 不是函数 JS

Cypress系列(27)- scrollIntoView() 命令详解

scrollintoview 可以带时间参数吗