it-swarm.com.de

Versetzen eines HTML-Ankers zum Anpassen des festen Headers

Ich versuche aufzuräumen, wie meine Anker funktionieren. Ich habe eine Kopfzeile, die oben auf der Seite fixiert ist. Wenn Sie also auf einen Anker an einer anderen Stelle auf der Seite verlinken, springt die Seite, sodass der Anker oben auf der Seite ist und der Inhalt hinter der fixierten Kopfzeile bleibt (hoffe ich) Das macht Sinn). Ich brauche eine Möglichkeit, den Anker um die 25px von der Höhe des Headers zu versetzen. Ich würde HTML oder CSS bevorzugen, aber Javascript wäre auch akzeptabel.

1011
Matt Dryden

Sie können CSS auch ohne Javascript verwenden.

Geben Sie Ihrem Anker eine Klasse:

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

Sie können den Anker dann um einen Versatz höher oder niedriger als auf der Seite positionieren, indem Sie ihn zu einem Blockelement machen und relativ dazu positionieren. -250px positioniert den Anker um 250px

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

Ich habe diese Lösung gefunden:

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

Dies erzeugt keine Lücke im Inhalt und Ankerverbindungen funktionieren wirklich gut.

316
Alexander Savin

Da dies ein Anliegen der Präsentation ist, wäre eine reine CSS-Lösung ideal. Diese Frage wurde jedoch im Jahr 2012 gestellt, und obwohl Lösungen für relative Positionierung/negative Margen vorgeschlagen wurden, scheinen diese Ansätze ziemlich verwerflich zu sein, verursachen potenzielle Flussprobleme und können nicht dynamisch auf Änderungen im DOM/Ansichtsfenster reagieren.

Vor diesem Hintergrund glaube ich, dass die Verwendung von JavaScript nach wie vor (Februar 2017) der beste Ansatz ist. Im Folgenden finden Sie eine Vanilla-JS-Lösung, die sowohl auf Ankerklicks als auch auf das Auflösen des Seitenhashs beim Laden reagiert. (Siehe JSFiddle) . Ändern Sie die Methode .getFixedOffset(), wenn dynamische Berechnungen erforderlich sind. Wenn Sie jQuery verwenden, hier ist eine modifizierte Lösung mit besserer Ereignisdelegierung und flüssigem Scrollen .

(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);
154
Ian Clark

Auch dafür suchte ich eine Lösung. In meinem Fall war es ziemlich einfach.

Ich habe ein Listenmenü mit allen Links:

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

Und darunter die Überschriften, in die es gehen soll.

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

Da ich jetzt oben auf meiner Seite ein festes Menü habe, kann ich es nicht einfach so machen, dass es zu meinem Tag wechselt, da sich das hinter dem Menü befinden würde.

Stattdessen habe ich ein span-Tag mit der richtigen ID in mein Tag eingefügt.

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

Verwenden Sie jetzt 2 Zeilen CSS, um sie richtig zu positionieren.

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

Ändern Sie den oberen Wert so, dass er der Höhe Ihrer festen Kopfzeile (oder mehr) entspricht. Nun gehe ich davon aus, dass dies auch mit anderen Elementen funktionieren würde.

141
Hrvoje Miljak

FWIW das hat bei mir funktioniert:

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

Reine CSS-Lösung, inspiriert von Alexander Savin:

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

Optional können Sie Folgendes hinzufügen, wenn das Ziel noch nicht auf dem Bildschirm angezeigt wird:

  vertical-align: top;
83
Ziav

Dabei werden viele Elemente aus vorherigen Antworten übernommen und zu einer winzigen (auf 194 Byte reduzierten) anonymen jQuery-Funktion kombiniert. Passen Sie fixedElementHeight an die Höhe Ihres Menüs oder Sperrelements an.

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

Wenn Ihnen die Animation nicht gefällt, ersetzen Sie sie

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

mit:

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

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

Meine Lösung kombiniert die Ziel- und Vorwähler für unser CMS. Andere Techniken berücksichtigen keinen Text im Anker. Passen Sie die Höhe und den negativen Rand an den gewünschten Versatz an ...

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

Ich hatte ein ähnliches Problem, leider kam ich nach der Implementierung aller oben genannten Lösungen zu folgendem Ergebnis.

  1. Meine inneren Elemente hatten eine fragile CSS-Struktur und die Implementierung eines positionsrelativen/absoluten Spiels brach das Seitendesign vollständig.
  2. CSS ist nicht meine Stärke.

Ich habe dieses einfache Bildlauf-js geschrieben, das den Versatz berücksichtigt, der durch den Header verursacht wird, und das div etwa 125 Pixel darunter verschoben. Bitte verwenden Sie es, wie Sie es für richtig halten.

Das HTML

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

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

Siehe eine Live-Implementierung hier .

30
Shouvik

Fügen Sie für moderne Browser einfach die Zielauswahl CSS3: zur Seite hinzu. Dies gilt automatisch für alle Anker.

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

Sie können es ohne js und ohne Änderung von HTML tun. Es ist nur CSS.

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

Das wird ein Pseudoelement vor jedem A-Tag mit einer ID anhängen. Passen Sie die Werte an die Höhe Ihres Headers an.

26
Zsolt Szilagy

Wie @moeffju andeutet, kann dies mit CSS erreicht sein. Das Problem, auf das ich gestoßen bin (was mich wundert, dass ich es nicht besprochen habe), ist der Trick, vorherige Elemente mit Auffüllungen oder einem transparenten Rand zu überlappen z-Reihenfolge.

Die beste Lösung, die ich gefunden habe, war das Platzieren von Abschnittsinhalten in einem div, das sich in z-index: 1 befindet:

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

Lösungen mit sich ändernden Positionseigenschaften sind nicht immer möglich (es kann das Layout zerstören), daher schlage ich Folgendes vor:

HTML:

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

CSS:

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

Benutze das:

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

um Überlappungen zu minimieren und die Schriftgröße auf 1px einzustellen. Leerer Anker funktioniert in einigen Browsern nicht.

10
user2475125

Für das gleiche Problem habe ich eine einfache Lösung verwendet: Auf jeden Anker eine Polsterung von 40px setzen.

9
odupont

Wenn Sie sich einen Teil des Codes aus einer Antwort ausleihen nter diesem Link (es ist kein Autor angegeben), können Sie dem Anker einen netten Smooth-Scroll-Effekt hinzufügen, während dieser bei -60px über dem Anker anhält. passt gut unter die feste bootstrap Navigationsleiste (benötigt 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

Ich bin auf dasselbe Problem gestoßen und habe am Ende die Klickereignisse manuell behandelt, wie zum Beispiel:

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

Scroll-Animation natürlich optional.

3
jean

Die oben genannten Methoden funktionieren nicht sehr gut, wenn Ihr Anker ein Tabellenelement oder eine Tabelle (Zeile oder Zelle) ist.

Ich musste Javascript verwenden und an das window hashchange -Ereignis binden, um dies zu umgehen ( 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();
    });

* Hinweis: Das Ereignis hashchange ist nicht in allen Browsern verfügbar.

3
Mottie

Anstatt eine Navigationsleiste mit fester Position zu haben, die vom Rest des Seiteninhalts überlagert wird (wobei der gesamte Seitenhauptteil scrollbar ist), sollten Sie stattdessen einen nicht scrollbaren Hauptteil mit einer statischen Navigationsleiste und dann den Seiteninhalt in einer Navigationsleiste haben Absolut positioniertes scrollbares Div.

Das heißt, HTML wie folgt haben ...

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

... und CSS gefällt das:

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

Dadurch wird das gewünschte Ergebnis auf einfache, nicht hackige Weise erzielt. Der einzige Unterschied zwischen diesem und einigen der oben vorgeschlagenen cleveren CSS-Hacks besteht darin, dass die Bildlaufleiste (in Browsern, die eine rendern) an den Inhalt div und nicht an die gesamte Höhe der Seite angehängt wird. Sie können dies als wünschenswert erachten oder nicht.

Hier ist eine JSFiddle, die dies in Aktion demonstriert.

2
Mark Amery

Dies wurde durch die Antwort von Shouvik inspiriert - dasselbe Konzept wie bei ihm, nur die Größe des festen Headers ist nicht fest codiert. Solange sich Ihr fester Header im ersten Header-Knoten befindet, sollte dies "nur funktionieren".

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

Sie können dies ohne eine ID erreichen, indem Sie den CSS-Selektor a[name]:not([href]) verwenden. Dies sucht einfach nach Links mit einem Namen und ohne href, z. <a name="anc1"></a>

Eine Beispielregel könnte sein:

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

Dieses Problem tritt auf einer TYPO3-Website auf, auf der alle "Inhaltselemente" wie folgt umschlossen sind:

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

und ich habe das Rendering so geändert, dass es so rendert:

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

Und dieses CSS:

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

Da die feste obere Leiste 40 Pixel hoch ist, funktionieren die Anker jetzt wieder und beginnen 10 Pixel unter der oberen Leiste.

Der einzige Nachteil dieser Technik ist, dass Sie :target nicht mehr verwenden können.

1
lipsumar

Eine weitere Wendung zu der hervorragenden Antwort von @Jan besteht darin, diese in den festen Header #uberbar zu integrieren, der jQuery (oder MooTools) verwendet. ( http://davidwalsh.name/persistent-header-opacity )

Ich habe den Code so optimiert, dass der obere Rand des Inhalts immer unterhalb des festen Headers liegt. Außerdem wurden die Anker von @Jan erneut hinzugefügt, um sicherzustellen, dass die Anker immer unterhalb des festen Headers positioniert sind.

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

Die jQuery (einschließlich Änderungen an der #uberbar und am Anker) nähert sich:

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

Und zum Schluss das 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>

Vielleicht ist dies nützlich für jemanden, der den überblendbaren Header mag!

0
Dzseti

Als Ergänzung zu Ziavs Antwort (danke an Alexander Savin) muss ich <a name="...">...</a> der alten Schule verwenden, da wir <div id="...">...</div> für einen anderen Zweck in unserem Code verwenden. Ich hatte einige Anzeigeprobleme bei der Verwendung von display: inline-block - die erste Zeile jedes <p> -Elements wurde leicht nach rechts eingerückt (sowohl in Webkit- als auch in Firefox-Browsern). Am Ende habe ich andere display Werte ausprobiert und display: table-caption funktioniert perfekt für mich.

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

Ich habe ein .vspace -Element mit einer Höhe von 40px hinzugefügt, das den Anker vor jedem meiner h1 -Elemente enthält.

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

Im CSS:

.vspace { height: 40px;}

Es funktioniert großartig und der Platz drosselt nicht.

0
Quentin

wie wäre es mit versteckten Span-Tags mit verknüpfbaren IDs, die die Höhe der Navigationsleiste angeben:

#head1 {
  padding-top: 60px;
  height: 0px;
  visibility: hidden;
}


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

hier ist die Geige: http://jsfiddle.net/N6f2f/7

0
Pete

Sie können auch einen Anker mit folgendem Attribut hinzufügen:

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

und geben Sie dem übergeordneten Container eine relative Position.

Funktioniert perfekt für mich.

0
Sanjo Elstak