scrollIntoView滚动太远


106

我有一个页面,其中从数据库中动态生成一个滚动条,其中包含带有div的表行。每个表格行都像一个链接,就像您在视频播放器旁边的YouTube播放列表中看到的一样。

当用户访问页面时,他们所在的选项应该位于滚动div的顶部。此功能正在运行。问题是它走得太远了。就像它们启用的选项一样,大约过高10px。因此,将访问该页面,该URL用于标识选择了哪个选项,然后将该选项滚动到滚动div的顶部。注意:这不是窗口的滚动条,它是带有滚动条的div。

我正在使用此代码使它将选定的选项移到div的顶部:

var pathArray = window.location.pathname.split( '/' );

var el = document.getElementById(pathArray[5]);

el.scrollIntoView(true);

它将其移动到div的顶部,但距离太远约10个像素。有人知道该如何解决吗?


44
缺少偏移配置的问题scrollIntoView
mystrdat

Answers:


56

如果大约10像素,那么我想您可以div像这样手动调整包含的滚动偏移量:

el.scrollIntoView(true);
document.getElementById("containingDiv").scrollTop -= 10;

1
会产生与相同的动画效果scrollIntoView吗?
1252748 2014年

1
@thomas AFAIK,普通的DOM scrollIntoView函数不会产生动画。您是在谈论scrollIntoView jQuery插件吗?
Lucas Trzesniewski 2014年

1
那位工作的人,只是方向错误,所以我要做的就是将其从+ = 10更改为-= 10,现在加载正确,非常感谢您的帮助!!!
马修·威尔逊

是的 我很困惑。我认为它很生动。抱歉!
1252748 2014年

16
它可以制作动画,只需添加即可el.scrollIntoView({ behavior: 'smooth' });。支持不是很好!
daveaspinall

107

平滑滚动到合适的位置

获取正确的 y坐标并使用window.scrollTo({top: y, behavior: 'smooth'})

const id = 'profilePhoto';
const yOffset = -10; 
const element = document.getElementById(id);
const y = element.getBoundingClientRect().top + window.pageYOffset + yOffset;

window.scrollTo({top: y, behavior: 'smooth'});

2
这是最正确的答案-我们伊斯利可以找到滚动的位置jQuery('#el').offset().top,然后使用window.scrollTo
亚历山大冈察洛夫

1
此解决方案+1。当我将一个容器相对于顶部偏移放置时,它帮助我正确滚动到该元素。
西甲联赛

任何使用HashLink和React Router来到这里的人都对我有用。在您HashLink,滚动丙 scroll={el => { const yCoordinate = el.getBoundingClientRect().top + window.pageYOffset; const yOffset = -80; window.scrollTo({ top: yCoordinate + yOffset, behavior: 'smooth' }); }}-其中所述偏移量为10-15像素比你的报头的大小更只是为了更好间距
罗布乙

84

您可以分两个步骤进行操作:

el.scrollIntoView(true);
window.scrollBy(0, -10); // Adjust scrolling with a negative value here

22
如果运行scrollIntoView()前尚未完成滚动scrollBy(),则后者将取消前者。至少在Chrome 71
戴夫·休斯

1
在这种情况下,请使用setTimeout,如下面的答案所示:stackoverflow.com/a/51935129/1856258 ..对我来说,它无需超时即可工作。谢谢!
leole

有没有一种方法可以直接将像素的校正器注入scrollIntoView中,以使应用程序直接移动到相关位置?就像scrollIntoView({adjustment:y}),我认为结尾处的自定义代码应该有可能
Webwoman

78

绝对方法定位锚

这样做的另一种方法是将锚点精确定位在页面上所需的位置,而不是依靠按偏移量滚动。我发现它可以更好地控制每个元素(例如,如果某些元素需要不同的偏移量),并且还可以更好地抵抗浏览器API的更改/差异。

<div id="title-element" style="position: relative;">
  <div id="anchor-name" style="position: absolute; top: -100px; left: 0"></div>
</div>

现在,相对于元素的偏移量指定为-100px。创建一个函数来创建此锚以重复使用代码,或者如果您使用的是诸如React之类的现代JS框架,则可以通过创建一个呈现锚的组件来实现此功能,并为每个元素传递锚名称和对齐方式,不一样。

然后只需使用:

const element = document.getElementById('anchor-name')
element.scrollIntoView({ behavior: 'smooth', block: 'start' });

平滑滚动,偏移量为100px。


12
到目前为止,这是顺利,简单地实施此操作的最佳方法。
catch22

2
我认为最好和最原生的方法。
ДаниилПронин

1
这是如此简单和光滑。其他许多只是添加不需要的JS。
RedSands

2
当“页面顶部”类型的链接位于导航栏下方并且您想要显示导航但不希望其他任何链接偏移时,此功能特别有用
Joe Moon

您可以使用负的margin-top来实现相同的效果,避免将元素与另一个具有相对位置的元素包装在一起
Sergi


11

这对我在Chrome中有效(滚动流畅且无计时漏洞)

它只是移动元素,启动滚动,然后将其移回。

如果元素已经在屏幕上,则没有可见的“弹出”。

pos = targetEle.style.position;
top = targetEle.style.top;
targetEle.style.position = 'relative';
targetEle.style.top = '-20px';
targetEle.scrollIntoView({behavior: 'smooth', block: 'start'});
targetEle.style.top = top;
targetEle.style.position = pos;

这是最好的解决方案...希望它被标记为答案。本来可以节省几个小时。
西瓦姆

7

基于先前的答案,我正在Angular5项目中进行此操作。

开始于:

// el.scrollIntoView(true);
el.scrollIntoView({
   behavior: 'smooth',
   block: 'start'
});
window.scrollBy(0, -10); 

但这带来了一些问题,需要为scrollBy()设置setTimeout,如下所示:

//window.scrollBy(0,-10);
setTimeout(() => {
  window.scrollBy(0,-10)
  }, 500);

而且它在MSIE11和Chrome 68+中完美运行。我没有在FF中测试过。500ms是我冒险的最短延迟。降低滚动有时会失败,因为平滑滚动尚未完成。根据您自己的项目进行调整。

对于这个简单但有效的解决方案,请向Fred727 +1 。


6
平滑滚动到某个元素然后再向上滚动会有点不舒服。
catch22

6

假设您要滚动到DOM中所有处于相同级别并且具有类名“ scroll-with-offset”的div,那么此CSS将解决此问题:

.scroll-with-offset {    
  padding-top: 100px;
  margin-bottom: -100px;
}

与页面顶部的偏移量为100px。它只能按预期与block:'start'一起工作:

element.scrollIntoView({ behavior: 'smooth', block: 'start' });

发生的情况是div的最高点在正常位置,但是其内部内容在正常位置下方100px开始。这就是padding-top:100px的用途。margin-bottom:-100px用于抵消以下div的额外利润。为了使解决方案完整,还添加此CSS以抵消最顶部和最底部div的边距/填充:

.top-div {
  padding-top: 0;
}
.bottom-div {
  margin-bottom: 0;
}

6

该解决方案属于@ Arseniy-II,我只是将其简化为一个函数。

function _scrollTo(selector, yOffset = 0){
  const el = document.querySelector(selector);
  const y = el.getBoundingClientRect().top + window.pageYOffset + yOffset;

  window.scrollTo({top: y, behavior: 'smooth'});
}

用法(您可以在此处的StackOverflow中打开控制台并对其进行测试):

_scrollTo('#question-header', 0);


3

我已经知道了,它对我来说非常有用:

// add a smooth scroll to element
scroll(el) {
el.scrollIntoView({
  behavior: 'smooth',
  block: 'start'});

setTimeout(() => {
window.scrollBy(0, -40);
}, 500);}

希望能帮助到你。


2

另一个解决方案是使用“ offsetTop”,如下所示:

var elementPosition = document.getElementById('id').offsetTop;

window.scrollTo({
  top: elementPosition - 10, //add your necessary value
  behavior: "smooth"  //Smooth transition to roll
});

2

因此,也许这有点笨拙,但到目前为止还不错。我在角度9中工作。

文件 .ts

scroll(el: HTMLElement) {
  el.scrollIntoView({ block: 'start',  behavior: 'smooth' });   
}

文件 .html

<button (click)="scroll(target)"></button>
<div  #target style="margin-top:-50px;padding-top: 50px;" ></div>

我用边距和顶部填充调整偏移量。

礼炮!


1

找到了解决方法。假设您要滚动到div(例如此处的Element),并且想在其上方留出20px的间距。将ref设置为其上方的已创建div:

<div ref={yourRef} style={{position: 'relative', bottom: 20}}/> <Element />

这样做将创建所需的间距。

如果您有标题,则在标题后面也创建一个空的div,并为其分配一个等于标题高度的高度,然后对其进行引用。


0

我的主要想法是在我们要滚动到的视图上方创建一个tempDiv。它运行良好,不会滞后于我的项目。

scrollToView = (element, offset) => {
    var rect = element.getBoundingClientRect();
    var targetY = rect.y + window.scrollY - offset;

    var tempDiv;
    tempDiv = document.getElementById("tempDiv");
    if (tempDiv) {
        tempDiv.style.top = targetY + "px";
    } else {
        tempDiv = document.createElement('div');
        tempDiv.id = "tempDiv";
        tempDiv.style.background = "#F00";
        tempDiv.style.width = "10px";
        tempDiv.style.height = "10px";
        tempDiv.style.position = "absolute";
        tempDiv.style.top = targetY + "px";
        document.body.appendChild(tempDiv);
    }

    tempDiv.scrollIntoView({ behavior: 'smooth', block: 'start' });
}

使用示例

onContactUsClick = () => {
    this.scrollToView(document.getElementById("contact-us"), 48);
}

希望对你有帮助


0

基于Arseniy-II的答案:我有一个用例,其中滚动实体不是窗口本身,而是内部模板(在本例中为div)。在这种情况下,我们需要为滚动容器设置一个ID,并通过它getElementById来使用其滚动功能:

<div class="scroll-container" id="app-content">
  ...
</div>
const yOffsetForScroll = -100
const y = document.getElementById(this.idToScroll).getBoundingClientRect().top;
const main = document.getElementById('app-content');
main.scrollTo({
    top: y + main.scrollTop + yOffsetForScroll,
    behavior: 'smooth'
  });

如果有人遇到类似情况,请留在这里!


0

这是我的2美分。

我还遇到了scrollIntoView滚动到某个元素之外的问题,因此我创建了一个脚本(本机javascript),该脚本将元素添加到目标位置,并使用CSS将其定位在顶部并滚动到该元素。滚动后,我再次删除创建的元素。

HTML:

//anchor tag that appears multiple times on the page
<a href="#" class="anchors__link js-anchor" data-target="schedule">
    <div class="anchors__text">
        Scroll to the schedule
    </div>
</a>

//The node we want to scroll to, somewhere on the page
<div id="schedule">
    //html
</div>

Javascript文件:

(() => {
    'use strict';

    const anchors = document.querySelectorAll('.js-anchor');

    //if there are no anchors found, don't run the script
    if (!anchors || anchors.length <= 0) return;

    anchors.forEach(anchor => {
        //get the target from the data attribute
        const target = anchor.dataset.target;

        //search for the destination element to scroll to
        const destination = document.querySelector(`#${target}`);
        //if the destination element does not exist, don't run the rest of the code
        if (!destination) return;

        anchor.addEventListener('click', (e) => {
            e.preventDefault();
            //create a new element and add the `anchors__generated` class to it
            const generatedAnchor = document.createElement('div');
            generatedAnchor.classList.add('anchors__generated');

            //get the first child of the destination element, insert the generated element before it. (so the scrollIntoView function scrolls to the top of the element instead of the bottom)
            const firstChild = destination.firstChild;
            destination.insertBefore(generatedAnchor, firstChild);

            //finally fire the scrollIntoView function and make it animate "smoothly"
            generatedAnchor.scrollIntoView({
                behavior: "smooth",
                block: "start",
                inline: "start"
            });

            //remove the generated element after 1ms. We need the timeout so the scrollIntoView function has something to scroll to.
            setTimeout(() => {
                destination.removeChild(generatedAnchor);
            }, 1);
        })
    })
})();

CSS:

.anchors__generated {
    position: relative;
    top: -100px;
}

希望这对任何人有帮助!


0

我为未通过上述解决方案解决此问题的用户添加了此CSS提示:

#myDiv::before {
  display: block;
  content: " ";
  margin-top: -90px; // adjust this with your header height
  height: 90px; // adjust this with your header height
  visibility: hidden;
}

0

UPD:我创建了一个npm软件包,该软件包比以下解决方案更好,并且更易于使用。

我的smoothScroll函数

我采用了史蒂夫·班顿(Steve Banton)的出色解决方案,并编写了一个使使用起来更加方便的函数。就像我之前尝试过的那样,使用window.scroll()或什至会更容易window.scrollBy(),但是这两个存在一些问题:

  • 使用它们后,一切都会变得很混乱。
  • 您无论如何都无法阻止它们,而必须等到滚动条的和为止。因此,我希望我的功能对您有用。此外,还有一个轻量级的polyfill,使其可以在Safari甚至IE中运行。

这是代码

只需复制它,然后将其弄乱就可以了。

import smoothscroll from 'smoothscroll-polyfill';

smoothscroll.polyfill();

const prepareSmoothScroll = linkEl => {
  const EXTRA_OFFSET = 0;

  const destinationEl = document.getElementById(linkEl.dataset.smoothScrollTo);
  const blockOption = linkEl.dataset.smoothScrollBlock || 'start';

  if ((blockOption === 'start' || blockOption === 'end') && EXTRA_OFFSET) {
    const anchorEl = document.createElement('div');

    destinationEl.setAttribute('style', 'position: relative;');
    anchorEl.setAttribute('style', `position: absolute; top: -${EXTRA_OFFSET}px; left: 0;`);

    destinationEl.appendChild(anchorEl);

    linkEl.addEventListener('click', () => {
      anchorEl.scrollIntoView({
        block: blockOption,
        behavior: 'smooth',
      });
    });
  }

  if (blockOption === 'center' || !EXTRA_OFFSET) {
    linkEl.addEventListener('click', () => {
      destinationEl.scrollIntoView({
        block: blockOption,
        behavior: 'smooth',
      });
    });
  }
};

export const activateSmoothScroll = () => {
  const linkEls = [...document.querySelectorAll('[data-smooth-scroll-to]')];

  linkEls.forEach(linkEl => prepareSmoothScroll(linkEl));
};

要创建链接元素,只需添加以下数据属性:

data-smooth-scroll-to="element-id"

您也可以将另一个属性设置为加法

data-smooth-scroll-block="center"

它代表功能的block选项scrollIntoView()。默认情况下为start。阅读有关MDN的更多信息。

最后

根据需要调整smoothScroll功能。

例如,如果您有一些固定的标头(或者用单词来称呼它masthead),则可以执行以下操作:

const mastheadEl = document.querySelector(someMastheadSelector);

// and add it's height to the EXTRA_OFFSET variable

const EXTRA_OFFSET = mastheadEl.offsetHeight - 3;

如果您没有这种情况,请删除它,为什么不:-D。


0

对于表行中的元素,请使用JQuery 将行移至所需行上方,然后只需滚动至该行即可。

假设我在一个表中有多个行,其中一些应由and admin查看。每个需要查看的行都有向上和向下箭头,将您带到上一个或下一个要查看的项目。

这是一个完整的示例,如果您在记事本中创建一个新的HTML文档并将其保存,则该示例应该运行。还有一些额外的代码可以检测出商品的顶部和底部,以供审核,因此我们不会抛出任何错误。

<html>
<head>
    <title>Scrolling Into View</title>
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
    <style>
        div.scroll { height: 6em; width: 20em; overflow: auto; }
        thead th   { position: sticky; top: -1px; background: #fff; }
        .up, .down { cursor: pointer; }
        .up:hover, .down:hover { color: blue; text-decoration:underline; }
    </style>
</head>
<body>
<div class='scroll'>
<table border='1'>
    <thead>
        <tr>
            <th>Review</th>
            <th>Data</th>
        </tr>
    </thead>
    <tbody>
        <tr id='row_1'>
            <th></th>
            <td>Row 1 (OK)</td>
        </tr>
        <tr id='row_2'>
            <th></th>
            <td>Row 2 (OK)</td>
        </tr>
        <tr id='row_3'>
            <th id='jump_1'><span class='up'>UP</span> <span class='down'>DN</span></th>
            <td>Row 3 (REVIEW)</td>
        </tr>
        <tr id='row_4'>
            <th></th>
            <td>Row 4 (OK)</td>
        </tr>
        <tr id='row_5'>
            <th id='jump_2'><span class='up'>UP</span> <span class='down'>DN</span></th>
            <td>Row 5 (REVIEW)</td>
        </tr>
        <tr id='row_6'>
            <th></th>
            <td>Row 6 (OK)</td>
        </tr>
        <tr id='row_7'>
            <th></th>
            <td>Row 7 (OK)</td>
        </tr>
        <tr id='row_8'>
            <th id='jump_3'><span class='up'>UP</span> <span class='down'>DN</span></th>
            <td>Row 8 (REVIEW)</td>
        </tr>
        <tr id='row_9'>
            <th></th>
            <td>Row 9 (OK)</td>
        </tr>
        <tr id='row_10'>
            <th></th>
            <td>Row 10 (OK)</td>
        </tr>
    </tbody>
</table>
</div>
<script>
$(document).ready( function() {
    $('.up').on('click', function() {
        var id = parseInt($(this).parent().attr('id').split('_')[1]);
        if (id>1) {
            var row_id = $('#jump_' + (id - 1)).parent().attr('id').split('_')[1];
            document.getElementById('row_' + (row_id-1)).scrollIntoView({behavior: 'smooth', block: 'start'});
        } else {
            alert('At first');
        }
    });

    $('.down').on('click', function() {
        var id = parseInt($(this).parent().attr('id').split('_')[1]);
        if ($('#jump_' + (id + 1)).length) {
            var row_id = $('#jump_' + (id + 1)).parent().attr('id').split('_')[1];
            document.getElementById('row_' + (row_id-1)).scrollIntoView({behavior: 'smooth', block: 'start'});
        } else {
            alert('At last');
        }
    });
});
</script>
</body>
</html>

-1

向下滚动特定元素的简单解决方案

const element = document.getElementById("element-with-scroll");
element.scrollTop = element.scrollHeight - 10;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.