关于javascript:抵消html锚点以调整固定标题

offsetting an html anchor to adjust for fixed header

本问题已经有最佳答案,请猛点这里访问。

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


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

给你的锚一个班:

1
 

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

1
2
3
4
5
6
a.anchor {
    display: block;
    position: relative;
    top: -250px;
    visibility: hidden;
}


我发现这个解决方案:

1
    <h1 style="padding-top: 40px; margin-top: -40px;">My anchor

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


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
(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);


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<ul>


<li>
one
</li>


<li>
two
</li>


<li>
three
</li>


<li>
four
</li>


</ul>

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
one
<p>
text here
</p>

two
<p>
text here
</p>

three
<p>
text here
</p>

four
<p>
text here
</p>

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

相反,我在我的标签中添加了一个带有正确ID的span标签。

1
<span id="one"></span>one

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

1
2
h3{ position:relative; }
h3 span{ position:absolute; top:-200px;}

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


FWIW这对我有用:

1
2
3
4
5
6
7
[id]::before {
  content: '';
  display: block;
  height:      75px;
  margin-top: -75px;
  visibility: hidden;
}


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

1
2
3
4
5
a[name] {
  padding-top: 40px;
  margin-top: -40px;
  display: inline-block; /* required for webkit browsers */
}

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

1
  vertical-align: top;


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    (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);

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

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

有:

1
window.scrollTo(0, $anchor.offset().top - fixedElementHeight);

Uglified版本:

1
 !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);


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

1
2
3
4
5
6
:target::before {
    content: '';
    display: block;
    height:      180px;
    margin-top: -180px;
}


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

  • 我的内部元素具有脆弱的CSS结构并且实现位置相对/绝对游戏,完全打破了页面设计。
  • CSS不是我的强项。
  • 我写了这个简单的滚动js,它解释了由于标题引起的偏移并将div重新定位在大约125像素以下。请根据需要使用它。

    HTML

    1
     <!-- #anchor here is the anchor tag which is on your URL -->

    JavaScript

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     $(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;
          }
        }
    });

    在这里查看实时实施。


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

    1
    2
    3
    4
    5
    6
    :target {
        display: block;    
        position: relative;    
        top: -100px;
        visibility: hidden;
    }


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

    1
    2
    3
    4
    5
    6
    a[id]::before {
        content: '';
        display: block;
        height: 50px;
        margin: -30px 0 0;
    }

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


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

    我找到的最佳解决方案是将节内容放在div z-index: 1中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 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;
    }

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

    HTML:

    1
    Anchor

    CSS:

    1
    2
    3
    4
    #top {
        margin-top: -250px;
        padding-top: 250px;
    }

    用这个:

    1
     

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


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


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $(".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(){
         });
    });


    如果您的锚是表元素或表(行或单元格)中,则上述方法不能很好地工作。

    我不得不使用javascript并绑定到窗口hashchange事件来解决此问题(演示):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function moveUnderNav() {
        var $el, h = window.location.hash;
        if (h) {
            $el = $(h);
            if ($el.length && $el.closest('table').length) {
                $('body').scrollTop( $el.closest('table, tr').position().top - 26 );
            }
        }
    }

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

    *注意:hashchange事件并非在所有浏览器中都可用。


    我遇到了同样的问题并最终手动处理了点击事件,例如:

    1
    2
    3
    4
    5
    6
    $('#mynav a').click(() ->
      $('html, body').animate({
          scrollTop: $($(this).attr('href')).offset().top - 40
      }, 200
      return false
    )

    当然,滚动动画是可选的。


    这得益于Shouvik的答案 - 与他的概念相同,只有固定标题的大小不是硬编码的。只要您的固定标头位于第一个标头节点中,这应该"正常工作"

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    /*jslint browser: true, plusplus: true, regexp: true */

    function anchorScroll(fragment) {
       "use strict";
        var amount, ttarget;
        amount = $('header').height();
        ttarget = $('#' + fragment);
        $('html,body').animate({ scrollTop: ttarget.offset().top - amount }, 250);
        return false;
    }

    function outsideToHash() {
       "use strict";
        var fragment;
        if (window.location.hash) {
            fragment = window.location.hash.substring(1);
            anchorScroll(fragment);
        }
    }

    function insideToHash(nnode) {
       "use strict";
        var fragment;
        fragment = $(nnode).attr('href').substring(1);
        anchorScroll(fragment);
    }

    $(document).ready(function () {
       "use strict";
        $("a[href^='#']").bind('click',  function () {insideToHash(this); });
        outsideToHash();
    });


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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    NAVBAR

      <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
      <p>
    More nonsense
    </p>

    ......和这样的CSS:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .static-navbar {
      height: 100px;
    }
    .scrollable-content {
      position: absolute;
      top: 100px;
      bottom: 0;
      overflow-y: scroll;
      width: 100%;
    }

    这以直接,非黑客的方式实现了期望的结果。上面提到的这个和一些聪明的CSS黑客之间的行为的唯一区别是滚动条(在呈现一个的浏览器中)将附加到内容div而不是页面的整个高度。您可能会或可能不会认为这是可取的。

    这是一个JSFiddle,展示了这一点。


    我在TYPO3网站上遇到了这个问题,所有"内容元素"都包含在以下内容中:

    1
    ...

    我改变了渲染,所以它呈现如下:

    1
    ...

    这个CSS:

    1
    2
    3
    4
    .anchor{
        position: relative;
        top: -50px;
    }

    固定的顶杆为40px高,现在锚点再次起作用并在顶杆下方开始10px。

    这种技术的唯一缺点是你不能再使用:target


    您可以使用a[name]:not([href]) css选择器在没有ID的情况下实现此目的。这只是查找具有名称且没有href的链接,例如

    示例规则可能是:

    1
    2
    3
    4
    5
    6
    a[name]:not([href]){
        display: block;    
        position: relative;    
        top: -100px;
        visibility: hidden;
    }


    您还可以使用以下attr添加锚点:

    1
    2
    3
    4
    (text-indent:-99999px;)
    visibility: hidden;
    position:absolute;
    top:-80px;

    并为父容器提供相对位置。

    适合我的作品。


    我添加了40px-height .vspace元素,在每个h1元素之前保持锚点。

    1
      Gherkin

    在CSS中:

    1
    .vspace { height: 40px;}

    它工作得很好,而且空间不大。


    添加Ziav的答案(感谢Alexander Savin),我需要使用旧式...,因为我们在代码中使用...作为其他目的。我使用display: inline-block时出现了一些显示问题 - 每个

    元素的第一行显示为略微缩进(在Webkit和Firefox浏览器上)。我最终尝试了其他display值,而display: table-caption对我来说非常有效。

    1
    2
    3
    4
    5
    .anchor {
      padding-top: 60px;
      margin-top: -60px;
      display: table-caption;
    }


    来自@Jan的优秀答案的另一个转折是将其合并到#uberbar固定标头中,该标头使用jQuery(或MooTools)。 (http://davidwalsh.name/persistent-header-opacity)

    我已经调整了代码,因此内容的顶部总是低于固定标题之下,并且还添加了来自@Jan的锚点,确保锚点始终位于固定标题下方。

    CSS:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #uberbar {
        border-bottom:1px solid #0000cc;
        position:fixed;
        top:0;
        left:0;
        z-index:2000;
        width:100%;
    }

    a.anchor {
        display: block;
        position: relative;
        visibility: hidden;
    }

    jQuery(包括对#uberbar和锚点方法的调整:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <script type="text/javascript">
    $(document).ready(function() {
        (function() {
            //settings
            var fadeSpeed = 200, fadeTo = 0.85, topDistance = 30;
            var topbarME = function() { $('#uberbar').fadeTo(fadeSpeed,1); }, topbarML = function() { $('#uberbar').fadeTo(fadeSpeed,fadeTo); };
            var inside = false;
            //do
            $(window).scroll(function() {
                position = $(window).scrollTop();
                if(position > topDistance && !inside) {
                    //add events
                    topbarML();
                    $('#uberbar').bind('mouseenter',topbarME);
                    $('#uberbar').bind('mouseleave',topbarML);
                    inside = true;
                }
                else if (position < topDistance){
                    topbarME();
                    $('#uberbar').unbind('mouseenter',topbarME);
                    $('#uberbar').unbind('mouseleave',topbarML);
                    inside = false;
                }
            });
            $('#content').css({'margin-top': $('#uberbar').outerHeight(true)});
            $('a.anchor').css({'top': - $('#uberbar').outerHeight(true)});
        })();
    });

    最后是HTML:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        <!--CONTENT OF FIXED HEADER-->

    ....

        <!--MAIN CONTENT-->
        ....
       
        ....
       
        ....

    也许这对于那些喜欢#uberbar fading dixed header的人来说很有用!


    如何使用可链接ID的隐藏span标记来提供导航栏的高度:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #head1 {
      padding-top: 60px;
      height: 0px;
      visibility: hidden;
    }


    <span class="head1">somecontent</span>
    <h5 id="headline1">This Headline is not obscured</h5>

    继承人:http://jsfiddle.net/N6f2f/7


    @ AlexanderSavin的解决方案适用于WebKit浏览器。

    我还必须使用:target伪类,它将样式应用于所选锚点以调整FFOperaIE9中的填充:

    1
    2
    3
    a:target {
      padding-top: 40px
    }

    请注意,此样式不适用于Chrome / Safari,因此您可能必须使用css-hacks,条件注释等。

    另外我想注意亚历山大的解决方案是有效的,因为目标元素是inline。如果您不想要链接,只需更改display属性:

    1
       <h1 style="padding-top: 40px; margin-top: -40px;">My anchor


    这是我们在我们网站上使用的解决方案。将headerHeight变量调整为标题高度。将js-scroll类添加到应在单击时滚动的锚点。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // SCROLL ON CLICK
    // --------------------------------------------------------------------------
    $('.js-scroll').click(function(){
        var headerHeight = 60;

        $('html, body').animate({
            scrollTop: $( $.attr(this, 'href') ).offset().top - headerHeight
        }, 500);
        return false;
    });