Div锚点滚动太远

44
我在我的网站顶部使用一个高约20像素的静态栏。当我点击锚链接时,文本的一部分会消失在顶部栏后面(对于那些不知道的人,维基百科的导航就是这样工作的。点击标题,浏览器会滚动到该位置)。是否有办法防止这种情况发生?我没法使用iFrame。我唯一能想到的就是每次滚动一点来把它滚回去,但还有其他方法吗?有没有一些CSS设置可以操纵页面主体或其他东西?

我会把iframe从问题中排除掉,这会让问题更加混乱。 - PJ Brunet
"还有其他方法吗?" 是的,可以使用JavaScript来更改浏览器的默认行为。默认情况下,锚点没有滚动到足够的视图中。 - PJ Brunet
1
可能是固定页面标题与页面锚重叠的问题的重复。 - PJ Brunet
7个回答

51
你可以只使用CSS而不需要任何javascript。
给你的锚点添加一个类:
<a class="anchor"></a>

您可以通过将锚点元素设置为块级元素,并进行相对定位,使其在页面上以比实际位置更高或更低的偏移量进行定位。-250px 将把锚点上移 250px。

a.anchor{display: block; position: relative; top: -250px; visibility: hidden;}

通过Jan的方式查看如何偏移HTML锚点以适应固定的标题


1
啊哈,所以 display: block 是问题所在。我尝试过类似的事情,但浏览器会忽略相对偏移,最终我使用了 a[name] {position: absolute; height: 150px; margin-top: -150px;} - riv
这个很棒。这个问题困扰我多年了。谢谢! - MTAdmin
5
这一定是我见过的最壮观的CSS技巧之一。 - David T
我很高兴找到了这个答案,并且它帮助我解决了我的问题。但我仍然想知道为什么有时滚动条会滚动得太远。也许只是锚点在标记中的位置太低了? - Craig London
应该是被接受的答案。干得好。 - jtubre

44

这里有一个现代的、单行的、纯 CSS 解决方案:

scroll-margin-top: 20px;

scroll-margin-top: 20px;

仅在滚动上下文环境中起作用,实际效果为添加一个空白边距。例如,在元素上使用scroll-margin-top: 20px;将使单击该元素的锚标签时,页面滚动到该元素上方20像素处。

兼容性 (查看 caniuse)

不支持 IE。Safari版本11-14使用非标准名称:scroll-snap-margin-top(但功能与预期相同)。Safari 14.1+使用标准名称。

完整示例:

nav{
  position: fixed;
  top:0;
  left:0;
  background: black;
  color: white;
  width: 100%;
}

#after-nav{
  margin-top: 25px;
}

#section-1{
  width: 100%;
  background: green;
  height: 500px;
  scroll-margin-top: 20px;
}

#section-2{
  width: 100%;
  background: blue;
  height: 500px;
  scroll-margin-top: 20px;
}

ul{
margin-top: 0;
}
<nav>
Nav bar: 20px (as in OP)
</nav>
<div id="after-nav">
Table of contents:
<ul>
<li><a href="#section-1">Section 1</a></li>
<li><a href="#section-2">Section 2</a></li>
</ul>


<div id="section-1">Section 1</div>
<div id="section-2">Section 2</div>
</div>


4
其他回答这个问题的一些答案已经接近10年了。在我看来,对于现代浏览器来说,这个答案是最简单和最干净的。 - Adnan
1
好的回答!但是这可能会干扰Bootstrap的滚动监听。 - Gajraj Singh Chouhan
1
非常新鲜干净。这个运行得非常好。谢谢! - Sean
1
“scroll-margin-top” 不支持 Internet Explorer、Safari < 14.1 以及 iOS < 14.5 上的 Safari。请添加 “scroll-snap-margin-top” 以支持 Safari 11+ 和 iOS 11+ 上的 Safari。 - th_lo
2
太棒了,谢谢! - shadow
显示剩余3条评论

9
为了解决这个问题,您可以使用CSS为要跳转的元素添加padding:

示例

另外一种方法是添加边框:

div{ 
  height: 650px; 
  background:#ccc; 
  /*the magic happens here*/
  border-top:42px solid #fff;
}
ul{
  top: 0; 
  width: 100%; 
  height:20px; 
  position: fixed; 
  background: deeppink; 
  margin:0;
  padding:10px; 
}
li{
  float:left;
  list-style:none;
  padding-left:10px;
}
div:first-of-type{ 
  margin-top:0; 
}
<!-- content to be placed inside <body>…</body> -->
<ul>
  <li><a href="#s1">link 1</a>
  <li><a href="#s2">link 2</a>
  <li><a href="#s3">link 3</a>
  <li><a href="#s4">link 4</a>
</ul>
<div id="s1" class="first">1</div>
<div id="s2">2</div>
<div id="s3">3</div>
<div id="s4">4</div>

然而,并非总是适用。

针对javascript的解决方案,您可以使用附加到锚点元素的点击事件,滚动调整像素量如下:

document.querySelector("a").addEventListener("click",function(e){
    // dynamically determining the height of your navbar
    let navbar = document.querySelector("nav");
    let navbarheight = parseInt(window.getComputedStyle(navbar).height,10);
    // show 5 pixels of previous section just for illustration purposes 
    let scrollHeight = document.querySelector(e.target.hash).offsetTop - navbarheight - 5;
    /* scrolling to the element taking the height of the static bar into account*/
    window.scroll(0,scrollHeight);
    /*properly updating the window location*/
    window.location.hash = e.target.hash;
    /* do not execute default action*/
    e.preventDefault();
});
nav{
  position:fixed;
  top:0;
  left:0;
  right:0;
  height:40px;
  text-align:center;
  background:#bada55;
  margin:0;
}
a{
  display:block;
  padding-top:40px;
}
#section1{
  height:800px;
  background:repeating-linear-gradient(45deg,#606dbc55,#606dbc55 10px,#46529855 10px,#46529855 20px);
}
#section2{
  height:800px;
  background:repeating-linear-gradient(-45deg,#22222255,#22222255 10px,#66666655 10px,#66666655 20px);
}
<nav>static header</nav>
<a href="#section2">jump to section 2</a> 
<div id="section1">Section 1</div>
<div id="section2">Section 2</div>


2
我应该附加哪个事件?请提供完整的jQuery示例。 - Arugin
@Arugin,我在jQuery中找到了解决方法https://dev59.com/GGgu5IYBdhLWcg3wMkbF#50690779,不需要填充,只要不滚动太远即可。填充是错误的解决方案,因为它可能会破坏网页设计。 - PJ Brunet
@PJBrunet 首先,如果您仔细阅读问题,OP没有包含JS标签,这就是为什么我首先提供了CSS解决方案(这是他明确提到的)。此外,我看不出为什么填充会“破坏”一个精心制作的网页设计。其次,我还提供了简洁的JavaScript解决方案,那么为什么要点踩呢? - Christoph
我想指出的是,即使没有导航栏,浏览器似乎也会将锚点带到屏幕边缘,这已经看起来很糟糕了。因此,导航栏只会加剧问题。例如,在您正在查看的这个页面上,如果单击“分享”链接,您可以看到问题——投票箭头被裁剪掉了页面的顶部,然后看起来像是被JS向下推了一下。您认为StackOverflow应该在每个答案的顶部添加更多的填充吗?如果问我,填充看起来很漂亮,我们不应该打破设计比例来修复浏览器滚动错误。 - PJ Brunet
嗯,很明显,楼主不同意你的看法,认为填充修复并不是一个“可接受”的解决方案 - 尤其是当他们正在寻找 CSS 解决方案时。 - Christoph
显示剩余3条评论

7
CSS-only:这有点麻烦,但是:target {padding-top: 20px;}可以工作,如果你链接到一个块级元素(我假设你这样做了,因为你的问题中提到了div)。然而,当你手动滚动后,它可能看起来不太好。示例http://dabblet.com/gist/3121729 尽管如此,我认为使用一点JavaScript来解决这个问题会更好。

1
这对我没用(我正在使用bootstrap),但是这里描述的变体可以:https://www.itsupportguides.com/knowledge-base/tech-tips-tricks/how-to-offset-anchor-tag-link-using-css/ - Partho
很可能这会破坏某人的设计。但我不会给你点踩;-) - PJ Brunet

5
我遇到了同样的问题。这里有一个jQuery解决方案。
$('a[href^="#"]').on('click',function (e) {
    e.preventDefault();
    var target = this.hash;
    var $trget = $(target);
    // Example: your header is 70px tall.
    var newTop = $trget.offset().top - 70; 
    $('html, body').animate ({
        scrollTop: newTop
    }, 500, 'swing', function () {
        window.location.hash = target;
    }); 
});

html, body 的原因是为了浏览器兼容性。https://dev59.com/r2Ik5IYBdhLWcg3wadcD#19738288 - PJ Brunet
类似的解决方案在这里 https://dev59.com/vmgu5IYBdhLWcg3wYWFX - PJ Brunet
由于您对这里的几个答案应用了非常严格的规则和严厉的批评,我在此提出以下不满之处: 1)针对仅涉及CSS的问题给出了JavaScript答案, 2)在1)的基础上,为完全不必要的事情包含了一个沉重的框架, 3)存在错误的代码(使用无效值覆盖窗口哈希), 4)生成重复内容(正确的做法是在答案的评论部分提供问题链接)。 请至少清理一下您的代码! - Christoph
作为SO社区的成员,您似乎对平台的工作原理知之甚少:1)标签是有原因的,2)下投票不是用来表达个人观点,而是用来标记那些粗糙、糟糕或明显错误的答案,3)避免重复内容应该符合每个用户的利益。此外,我已经解释了您代码中的问题,所以这只是一种教育性的举动,旨在让您修复代码中明显的问题 - 但是,是的,如果是友好的用户,我可能会自己编辑答案而不是进行下投票。 - Christoph
@Christoph 你对 window hash 的问题是正确的,我已经修复了它。无论如何,进一步研究后发现这是一个重复的问题。 - PJ Brunet
显示剩余2条评论

0

在查看了以上所有答案后,能够解决我的滚动锚点问题的是以下代码:

代码:

#idname {
    scroll-margin-top: 20px;
}

感谢那些提出这个答案的人。您可以显然更改边距的像素数。我将其从20px更改为40px,现在它完美运行。


0

1
如果我没有一个与这个问题竞争的帖子,我会因为引用w3schools而给出-1。如果你想知道原因,请查看http://w3fools.com/。 - Christoph
@Christoph 谢谢您提供宝贵的信息。这里我只是举了一个例子来展示它的工作原理,没有更多的内容。 - RAN
1
是的,我只是想指出,必须非常小心地使用w3schools。如果您想要更好的基础,请改用MDN - Christoph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接