jQuery / Ein Menü beim Scrollen oben fixieren
Vergleichen Sie die Dokument-Scrollposition mit dem ursprünglichen Menüort und aktivieren Sie ab dort eine Klasse mit position:fixed. Oberhalb entfernen Sie sie. Lassen Sie am alten Ort gleich hohe Fläche stehen, damit der Inhalt beim Verlassen des normalen Flusses nicht hochspringt.
Stabile Grenze und Platzhalter
CSS position:fixed verlässt den Dokumentfluss und richtet sich gewöhnlich am Viewport aus. top:0 ist dessen Oberkante. Der alte Ort ist eine Dokumentkoordinate; die Vergleichskoordinaten müssen zusammenpassen.
Messen Sie nicht die bereits fixierte Menüposition als ursprünglichen Ort. #menu-slot bleibt im Dokument. Sein Viewport-top plus window.scrollY ergibt die Dokumentgrenze.
Ein fixiertes Menü übernimmt die Elternbreite möglicherweise anders. Setzen Sie left und width vom Slot und erhalten Sie dessen Platz mit der Rahmenhöhe des Menüs. Ohne Margin und mit border-box stimmen die Grenzen überein.
Vollständiges Scroll- und Resize-Beispiel
Speichern Sie alles als UTF-8 fixed-menu-demo.html und öffnen Sie es im Browser. Internet lädt die offizielle jQuery-Datei 4.0.0. Kein Server und keine Installation sind nötig. Grenzwechsel, Inhaltsposition, Größenwechsel und Anker wurden in Windows Edge mit 3.7.1 und 4.0.0 geprüft.
Der Kopf ist 180px hoch; die Anfangsgrenze liegt bei Dokumentposition 180. Untere Steuerung und Status sind Übungshilfen, keine notwendigen Seitenelemente.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>A menu fixed after its original position</title>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<style>
:root { --menu-height: 60px; }
html { scroll-behavior: auto; }
body { margin: 0; font: 16px/1.5 sans-serif; }
header { height: 180px; display: grid; place-items: center; background: #e7f2f2; }
.wrap { width: 100%; max-width: 800px; padding: 0 20px; margin: 0 auto; box-sizing: border-box; }
#menu { box-sizing: border-box; width: 100%; display: flex; flex-wrap: wrap; gap: 12px; padding: 12px; border: 2px solid teal; background: #fff; }
#menu a { padding: 4px; color: #075c55; }
#menu.is-fixed { position: fixed; top: 0; z-index: 20; box-shadow: 0 3px 8px #0003; }
main section { min-height: 650px; padding-top: 24px; }
main h2 { margin: 0; scroll-margin-top: calc(var(--menu-height) + 16px); }
#controls { position: fixed; right: 8px; bottom: 8px; z-index: 30; background: #fff; padding: 4px; }
button { font: inherit; margin: 2px; }
#result { position: fixed; left: 8px; bottom: 70px; background: #fff; white-space: pre-wrap; padding: 6px; z-index: 30; }
</style>
</head>
<body>
<header>Header before the menu</header>
<div class="wrap"><div id="menu-slot">
<nav id="menu" aria-label="Sections"><a href="#alpha">Alpha section</a><a href="#beta">Beta section</a><a href="#gamma">Gamma section</a></nav>
</div></div>
<main class="wrap">
<section><h2 id="alpha">Alpha</h2><p>First section content.</p></section>
<section><h2 id="beta">Beta</h2><p>Second section content.</p></section>
<section><h2 id="gamma">Gamma</h2><p>Third section content.</p></section>
</main>
<div id="controls"><button type="button" id="down">Go to 300px</button><button type="button" id="reset">Back to 0px</button></div>
<pre id="result" aria-live="polite"></pre>
<script>
$(function () {
const $menu = $("#menu"), $slot = $("#menu-slot");
let scheduled = false;
function update() {
scheduled = false;
const rect = $slot[0].getBoundingClientRect();
const threshold = rect.top + window.scrollY;
const fixed = $(window).scrollTop() >= threshold;
$menu.toggleClass("is-fixed", fixed).css({
left: fixed ? rect.left + "px" : "",
width: fixed ? rect.width + "px" : ""
});
const height = $menu.outerHeight();
$slot.css("height", height + "px");
document.documentElement.style.setProperty("--menu-height", height + "px");
$("#result").text(`menu=${fixed ? "fixed" : "flow"}\nthreshold=${Math.round(threshold)}\nscrollY=${Math.round(window.scrollY)}`);
}
function schedule() {
if (!scheduled) { scheduled = true; window.requestAnimationFrame(update); }
}
$(window).on("scroll.fixedMenuLesson resize.fixedMenuLesson load.fixedMenuLesson", schedule);
$("#down").on("click", function () { window.scrollTo(0, 300); schedule(); });
$("#reset").on("click", function () { window.scrollTo(0, 0); schedule(); });
update();
});
</script>
</body>
</html>
Anfangsausgabe oben. flow bezeichnet normale Anordnung.
menu=flow
threshold=180
scrollY=0
- Langsam scrollen: Bei 179px normal, ab 180px fixiert an top=0.
- Go to 300px ergibt menu=fixed, threshold=180, scrollY=300. Inhalt darf nicht um die Menühöhe hochspringen.
- Fixiert breiter und schmaler stellen: linke Kante und Breite folgen dem Inhaltscontainer, Umbrüche ändern die Platzhalterhöhe.
- Beta section folgt dem echten #beta-Anker und lässt dessen Überschrift sichtbar. Links bleiben fokussierbar und normal per Tastatur bedienbar.
- Back to 0px stellt Fluss wieder her und leert festes left und width. Ein vorhandener URL-Hash wird dabei nicht gelöscht.
Aktualisierung und Ankerabstand
$(window).scrollTop() wird mit dem Slot verglichen. .toggleClass("is-fixed", fixed) setzt den booleschen Zustand ausdrücklich. Einargumentiges Toggle bei jedem Ereignis würde die Klasse nur abwechseln.
scroll, resize und load planen update() mit requestAnimationFrame() vor dem nächsten Zeichnen. scheduled verhindert mehrere offene Rückrufe. Das garantiert weder Millisekundenabstand noch Bildrate; halten Sie Lesen und Änderungen leichtgewichtig.
Erst die Breite, dann die Höhe berücksichtigt umgebrochene Links. Ein initiales update() behandelt wiederhergestellte Scrollpositionen. Bei später wachsendem Inhalt oder Schriftänderungen verbinden Sie schedule() mit deren Abschluss; nicht jede DOM-Änderung wird automatisch beobachtet.
--menu-height plus 16px setzt scroll-margin-top der Ziele. So verdeckt das Menü keine normalen Ankerüberschriften. Andere feste Kopfzeilen erfordern passende top-Werte und Zielabstände. Behalten Sie echte href und verhindern Sie Navigation nicht unnötig.
CSS sticky und Einsatzprüfung
Für einfaches Haften oben kann position:sticky mit top ohne JavaScript ausreichen. Es erhält Platz und wird durch Scrollcontainer und enthaltenden Block begrenzt. Prüfen Sie kurze Eltern und overflow-Vorfahren; sticky ist nicht identisch mit fixed.
Das Beispiel scrollt das Dokument, keinen inneren Bereich. Transformierte Vorfahren können den Bezug von fixed verändern. Untersuchen Sie die Struktur, statt globale position- oder overflow-Regeln zu überschreiben.
Ein hoher z-index löst nicht alle Modals oder Stapelkontexte. Prüfen Sie mobilen Inhalt, sichtbaren Fokus und Bedienbarkeit beim Zoom. Entfernen oder versetzen Sie Übungsüberlagerungen auf echten Seiten.
Beim Abbau müssen neben Ereignisnamen auch Klasse, left/width, Platzhalterhöhe und eigene Eigenschaft im Zuständigkeitsbereich zurückgesetzt werden. Wiederholte App-Initialisierung braucht Lebenszyklusverwaltung gegen doppelte Handler.




