web-dev-qa-db-ja.com

固定ヘッダを調整するためのHTMLアンカーのオフセット

私は私のアンカーが働く方法をきれいにしようとしています。ページ上部に固定されたヘッダーがあるので、ページ内の他の場所にあるアンカーにリンクすると、ページがジャンプしてアンカーがページ上部に表示され、固定ヘッダーの後ろにコンテンツが残りますそれは理にかなっている)。ヘッダーの高さからアンカーを25ピクセル分オフセットする方法が必要です。私はHTMLかCSSを好むでしょうが、Javascriptも同様に受け入れられるでしょう。

968
Matt Dryden

JavaScriptを使わずにCSSを使うことができます。

あなたのアンカーにクラスを与えます:

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

その後、アンカーをブロック要素にして相対的に配置することで、アンカーをページ上の実際の表示位置よりも高いまたは低いオフセットに配置できます。 -250ピクセルはアンカーを250ピクセル上に配置します

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

私はこの解決策を見つけました:

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

これによってコンテンツにギャップが生じることはなく、アンカーリンクは本当にうまく機能します。

305
Alexander Savin

これはプレゼンテーションの問題であるため、純粋なCSSソリューションが理想的です。しかし、この問題は2012年に提起されたもので、相対的ポジショニング/ネガティブマージンの解決策が提案されていますが、これらのアプローチはかなり厄介で、潜在的なフローの問題を引き起こし、DOM /ビューポートの変更に動的に対応できません。

それを念頭に置いて、私はJavaScriptを使用することがstill(2017年2月)が最善のアプローチであると信じています。下記はVanilla-JSソリューションです。アンカークリックに応答し、loadのページハッシュを解決します(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);
149
Ian Clark

私もこれに対する解決策を探していました。私の場合は、とても簡単でした。

すべてのリンクを含むリストメニューがあります。

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

あなたの固定ヘッダの高さ(あるいはそれ以上)に合うように上の値を変更してください。今、私はこれが他の要素でもうまくいくと思います。

137
Hrvoje Miljak

これは私のために働いた:

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

Alexander Savinに触発された純粋なcssソリューション:

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

ターゲットがまだ画面から外れている場合は、オプションで以下を追加することができます。

  vertical-align: top;
81
Ziav

これは以前の答えから多くの要素を取り、小さな(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);

醜いバージョン:

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

私は同様の問題に直面していました、残念ながら上記のすべての解決策を実行した後、私は次の結論に至りました。

  1. 私の内側の要素は壊れやすいCSS構造を持ち、相対位置/絶対位置の位置関係を実装していましたが、ページデザインを完全に壊していました。
  2. CSSは私の強いスーツではありません。

私はこの単純なスクロールjsを書きました、それはヘッダーのために引き起こされるオフセットを考慮して、そして約125ピクセル下のdivを再配置しました。あなたが合うようにそれを使ってください。

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

ライブの実装はこちら を参照してください。

29
Shouvik

最近のブラウザでは、CSS3:ターゲットセレクタをページに追加するだけです。これは自動的にすべてのアンカーに適用されます。

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

あなたはjsなしでそしてhtmlを変えることなしにそれをすることができます。 CSSのみです。

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

それはidを持つすべてのa-tagの前に擬似要素を追加します。ヘッダの高さに合わせて値を調整します。

26
Zsolt Szilagy

私の解決策は私たちのCMSのためのターゲットと前セレクタを組み合わせたものです。他の手法ではアンカー内のテキストを考慮しません。必要なオフセットに合わせて高さと負の余白を調整します。

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

@moeffjuが示唆するように、これは CSSで達成することができます 。私が遭遇した問題(私がこれまで議論したことがなかったのは驚いたことですが)前の要素をパディングまたは透明なボーダーで重ね合わせることによるトリックです。 zオーダー.

私が見つけた最善の解決策は、セクションのコンテンツをz-index: 1divに配置することです。

// 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;
}
11
Kris Braun

同じ問題について、私は簡単な解決策を使用しました。各アンカーに40pxのパディングトップを追加します。

9
odupont

位置特性を変更する解決策は常に可能というわけではありません(それはレイアウトを破壊することができます)それ故に私はこれを提案します:

HTML:

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

CSS:

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

これを使って:

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

重なりを最小限にし、font-sizeを1pxに設定します。空のアンカーは一部のブラウザでは動作しません。

9
user2475125

このリンクでの回答からコードの一部を借用する (作成者は指定されていない)アンカーに-60pxで止まるようにしながら、アンカーにNice smooth-scroll効果を含めることができます。固定されたブートストラップナビゲーションバーのすぐ下(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(){
     });
});
8
Mark C Mitchell

私はこれと同じ問題に遭遇し、手動でクリックイベントを処理することになりました。

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

もちろんスクロールアニメーションはオプションです。

3
jean

上記の方法は、アンカーがテーブル要素である場合、またはテーブル(行またはセル)内にある場合はあまりうまくいきません。

この問題を回避するには、javascriptを使用してwindowの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 イベントはすべてのブラウザで利用できるわけではありません。

3
Mottie

ページの残りのコンテンツに重なっている固定位置ナビゲーションバーを使用する代わりに(ページ本体全体をスクロール可能にする)、代わりに固定ナビゲーションバーを使用してページコンテンツをスクロール可能にすることを検討します。下の絶対位置スクロール可能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ハックのいくつかの間の動作の唯一の違いはスクロールバー(それをレンダリングするブラウザで)はページの全高ではなくcontent divにアタッチされるということです。あなたはこれを望ましいと考えてもしなくてもよいです。

これが実際に動作していることを示すJSFiddleです。

2
Mark Amery

これはShouvikによる答えに触発されました - 彼と同じ概念、固定ヘッダのサイズだけがハードコードされていません。あなたの固定ヘッダが最初のヘッダノードにある限り、これは "ただ動く"はずです。

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

私はTYPO3ウェブサイトでこの問題に直面しています。そこでは全ての "Content Elements"が次のようなもので包まれています。

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

そして私はそれがこのようにレンダリングするようにレンダリングを変更しました:

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

そしてこのCSS:

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

固定トップバーの高さは40ピクセルで、アンカーは再び機能し、トップバーの下から10ピクセルの位置から開始します。

このテクニックの唯一の欠点はあなたがもはや:targetを使うことができないということです。

1
lipsumar

@Janからの優れた回答へのさらなるひねりは、jQuery(またはMooTools)を使用する#uberbar固定ヘッダにこれを組み込むことです。 ( 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フェージング混合ヘッダが好きな人には便利です!

0
Dzseti

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;
}
0
Prashant Tiwari

a[name]:not([href]) cssセレクタを使うと、IDなしでこれを実現できます。これは単に名前とhrefのないリンクを探すだけです。 <a name="anc1"></a>

規則の例は次のとおりです。

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

アンカーを保持する40px-heightの.vspace要素を各h1要素の前に追加しました。

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

CSSでは:

.vspace { height: 40px;}

それは素晴らしい仕事をしていて、スペースはぎっしり詰まっていません。

0
Quentin

ナビゲーションバーの高さを提供するリンク可能なIDを持つ隠しスパンタグはどうでしょうか。

#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

0
Pete

Follow attrを使ってアンカーを追加することもできます。

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

そして親コンテナに相対位置を与えます。

私にぴったりです。

0
Sanjo Elstak