jQuery fixed on top für Contao
Anleitung für Contao 5.3
Manche Elemente sollen einfach nicht mitscrollen: die Navigation, ein Suchfeld, die Telefonnummer im Kopfbereich – alles, was Besucher jederzeit greifbar haben sollen. „Fixed on top“ heißt das Muster, und es gehört zu den meistgewünschten Kleinigkeiten in Website-Projekten.
Für Contao existieren dafür zwei kompakte jQuery-Lösungen, die sich als Templates einbinden lassen: j_sticky_fixed_header.html5 und j_sticky_fixed_top.html5. Dieser Beitrag zeigt beide im Detail – und im Update weiter unten den moderneren Weg, der ganz ohne jQuery auskommt.
Funktionsweise der Scripte
1. Script j_sticky_fixed_header.html5
Dieses Script sorgt dafür, dass ein Element mit der Klasse .fixed-on-top nicht direkt am oberen Bildschirmrand fixiert wird, sondern unterhalb des Elements #header.
Der Ablauf im Detail:
- Das Script ermittelt die ursprüngliche Position des fixierten Elements sowie die Höhe des Headers (
#header). - Beim Scrollen prüft jQuery, ob der Scrollwert größer ist als die Ausgangsposition des Elements abzüglich der Headerhöhe.
- Falls ja, erhält das Element
.fixed-on-topdie zusätzliche CSS-Klasse.fixedund wird mittop: headerdirekt unterhalb des Kopfbereichs positioniert. - Gleichzeitig wird für das nachfolgende Element mit
.fixed-on-top-itemein Abstand in Höhe des fixierten Elements erzeugt. Dadurch verschiebt sich das Layout nicht unkontrolliert nach oben. - Wird wieder nach oben gescrollt, entfernt das Script alle gesetzten Klassen und Styles, sodass das Element an seiner ursprünglichen Position steht.
Einsatzgebiet:
Ideal für Webseiten mit einem festen Kopfbereich (Logo, Navigation), bei denen weitere Inhalte darunter dauerhaft sichtbar bleiben sollen.
<script>
$(document).ready(function() {
var top = $('.fixed-on-top').offset().top;
var header = $('#header').height();
var height = $('.fixed-on-top').height();
$(window).scroll(function(){
if ($(this).scrollTop() > top - header) {
$('.fixed-on-top').addClass('fixed');
$('.fixed-on-top').css('top', header);
$('.fixed-on-top-item').addClass('fixed');
$('.fixed-on-top-item').css('margin-top', height);
} else {
$('.fixed-on-top').removeClass('fixed');
$('.fixed-on-top').css('top', '');
$('.fixed-on-top-item').removeClass('fixed');
$('.fixed-on-top-item').css('margin-top', '');
}
});
});
</script>
2. Script j_sticky_fixed_top.html5
Dieses Script arbeitet ähnlich, verzichtet jedoch auf den Bezug zum Header. Das Element wird direkt am oberen Bildschirmrand fixiert.
Der Ablauf im Detail:
- Ausgangsposition und Höhe des Elements werden bestimmt.
- Sobald der Scrollwert größer ist als die ursprüngliche Position, wird das Element mit
.fixed-on-topfixiert. - Auch hier erhält das nachfolgende Element
.fixed-on-top-itemeinen Abstand, um Layout-Verschiebungen zu verhindern. - Beim Zurückscrollen wird das Element wieder an seiner ursprünglichen Stelle angezeigt.
Einsatzgebiet:
Geeignet für Inhalte, die unabhängig vom Header jederzeit am oberen Bildschirmrand sichtbar sein sollen – beispielsweise eine fixe Call-to-Action-Leiste oder ein Hinweisbanner.
<script>
$(document).ready(function() {
var top = $('.fixed-on-top').offset().top;
var height = $('.fixed-on-top').height();
$(window).scroll(function(){
if ($(this).scrollTop() > top ) {
$('.fixed-on-top').addClass('fixed');
$('.fixed-on-top').css('top');
$('.fixed-on-top-item').addClass('fixed');
$('.fixed-on-top-item').css('margin-top', height);
} else {
$('.fixed-on-top').removeClass('fixed');
$('.fixed-on-top').css('top', '');
$('.fixed-on-top-item').removeClass('fixed');
$('.fixed-on-top-item').css('margin-top', '');
}
});
});
</script>
Unterschiede im Überblick
| Script | Fixierung an Position | Typischer Anwendungsfall |
|---|---|---|
| j_sticky_fixed_header.html5 | Direkt unterhalb des #header |
Navigations- oder Funktionsleisten unterhalb der Hauptnavigation |
| j_sticky_fixed_top.html5 | Ganz oben am Bildschirmrand | Banner, Hinweisleisten, schmale Toolbars |
Einbindung in Contao
Die Integration in Contao erfolgt in drei einfachen Schritten:
- Template anlegen
- Im Contao-Template-Ordner eine Kopie von
j_sticky_fixed_header.html5oderj_sticky_fixed_top.html5erstellen. - Das gewünschte Script einfügen.
- Im Contao-Template-Ordner eine Kopie von
- Template einbinden
- Im Seitenlayout im Bereich jQuery-Templates das zuvor angelegte Template auswählen.
- Klasse vergeben
- Dem gewünschten Element im Inhaltselement die CSS-Klasse
.fixed-on-tophinzufügen. - Optional: Das nächste Element erhält die Klasse
.fixed-on-top-item, damit der Layoutfluss korrekt bleibt.
- Dem gewünschten Element im Inhaltselement die CSS-Klasse
CSS-Beispiel für die Fixierung
Damit die Funktion korrekt arbeitet, ist ein kurzes CSS notwendig:
.fixed {
position: fixed;
width: 100%;
z-index: 1000;
}
Dieses Beispiel fixiert das Element dauerhaft sichtbar, sorgt für volle Breite und garantiert mit z-index, dass es nicht von anderen Elementen überlagert wird.
Ohne jQuery: derselbe Effekt mit CSS und drei Zeilen JavaScript
Update 2026: Die gezeigte Lösung funktioniert weiterhin – setzt aber jQuery voraus. Für dieselbe Aufgabe braucht es heute weder eine Bibliothek noch einen Scroll-Listener: CSS erledigt den Hauptteil, ein paar Zeilen reines JavaScript den Rest.
Variante 1: reines CSS – position: sticky
.header {
position: sticky;
top: 0;
z-index: 50;
background: var(--bg, #fff);
}
/* Sprungmarken landen nicht mehr unter dem Header */
:target { scroll-margin-top: 6rem; }
/* Kein Ruckeln beim Umschalten des Zustands */
@media (prefers-reduced-motion: no-preference) {
.header { transition: box-shadow .2s ease, padding .2s ease; }
}Das genügt für den Standardfall: Der Header bleibt oben kleben, sobald er den oberen Rand erreicht – ohne Skript, ohne Layoutsprung, ohne Kosten für die Ladezeit. Der frühere jQuery-Ansatz musste die Höhe des Headers messen und einen Platzhalter einfügen; sticky braucht beides nicht.
Variante 2: Zustand erkennen, ohne Scroll-Listener
Wer den Header beim Scrollen optisch verändern will – kleiner, mit Schatten –, braucht einen Zustand. Ein Scroll-Listener feuert dabei hunderte Male pro Sekunde und ist der klassische Grund für schlechte INP-Werte. Der IntersectionObserver löst das eleganter:
const wache = document.createElement("div"); // unsichtbarer Wächter
wache.style.cssText = "position:absolute;top:0;height:1px;width:1px";
document.body.prepend(wache);
const header = document.querySelector(".header");
new IntersectionObserver(
([eintrag]) => header.classList.toggle("is-sticky", !eintrag.isIntersecting),
{ threshold: 0 }
).observe(wache);.header.is-sticky {
box-shadow: 0 2px 12px rgb(0 0 0 / .12);
padding-block: .4rem; /* etwas kompakter */
}Der Unterschied ist messbar: Statt bei jedem Scroll-Ereignis Rechenzeit im Hauptthread zu verbrauchen, meldet sich der Browser genau zweimal – beim Verlassen und beim Wiedererreichen der Startposition.
Was dabei oft vergessen wird
- Ein fixierter Header verdeckt bei Sprungmarken den Zielbereich –
scroll-margin-toplöst das ohne JavaScript. - Auf kleinen Bildschirmen frisst ein sticky Header wertvolle Höhe. Ab einer bestimmten Breite abschalten ist oft die bessere Entscheidung als ihn zu verkleinern.
- Wer die Höhe des Headers per JavaScript animiert, produziert Layoutverschiebungen (CLS). Änderungen gehören in
transformoder in feste Höhen. - jQuery nur für diesen Effekt zu laden, kostet rund 30 Kilobyte – bei einer Aufgabe, die CSS in vier Zeilen erledigt.
Fazit
Beide Wege führen zum Ziel – aber sie stammen aus verschiedenen Epochen. Die jQuery-Templates funktionieren zuverlässig und sind in bestehenden Contao-Installationen schnell nachgerüstet; wo jQuery ohnehin schon geladen wird, spricht wenig dagegen.
Bei neuen Projekten hat sich das Blatt gedreht: position: sticky erledigt den Standardfall in vier Zeilen CSS, der IntersectionObserver liefert den Rest ohne Scroll-Listener – schneller, sparsamer und ohne 30 Kilobyte Bibliothek im Gepäck. Die ehrliche Empfehlung lautet deshalb: Bestand pflegen, Neues ohne jQuery bauen.