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

时间:2012-05-24 07:04:13

标签: javascript html css anchor offset

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

28 个答案:

答案 0 :(得分:1053)

你可以在没有任何javascript的情况下使用CSS。

给你的主持人上课:

<a class="anchor" id="top"></a>

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

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

答案 1 :(得分:305)

我找到了这个解决方案:

<a name="myanchor">
    <h1 style="padding-top: 40px; margin-top: -40px;">My anchor</h1>
</a>

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

答案 2 :(得分:151)

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

考虑到这一点,我相信使用JavaScript 仍然(2017年2月)是最好的方法。下面是一个vanilla-JS解决方案,它将响应锚点击并解决负载 (See JSFiddle) 上的页面哈希。如果需要动态计算,请修改.getFixedOffset()方法。如果你正在使用jQuery,here's a modified solution with better event delegation and smooth scrolling

(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);

答案 3 :(得分:137)

我也在寻找解决方案。就我而言,这很容易。

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

<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>

低于它应该去的标题。

<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>

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

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

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

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

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

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

答案 4 :(得分:96)

FWIW这对我有用:

*[id]:before { 
  display: block; 
  content: " "; 
  margin-top: -75px; 
  height: 75px; 
  visibility: hidden; 
}

答案 5 :(得分:81)

纯粹的css解决方案受到Alexander Savin的启发:

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

如果目标仍然不在屏幕上,您可以选择添加以下内容:

  vertical-align: top;

答案 6 :(得分:36)

这需要从以前的答案中获取许多元素,并将其组合成一个微小的(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);

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

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

使用:

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

Uglified version:

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

答案 7 :(得分:30)

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

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

    HTML

    <div id="#anchor"></div> <!-- #anchor here is the anchor tag which is on your URL -->
    

    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;
          }
        }
    });
    

    查看live implementation here

答案 8 :(得分:27)

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

:target {
    display: block;    
    position: relative;     
    top: -100px;
    visibility: hidden;
}

答案 9 :(得分:26)

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

:target:before {
    content: "";
    display: block;
    height: 180px;
    margin: -180px 0 0;
}

答案 10 :(得分:26)

你可以不用js而不用改变html。这只是css-only。

a[id]:before {
    content:"";
    display:block;
    height:50px;
    margin:-30px 0 0;
}

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

答案 11 :(得分:11)

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

我发现的最佳解决方案是将部分内容放在位于div的{​​{1}}中:

z-index: 1

答案 12 :(得分:9)

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

答案 13 :(得分:9)

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

HTML:

<a id="top">Anchor</a>

CSS:

#top {
    margin-top: -250px;
    padding-top: 250px;
}

使用此:

<a id="top">&nbsp;</a>

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

答案 14 :(得分:8)

从给定at this link的答案中借用一些代码(没有指定作者),你可以在锚点上包含一个漂亮的平滑滚动效果,同时使它停在锚点上方-60px处,很适合在固定的bootstrap导航栏下面(需要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(){
     });
});

答案 15 :(得分:3)

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

我必须使用javascript并绑定到窗口hashchange事件来解决此问题(demo):

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事件并非在所有浏览器中都可用。

答案 16 :(得分:3)

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

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

滚动动画当然是可选的。

答案 17 :(得分:2)

这是受到Shouvik的回答的启发 - 与他相同的概念,只有固定标题的大小不是硬编码的。只要您的固定标头位于第一个标头节点中,这应该&#34;只需工作&#34;

/*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();
});

答案 18 :(得分:2)

不要使用固定位置的导航栏(页面内容的其余部分可以滚动),而是考虑使用带有静态导航栏的不可滚动的主体,然后使用页面在下面绝对定位的可滚动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>

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

.static-navbar {
  height: 100px;
}
.scrollable-content {
  position: absolute;
  top: 100px;
  bottom: 0;
  overflow-y: scroll;
  width: 100%;
}

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

Here's a JSFiddle demonstrating this in action.

答案 19 :(得分:1)

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

<div id="c1234" class="contentElement">...</div>

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

<div id="c1234" class="anchor"></div>
<div class="contentElement">...</div>

这个CSS:

.anchor{
    position: relative;
    top: -50px;
}

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

此技术的唯一缺点是您无法再使用:target

答案 20 :(得分:0)

您还可以添加一个跟随attr:

的锚点
(text-indent:-99999px;)
visibility: hidden;
position:absolute;
top:-80px;    

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

适合我的作品。

答案 21 :(得分:0)

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

示例规则可能是:

a[name]:not([href]){
    display: block;    
    position: relative;     
    top: -100px;
    visibility: hidden;
}

答案 22 :(得分:0)

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

#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

答案 23 :(得分:0)

我在每个.vspace元素之前添加了40px-height h1元素。

<div class="vspace" id="gherkin"></div>
<div class="page-header">
  <h1>Gherkin</h1>
</div>

在CSS中:

.vspace { height: 40px;}

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

答案 24 :(得分:0)

添加Ziav的答案(感谢Alexander Savin),我需要使用旧学校<a name="...">...</a>,因为我们在代码中将<div id="...">...</div>用于其他目的。我使用display: inline-block时出现了一些显示问题 - 每个<p>元素的第一行显示为略微缩进(在Webkit和Firefox浏览器上)。我最终尝试了其他display值,而display: table-caption对我来说非常适合。

.anchor {
  padding-top: 60px;
  margin-top: -60px;
  display: table-caption;
}

答案 25 :(得分:0)

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

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

CSS:

#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和锚点方法的调整:

<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)});
    })();
});
</script>

最后是HTML:

<div id="uberbar">
    <!--CONTENT OF FIXED HEADER-->
</div>
....
<div id="content">
    <!--MAIN CONTENT-->
    ....
    <a class="anchor" id="anchor1"></a>
    ....
    <a class="anchor" id="anchor2"></a>
    ....
</div>

对于喜欢#uberbar fading dixed header的人来说,这可能很有用!

答案 26 :(得分:-1)

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

// SCROLL ON CLICK
// --------------------------------------------------------------------------
$('.js-scroll').click(function(){
    var headerHeight = 60;

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

答案 27 :(得分:-1)

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

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

a:target {
  padding-top: 40px
}

请注意,此格式不适用于Chrome / Safari,因此您可能不得不使用css-hacks,条件评论等。

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

即可
<div id="myanchor" style="display: inline">
   <h1 style="padding-top: 40px; margin-top: -40px;">My anchor</h1>
</div>