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)。如果需要动态计算,请修改
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; } |
我一直面临类似的问题,不幸的是,在实施了上述所有解决方案之后,我得出了以下结论。
我写了这个简单的滚动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顺序。
我找到的最佳解决方案是将节内容放在
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并绑定到窗口
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。
这种技术的唯一缺点是你不能再使用
您可以使用
示例规则可能是:
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
1 | Gherkin |
在CSS中:
1 | .vspace { height: 40px;} |
它工作得很好,而且空间不大。
添加Ziav的答案(感谢Alexander Savin),我需要使用旧式
元素的第一行显示为略微缩进(在Webkit和Firefox浏览器上)。我最终尝试了其他
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的解决方案适用于
我还必须使用:target伪类,它将样式应用于所选锚点以调整
1 2 3 | a:target { padding-top: 40px } |
请注意,此样式不适用于
另外我想注意亚历山大的解决方案是有效的,因为目标元素是
1 | <h1 style="padding-top: 40px; margin-top: -40px;">My anchor |
这是我们在我们网站上使用的解决方案。将
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; }); |