抵消html锚点以调整固定标题

Mat*_*den 1056 html javascript css anchor offset

我正在努力清理锚点的工作方式.我有一个固定在页面的顶部的头,所以当你在页面的其他地方链接到一个锚,页面跳转所以其锚固在页面的顶部,留下固定头后面的内容(我希望那讲得通).我需要一种方法来将锚点从头部的高度偏移25px.我更喜欢HTML或CSS,但Javascript也可以接受.

Jan*_*Jan 1133

您可以在没有任何JavaScript的情况下使用CSS.

给你的锚一个班:

<a class="anchor" id="top"></a>
Run Code Online (Sandbox Code Playgroud)

然后,您可以将锚点设置为高于或低于页面实际显示位置的偏移量,方法是将其设置为块元素并相对定位.-250px将锚定位置为250px

a.anchor {
    display: block;
    position: relative;
    top: -250px;
    visibility: hidden;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果你想让它适用于可见元素,你也可以使用一个伪元素,一个la` .thing-with-anchor:before {content:''; 显示:块; 位置:相对; 宽度:0; 身高:5em; margin-top:-5em}`显示块和位置相对是必不可少的. (89认同)
  • 爱你的解决方案!它似乎不适用于IE7.无论如何,我打算从现在开始忽略IE7用户...... (79认同)
  • 我们不应该再使用带有href属性的标签了.相反,我们假设在标题/部分/ etc中使用id标记用于锚定文本.那么解决方案是什么? (21认同)
  • @harpo:好主意,但不起作用.在Chrome 40中尝试过. (16认同)
  • 这是更好的答案,因为它不依赖于其位置设置为相对的父元素.感谢发布此内容. (8认同)
  • 如果我们想要使用:target,这不起作用. (3认同)
  • 对此的更新答案(截至 2021 年)是使用 `scroll-padding` https://css-tricks.com/almanac/properties/s/scroll-padding/ (3认同)
  • 我建议其他人使用它代替span标签。如果您要添加标记,不妨将其设为传统的锚标记。 (2认同)
  • 天才爱你 (2认同)
  • 为了使它保持动态,您可以使用诸如“ top:calc(-&lt;?php echo $ fixed_header_height;?&gt;-20px);”之类的东西在菜单和锚点之间留出一些空间。 (2认同)

Ale*_*vin 322

我找到了这个解决方案

<a name="myanchor">
    <h1 style="padding-top: 40px; margin-top: -40px;">My anchor</h1>
</a>
Run Code Online (Sandbox Code Playgroud)

这不会在内容中产生任何差距,并且锚链接工作非常好.

  • 由于隐藏的填充/边距,<h1>上方的元素将无法点击.我最终使用了Ian Clack的jQuery解决方案,效果很好. (27认同)
  • 工作得很好.我创建了一个特殊的CSS锚类,并将其附加到我的锚点:<a class="anchor" name="foo"> </a>.谢谢. (5认同)
  • 我想我想出来了:h2 [id],h3 [id],h4 [id],[name] {padding-top:XXpx; padding-bottom:XXpx; 它适用于所有带ID的h2,h3,h4标签以及命名锚点. (5认同)
  • 导航中的指标仍然有问题。因此,如果您向下滚动页面,则活动导航项不会切换,直到您滚动经过锚点目标。 (3认同)
  • 您将如何使其与使用元素 ID 的锚点一起使用,即 &lt;h1 id="smth"&gt;Text&lt;/h1&gt;...&lt;a href="#smth"&gt;Link&lt;/a&gt;? (2认同)
  • 为了防止上面的元素不可点击的问题,您可以在 h* 元素上设置负 z 索引。`h3[id] { z 索引:-1; }` (2认同)

Ian*_*ark 159

由于这是演示的关注点,纯CSS解决方案将是理想的.然而,这个问题是在2012年提出的,尽管已经提出了相对定位/负边际解决方案,但这些方法看起来相当笨拙,产生了潜在的流量问题,并且无法动态响应DOM /视口的变化.

考虑到这一点,我相信使用JavaScript 仍然是(2017年2月)最好的方法.下面是一个vanilla-JS解决方案,它将响应锚点击并在加载时解析页面哈希(参见JSFiddle)..getFixedOffset()如果需要动态计算,请修改方法.如果您正在使用jQuery,这里是一个经过修改的解决方案,具有更好的事件委派和平滑滚动.

(function(document, history, location) {
  var HISTORY_SUPPORT = !!(history && history.pushState);

  var anchorScrolls = {
    ANCHOR_REGEX: /^#[^ ]+$/,
    OFFSET_HEIGHT_PX: 50,

    /**
     * Establish events, and fix initial scroll position if a hash is provided.
     */
    init: function() {
      this.scrollToCurrent();
      window.addEventListener('hashchange', this.scrollToCurrent.bind(this));
      document.body.addEventListener('click', this.delegateAnchors.bind(this));
    },

    /**
     * Return the offset amount to deduct from the normal scroll position.
     * Modify as appropriate to allow for dynamic calculations
     */
    getFixedOffset: function() {
      return this.OFFSET_HEIGHT_PX;
    },

    /**
     * If the provided href is an anchor which resolves to an element on the
     * page, scroll to it.
     * @param  {String} href
     * @return {Boolean} - Was the href an anchor.
     */
    scrollIfAnchor: function(href, pushToHistory) {
      var match, rect, anchorOffset;

      if(!this.ANCHOR_REGEX.test(href)) {
        return false;
      }

      match = document.getElementById(href.slice(1));

      if(match) {
        rect = match.getBoundingClientRect();
        anchorOffset = window.pageYOffset + rect.top - this.getFixedOffset();
        window.scrollTo(window.pageXOffset, anchorOffset);

        // Add the state to history as-per normal anchor links
        if(HISTORY_SUPPORT && pushToHistory) {
          history.pushState({}, document.title, location.pathname + href);
        }
      }

      return !!match;
    },

    /**
     * Attempt to scroll to the current location's hash.
     */
    scrollToCurrent: function() {
      this.scrollIfAnchor(window.location.hash);
    },

    /**
     * If the click event's target was an anchor, fix the scroll position.
     */
    delegateAnchors: function(e) {
      var elem = e.target;

      if(
        elem.nodeName === 'A' &&
        this.scrollIfAnchor(elem.getAttribute('href'), true)
      ) {
        e.preventDefault();
      }
    }
  };

  window.addEventListener(
    'DOMContentLoaded', anchorScrolls.init.bind(anchorScrolls)
  );
})(window.document, window.history, window.location);
Run Code Online (Sandbox Code Playgroud)

  • 好评,我会更新:) - BTW它也应该是`$("body").on("click","a"...`因为它可能需要用于添加到文档中的锚点通过脚本(因此我使用`.live`的原因) (4认同)
  • 效果很好,虽然对于jquery 1.7+,使用$("a").on("click",...而不是$("a").live("click",... (3认同)
  • 另外,值得注意的是,这会与其他 href/id 对混淆,如折叠、轮播等......有没有简单的方法来解决这个问题? (3认同)
  • @tom10 我想你只需要让选择器更具体,要么通过使用`a:not(.not-anchor)`(或名称不太容易混淆的名字)将锚点列入黑名单,然后确保你的折叠/轮播库添加这些类到他们的锚点。如果您使用 jQuery UI 或 Bootstrap,我想他们会添加一些您可以参考的其他类。 (3认同)
  • 如果您连续两次单击同一锚点(从带有锚点链接的菜单中),则第二次单击效果不佳。 (3认同)
  • @ tom10我在`href`中为```添加了一些条件,以避免破坏崩溃和轮播:`if(href.indexOf("#")== 0 &&(!$(this ).data()|| this.self == window))` (2认同)

Hrv*_*jak 148

我也在寻找解决方案.在我的情况下,这很容易.

我有一个包含所有链接的列表菜单:

<ul>
<li><a href="#one">one</a></li>
<li><a href="#two">two</a></li>
<li><a href="#three">three</a></li>
<li><a href="#four">four</a></li>
</ul>
Run Code Online (Sandbox Code Playgroud)

在它下面应该去的标题下面.

<h3>one</h3>
<p>text here</p>

<h3>two</h3>
<p>text here</p>

<h3>three</h3>
<p>text here</p>

<h3>four</h3>
<p>text here</p>
Run Code Online (Sandbox Code Playgroud)

现在,因为我的页面顶部有一个固定的菜单,我不能让它转到我的标签,因为它会在菜单后面.

相反,我在我的标记中添加了一个带有正确id的span标记.

<h3><span id="one"></span>one</h3>
Run Code Online (Sandbox Code Playgroud)

现在使用2行css来正确定位它们.

h3{ position:relative; }
h3 span{ position:absolute; top:-200px;}
Run Code Online (Sandbox Code Playgroud)

更改顶部值以匹配固定标题的高度(或更多).现在我假设这也适用于其他元素.

  • `span` 可以替换为 `a` (4认同)
  • 这应该得到复选标记。 (2认同)
  • 我喜欢这个解决方案 (2认同)
  • 这非常有效,并且避免了我使用其他技术遇到的一些问题,例如在使用设置 padding-top 的 h2 标签时。 (2认同)

Mar*_*ham 108

FWIW这对我有用:

[id]::before {
  content: '';
  display: block;
  height:      75px;
  margin-top: -75px;
  visibility: hidden;
}
Run Code Online (Sandbox Code Playgroud)

  • 你这个男人!很好的解决方案。 (7认同)
  • 我不完全理解为什么会这样,但是我 +1。其他的没有一个像这一样容易实现或工作。:-) (4认同)
  • 这很棒!但是,如果 `[id]` 元素是 `display: flex` 容器的子元素,则不起作用:`::before` 元素创建的高度不会影响 `[id]` 的高度元素因为 flex ‍♀️ (4认同)
  • 有人可以解释为什么这有效吗? (3认同)
  • 我将其包装在媒体查询中,因此它仅适用于具有固定导航的中型和大型屏幕。 (2认同)
  • 这可能有效,但是它将与标题之前的内容重叠。 (2认同)

Zia*_*iav 85

受亚历山大·萨文启发的纯css解决方案:

a[name] {
  padding-top: 40px;
  margin-top: -40px;
  display: inline-block; /* required for webkit browsers */
}
Run Code Online (Sandbox Code Playgroud)

(可选)如果目标仍在屏幕外,您可能需要添加以下内容:

  vertical-align: top;
Run Code Online (Sandbox Code Playgroud)

  • 我刚刚尝试使用Chrome,并且不需要显示内联块. (6认同)
  • @Asrail对我而言如果没有它就行不通 (2认同)
  • a [name]选择器不应受链接影响 (2认同)

Lan*_*nce 38

这需要从以前的答案中获取许多元素,并将其组合成一个微小的(194字节缩小的)匿名jQuery函数.调整fixedElementHeight以获取菜单或阻止元素的高度.

    (function($, window) {
        var adjustAnchor = function() {

            var $anchor = $(':target'),
                    fixedElementHeight = 100;

            if ($anchor.length > 0) {

                $('html, body')
                    .stop()
                    .animate({
                        scrollTop: $anchor.offset().top - fixedElementHeight
                    }, 200);

            }

        };

        $(window).on('hashchange load', function() {
            adjustAnchor();
        });

    })(jQuery, window);
Run Code Online (Sandbox Code Playgroud)

如果您不喜欢动画,请替换

$('html, body')
     .stop()
     .animate({
         scrollTop: $anchor.offset().top - fixedElementHeight
     }, 200);
Run Code Online (Sandbox Code Playgroud)

有:

window.scrollTo(0, $anchor.offset().top - fixedElementHeight);
Run Code Online (Sandbox Code Playgroud)

Uglified版本:

 !function(o,n){var t=function(){var n=o(":target"),t=100;n.length>0&&o("html, body").stop().animate({scrollTop:n.offset().top-t},200)};o(n).on("hashchange load",function(){t()})}(jQuery,window);
Run Code Online (Sandbox Code Playgroud)

  • @MajesticRa一个棘手的问题是onload或scroll事件中的操作顺序.如果页面在页面加载或滚动后调整布局(例如缩小标头),则:目标偏移的计算可能是错误的. (2认同)

Lez*_*ezz 34

我的解决方案结合了CMS的目标和选择器之前.其他技术不考虑锚中的文本.将高度和负边距调整为您需要的偏移量...

:target::before {
    content: '';
    display: block;
    height:      180px;
    margin-top: -180px;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果只支持现代浏览器就可以了,我建议只使用`:target {scroll-margin-top: 180px; }` (或您希望的任何其他测量或 `calc()`)。 (15认同)
  • 如果您决定使用此方法,那么添加“pointer-events: none;”可能是个好主意。因为否则与不可见覆盖层(锚点上方)相交的链接将不可单击。 (5认同)
  • 这两个 CSS 解决方案乍一看对我不起作用,但最后我发现它**可能与其他 CSS 属性不兼容**。添加了一个包装器并解决了问题。:target:before 与 display:block 的组合最适合我。 (2认同)
  • [解决]这个解决方案有效,我用`display:block;`来解决这个问题.没有特殊的类,空标签或javascript. (2认同)
  • 该解决方案通过断开它们的连接来弄乱折叠的边距。编辑:我只是将 id 放在 `&lt;h1&gt;` 而不是 `&lt;section&gt;` 上,它工作正常 (2认同)

Sho*_*vik 30

我一直面临着类似的问题,不幸的是,在实施了上述所有解决方案之后,我得出了以下结论.

  1. 我的内部元素具有脆弱的CSS结构并且实现位置相对/绝对游戏,完全打破了页面设计.
  2. CSS不是我的强项.

我写了这个简单的滚动js,它解释了由于标题引起的偏移并将div重新定位在大约125像素以下.请根据需要使用它.

HTML

<div id="#anchor"></div> <!-- #anchor here is the anchor tag which is on your URL -->
Run Code Online (Sandbox Code Playgroud)

JavaScript

 $(function() {
  $('a[href*=#]:not([href=#])').click(function() {
    if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') 
&& location.hostname == this.hostname) {

      var target = $(this.hash);
      target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
      if (target.length) {
        $('html,body').animate({
          scrollTop: target.offset().top - 125 //offsets for fixed header
        }, 1000);
        return false;
      }
    }
  });
  //Executed on page load with URL containing an anchor tag.
  if($(location.href.split("#")[1])) {
      var target = $('#'+location.href.split("#")[1]);
      if (target.length) {
        $('html,body').animate({
          scrollTop: target.offset().top - 125 //offset height of header here too.
        }, 1000);
        return false;
      }
    }
});
Run Code Online (Sandbox Code Playgroud)

在这里查看实时实施.

  • 这是一个很好的解决方案,对我来说效果很好. (3认同)
  • 非常,非常感激.很好,很顺利. (2认同)

Ale*_*one 30

对于现代浏览器,只需将CSS3:target选择器添加到页面即可.这将自动应用于所有锚点.

:target {
    display: block;    
    position: relative;     
    top: -100px;
    visibility: hidden;
}
Run Code Online (Sandbox Code Playgroud)

  • 您甚至可以执行`:target:before`来创建隐藏的伪元素,而不是隐藏整个目标. (7认同)
  • 在大多数情况下,隐藏`:target`似乎是一个坏主意.您正在向任何向您的页面发送链接的人授予选择具有ID的任何元素的权力,并将其从查看者将看到的页面中删除.在驯服方面,技术娴熟的恶作剧者可能会注意到这一点,并决定对它有一些乐趣,就像他们曾经玩过能够通过操纵他们的URL来链接到一些新闻网站在Facebook上显示愚蠢的标题一样有趣.更严重的是,如果您的页面具有类似"不与任何人共享此秘密信息"消息的ID,则可能是安全漏洞. (3认同)

Zso*_*gyi 28

你可以在没有js的情况下完成它而不需要改变html.这只是css-only.

a[id]::before {
    content: '';
    display: block;
    height: 50px;
    margin: -30px 0 0;
}
Run Code Online (Sandbox Code Playgroud)

这将在每个具有id的a-tag之前附加一个伪元素.调整值以匹配标题的高度.

  • 有趣的想法,但请注意,如果你碰巧有与`id`的可见链接,这会搞砸显示屏. (6认同)
  • 如果您想知道为什么它不适合您,请检查父元素是否没有* border *或* padding *。 (3认同)
  • 我将其修改为“:target [id]:before”,因为我锚定链接的ID有时为h1-h6。 (2认同)

Kri*_*aun 11

正如@moeffju所说,这可以通过CSS实现.我遇到的问题(我很惊讶,我还没有看到过讨论)是将前面的元素与填充或透明边框重叠的技巧,可以防止在这些部分底部的悬停和点击操作,因为下面的部分更高Z顺序.

我找到的最佳解决方案是将部分内容放在以下div位置z-index: 1:

// Apply to elements that serve as anchors
.offset-anchor {
  border-top: 75px solid transparent;
  margin: -75px 0 0;
  -webkit-background-clip: padding-box;
  -moz-background-clip: padding;
  background-clip: padding-box;
}

// Because offset-anchor causes sections to overlap the bottom of previous ones,
// we need to put content higher so links aren't blocked by the transparent border.
.container {
  position: relative;
  z-index: 1;
}
Run Code Online (Sandbox Code Playgroud)


use*_*125 10

具有改变位置属性的解决方案并不总是可行的(它可以破坏布局)因此我建议:

HTML:

<a id="top">Anchor</a>
Run Code Online (Sandbox Code Playgroud)

CSS:

#top {
    margin-top: -250px;
    padding-top: 250px;
}
Run Code Online (Sandbox Code Playgroud)

用这个:

<a id="top">&nbsp;</a>
Run Code Online (Sandbox Code Playgroud)

最小化重叠,并将font-size设置为1px.空锚在某些浏览器中不起作用.


odu*_*ont 9

对于同样的问题,我使用了一个简单的解决方案:在每个锚点上放置一个40px的填充顶部.

  • 谢谢,这基本上就是我最终做的,但我想知道是否有一个解决方案,在这种情况下添加额外的填充可能会很尴尬. (2认同)

小智 8

这个链接给出的答案中借用一些代码(没有指定作者),你可以在锚点上包含一个漂亮的平滑滚动效果,同时使它停在锚点上方-60px处,很好地适应固定的引导程序导航bar(需要jQuery):

$(".dropdown-menu a[href^='#']").on('click', function(e) {
   // prevent default anchor click behavior
   e.preventDefault();

   // animate
   $('html, body').animate({
       scrollTop: $(this.hash).offset().top - 60
     }, 300, function(){
     });
});
Run Code Online (Sandbox Code Playgroud)


Mar*_*ery 5

不要使用与页面其余内容重叠的固定位置导航栏(整个页面主体可滚动),而是考虑使用带有静态导航栏的不可滚动主体,然后将页面内容放在下面绝对定位的可滚动 div。

也就是说,有这样的 HTML...

<div class="static-navbar">NAVBAR</div>
<div class="scrollable-content">
  <p>Bla bla bla</p>
  <p>Yadda yadda yadda</p>
  <p>Mary had a little lamb</p>
  <h2 id="stuff-i-want-to-link-to">Stuff</h2>
  <p>More nonsense</p>
</div>
Run Code Online (Sandbox Code Playgroud)

...CSS 像这样:

.static-navbar {
  height: 100px;
}
.scrollable-content {
  position: absolute;
  top: 100px;
  bottom: 0;
  overflow-y: scroll;
  width: 100%;
}
Run Code Online (Sandbox Code Playgroud)

然而,这种方法有一个显着的缺点,即当页面标题中的元素获得焦点时,用户将无法使用键盘滚动页面(例如,通过向上和向下箭头或 Page Up 和 Page Up 和向下翻页键)。

这是一个 JSFiddle 演示了这一点。

  • 非hacky,但是:(1)在这个例子之外完全没用,(2)采用和维护很麻烦,(3)反语义和/或CSS滥用,(4)不直观。我随时都会采用 _clean_ `a:before {...}` 解决方案! (3认同)