OnePager
Single-Page-Webseiten oder OnePager (auch als pageless Design bekannt) sind gerade für Web-Visitenkarten, Landing Pages und kleinere Projekte perfekt geeignet.
Bei Single-Page-Webseiten wird der Inhalt nicht auf mehrere kurze Seiten verteilt, sondern auf einer einzigen "langen" Seite sofort geladen und durch Scrollen oder Navigieren ohne weiteren HTTP-Request eingeblendet.
Allerdings wird dieser potentielle Geschwindigkeitsvorteil durch den Einsatz von Frameworks oft in sein Gegenteil verkehrt, wenn erst mehrere MB geladen werden müssen, um die Funktionalität einer einzelnen Seite herzustellen. In diesen Beispielen sehen Sie, wie Sie eine solche Seite nur mit CSS realisieren können.
Inhaltsverzeichnis
Vorüberlegungen
OnePager sind sinnvoll, wenn Sie einen klar umrissenen Inhalt von einigen Abschnitten haben. Das kann eine Landing Page, eine Web-Visitenkarte oder eine Präsentation eines neuen Produkts oder einer Recherche sein. Umfangreichere Webprojekte oder nicht thematisch zusammengehörende Seiten sollten besser mit klassischen Einzelseiten realisiert werden.[1][2][3]
Die Bedienung eines One-Pagers sollte so einfach wie möglich sein. Das Scrollen mit Maus und Wischgeste führt die Benutzer intuitiv zu weiteren Inhalten. Trotzdem sollte es in den einzelnen Bereichen Wegweiser für die weitere Nutzung geben - besonders wenn es mehrere Möglichkeiten gibt - wie z. B. ein mögliches horizontales Scrollen zur Seite.
HTML-Grundstruktur
Diese Webseite besteht aus mehreren Seiten(bereichen), die in section
-Elementen untereinander notiert sind. Diese werden mit einer eindeutigen id
ausgezeichnet.
<section id="part_1">
<h2>Willkommen bei der Schreinerei Meier im Internet!</h2>
...
</section>
<section id="part_2">
<h2>Unsere Geschichte:</h2>
...
</section>
<section id="part_3">
<h2>Unsere nur zu Beispielzwecken erdachten Phantasie-Preise</h2>
...
...
<nav>
<ul id="navigation">
<li><a href="#part_1">Startseite</a></li>
<li><a href="#part_2">Geschichte</a></li>
<li><a href="#part_3">Preise</a></li>
</ul>
</nav>
Die Navigation besteht aus internen Ankern, , die auf diese id-Anker referenzieren.
Dateiorganisation
Sollte CSS und JavaScript im Dokument integriert werden oder in Einzeldateien in Unterverzeichnissen angelegt werden?
Für die Unterverzeichnisse spricht …
- die spätere Erweiterbarkeit mit Unterseiten (die bei einem OnePager ja eigentlich unnötig, aber nicht ausgeschlossen ist)
- dass ausgelagertes CSS und JavaScript bei schnellen DSL-Verbindungen schon parallel zum HTML geladen wird.
Für die Kompaktvariante spricht,
- dass die Latenzzeiten in Mobilfunknetzen bei mehreren Dateien zu einem verzögertem Laden führen, so dass es vorteilhaft ist, alles zusammen in einem HTTP-Request zu haben.
Auch der Aufbau des Frickl-Beispiels wird in der Kompaktvariante einfacher.
One-Pager mit Scroll Snap
Information: Smooth Scrolling
Benutzer, die auf mobilen Geräten mit Touchscreen surfen, sind es gewohnt weitere Inhalte mit einem Wisch in den sichtbaren Bereich zu holen. Deshalb sollen die einzelnen Sektionen im nächsten Beispiel untereinander in ihrer normalen Reihenfolge angeordnet werden. Ein Wisch (oder eine Mausbewegung oder ein Klick in der Navigation) verschiebt den Inhalt und führt zum nächsten Abschnitt. Schön wäre, wenn nicht impulsiv bis zum Seitenende gescrollt wird, sondern an der nächsten Überschrift automatisch angehalten werden kann.
Diese Technologie nennt sich Scroll Snap. Beim Scrollen rastet der Browser an bestimmten Stellen im Layout ein, wenn über diese Punkte hinweg gescrollt werden soll. Während dies früher den Einsatz von JavaScript und jQuery erforderte, reicht heute allein CSS.
Das HTML-Markup besteht wie im vorherigen Beispiel aus einer Navigation und mehreren Abschnitten innerhalb von section-Elementen. Die section-Elemente erhalten zusätzlich einen tabindex, damit sie auch mit der Tastatur angesteuert werden können.[4]
Scroll Snap benötigt einen Scroll-Container, in dem gescrollt werden soll und mehrere Kindelemente, die verschiebbar sein sollen.
@media (prefers-reduced-motion: no-preference) {
body {
margin: 0;
scroll-snap-type: y mandatory;
scroll-behavior: smooth;
scroll-padding: 0; /* nicht nötig */
}
section {
height: 100vh;
scroll-snap-align: start;
scroll-margin: 0; /* nicht nötig */
}
}
Der body hat 2 neue Regelsätze:
- scroll-snap-type hat 2 Parameter:
-
y
legt fest, dass der Scroll-Container in y-Richtung, also nach unten gescrollt wird. -
mandatory
legt fest, dass am nächsten Fangpunkt eingerastet wird.
-
- scroll-behavior spezifiziert das Scroll-Verhalten einer Scroll-Box, wenn ein Scroll-Event durch eine Navigation ausgelöst wird. Dass heißt, wenn ein interner Seitenanker auf eine section-klick, kommt es nicht zu einem plötzlichen Übergang, sondern ebenfalls zu einem Runterscrollen.
- scroll-padding legt einen (möglichen) Innenabstand innerhalb des Scroll-Containers fest.
Die section-Elemente haben ebenfalls neue Eigenschaften:
- scroll-snap-align legt mit dem Wert
start
fest, dass sich die Fangposition am Anfang der Scroll-Box befindet. - scroll-margin legt einen (möglichen) Außenabstand außerhalb des Fangpunktes der Scroll-Box fest.
Diese Regelsätze sind innerhalb einer Medienabfrage verschachtelt.
- Sind keine Festlegungen für prefers-reduced-motion getroffen, wird gescrollt.
- Falls der Benutzer dies in seinem Betriebssystem so angegeben hat, wird auf das Scollen verzichtet
(→ Der Benutzer erhält die Kontrolle, wie die Webseite bei ihm animiert wird.)
Exkurs: Bildergalerie mit horizontalem Scrollen
Diese Bildergalerie lässt sich entlang der X-Achse horizontal scrollen. Dafür war früher einiges an JavaScript notwendig - heute benötigt man nur wenige Zeilen CSS:
.gallery {
display: flex;
overflow-y: hidden;
scroll-snap-type: x mandatory;
}
.gallery img {
scroll-snap-align: center;
}
Alle Elemente, die sich im Container-Element mit der Klasse gallery
befinden, werden nun horizontal angeordnet:
- display:flex: erzeugt einen flexiblen Container, in dem sich alle Kindelemente (die Bilder) gleichmäßig verteilen
- overflow-y: hidden: sorgt, dafür, dass außerhalb des Containers befindliche Elemente nicht angezeigt werden.
Für den Scroll Snap benötigen sowohl der Container als auch die scrollenden Elemente jeweils nur eine Festlegung:
- scroll-snap-type für den Scroll-Container
-
x
Das Scrollen soll entlang der X-Achse, also horizontal erfolgen -
mandatory
erzwingt das Einschnappen an der Fangposition
-
- scroll-snap-align legt für die Kindelemente fest, wo sich die Fangposition befindet
-
center
Die Fangposition befindet sich in der Mitte der Scrollelemente, die Bilder werden nach dem Scroll Snap also zentriert angezeigt
-
Fazit
Die hier nur mit CSS realisierten Alternativen sind voll funktionsfähig. Allerdings lassen sich einige Extras wie Weiter-Links nur mit vielen Zeilen CSS realisieren und sind dann schwer zu erweitern, bzw. fehlen wie das Lazy-Loading gegenüber einer mit JavaScript erweiterten Variante.
Komfort-Version mit JavaScript
In diesem Beispiel wollen wir mit JavaScript und der Intersection-Observer-API den aktuell sichtbaren Teil der Webseite identifizieren, dann den entsprechenden Seitenanker in der Navigation mit aria-current auszeichnen sowie Links zum nächsten Kapitel erzeugen. Bilder werden mit Lazy-Loading erst eingeblendet, wenn der Nutzer in den entsprechenden Seitenbereich scrollt.
Das HTML-Dokument bleibt unverändert. Nach den Regeln des Unobtrusive JavaScript wird dem Dokument durch JavaScript zusätzliche Funktionalität hinzugefügt.
Es ermittelt den sichtbaren Bereich der Webseite und kennzeichnet diesen und fügt Links zu benachbarten Abschnitten hinzu. Dies alles geschieht automatisch - bei einer Änderung des HTMLs ist keine Anpassung des Scripts nötig.
Intersection Observer API
Die Intersection Observer API ermöglicht es, ein Element zu beobachten und zu erkennen, wenn es einen bestimmten Punkt in einem Scroll-Container - oft (aber nicht immer) den Viewport - passiert und eine Callback-Funktion auslöst.[5]
Die Intersection Observer API soll verwendet werden, um in der Seitennavigation den Link zum gerade sichtbaren Abschnitt hervor zu heben. Dieses soll sowohl beim Anspringen durch Klick auf den Link als auch beim Scrollen erfolgen.
Dazu muss ein Observer definiert und den Sections zugeordnet werden:
const observer = new IntersectionObserver(function(entries) {
...
}, { threshold: 0.1 });
document.querySelectorAll("section").forEach(element => observer.observe(element));
Hierdurch wird die dem IntersectionObserver als Parameter mitgegebene anonyme Funktion immer dann aufgerufen, wenn sich die Sichtbarkeit des überwachten Elements ändert. Der zweite Parameter ( threshold
) gibt an, wie weit das Element in den Viewport ragen muss, bis der Observer reagiert, hier 10%.
Die Observerfunktion soll dann den Link zum Element, dessen Sichtbarkeit sich geändert hat, suchen und markieren. Die Suche erfolgt über die ID des section-Elements. Als Markierung wird dem Link das aria-current-Attribut mit dem Wert location
gegeben oder genommen.
Das Script sieht dann so aus:
const observer = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
const linkselector = `nav [href='#${entry.target.id}']`;
const linkelement = document.querySelector(linkselector);
if(linkelement) {
if(entry.isIntersecting) linkelement.parentNode.setAttribute("aria-current", "location");
else linkelement.parentNode.removeAttribute("aria-current");
}
});
}, { threshold: 0.1 });
document.querySelectorAll("section").forEach(element => observer.observe(element));
Durch die Verwendung des Attributs aria-current wird auch assistiven Technologien mitgeteilt, welcher Teil der Seite gerade angesprungen oder angescrollt wurde.
Damit der Link nun auch optisch hervorgehoben wird, wird dem CSS noch folgende Regel hinzugefügt:
[aria-current=location]:before {
content: "► ";
}
Zusätzlich zur Navigation sollten die jeweils vorhergehenden und folgenden Abschnitte mit Vor- und Zurück-Pfeilen verlinkt werden.
Bei unserer Schreinereiseite würden wir einen "nach oben"- und einen " nach unten"-Pfeil realisieren – in dieser Variante wird von links nach rechts gescrollt, deshalb erscheint ein Rechtspfeil:
// Bsp. folgt
ToDo (weitere ToDos)
Ich würde hier eine neue Funktion verwenden, die [aria-current=location] ausliest und mit Element.closest dann die id's der Nachbar-sections ausliest und dafür Links erstellt. Wir brauchen ja keine Linktexte, sondern nur Pfeile!
Variante mit Skip-to-Top-Link
Die oben vorgestelle Variante, die wir gleich weiter ausbauen, hat einen fixen header, der immer am oberen Rand sichtbar bleibt, auch wenn zum vorher unten liegenden Inhalt gescrollt wird.
Diese Variante soll ohne fixen Header gestaltet werden. Sobald die Navigation unsichtbar wird, soll ein Skip-To-Top-Link erscheinen, der nach oben zielt.
<a href="#willkommen" id="skip-to-top" hidden>skip to top</a>
Der Link wird mit dem hidden-Attribut ausgeblendet.
Information: Trennung von Inhalt, Darstellung und Verhalten
Geht so eine Lösung nur mit CSS?[6] - Hier hilft es, sich vom vermeintlich einfachen Lösungsweg - dem Verzicht auf JS - zu lösen.
- HTML ist für die Auszeichnung des Inhalts zuständig
- CSS sorgt für die Darstellung
- JS reagiert auf Verhalten des Nutzers
const topLink = document.querySelector('#skip-to-top');
const nav = document.querySelector('nav');
let observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (!entry.isIntersecting) {
topLink.hidden = false;
} else {
topLink.hidden = true;
}
});
}, {
threshold: [0]
});
observer.observe(nav);
Ein IntersectionObserver beobachtet mit der isIntersecting-Eigenschaft, ob sich der übergebende Parameter (das nav-Element) im sichtbaren Bereich befindet.
- trifft das nicht zu, wird dem skip-to-top-Link das hidden-Attribut entfernt
- ansonsten wieder hinzugefügt.
Der oben besprochene threshold
-Parameter wurde hier auf 0 gesetzt.
Lazy-Loading
Damit beim OnePager die Bilder erst geladen werden, wenn sie in den sichtbaren Bereich kommen, erhält das img-Element ein loading-Attribut. Es legt fest, wie externe Medien geladen werden sollen. [7]
<img src="img/cabinet.svg" alt="Kommode" loading="lazy" width="200" height="200">
...
Mit loading="lazy"
wird festgelegt, dass Medien außerhalb des sichtbaren Bereichs erst geladen werden, wenn der Benutzer scrollt. Zusätzlich sollte mit einer Breitenangabe der Patz für das Bild reserviert werden, damit die Seite nicht immer wieder neu gerendert werden muss.
Dies wollen wir mit ein bisschen CSS animieren:
<span class="lazyContainer">
<img src="img/cabinet.svg" alt="Kommode" loading="lazy">
</span>
.lazyContainer {
position:relative;
display:inline-block;
width: 100%;
aspect-ratio: 4/3;
overflow: hidden;
vertical-align: bottom;
}
.lazyImage img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 1;
transform: scale(1);
transition: all 0.5s;
}
ToDo (weitere ToDos)
URL-Anpassung
Über die Links der Navigation kommt man zu den internen Seitenankern auf die einzelnen „Seiten“. Dabei werden die URLs, bzw. Seitenanker in der History API gespeichert. Ein Klick auf den Zurück-Button des Browsers führt zum letzten geklickten Abschnitt, nicht zur letzen anderen Webseite.[9][10]
Dies könnte man auch mit JavaScript verfeinern.
Feinschliff mit CSS
Scrollbalken
Da die Seite so gestaltet ist, dass der Seitenkopf beim Scrollen stehen bleibt, und dieses dadurch realisiert wurde, dass nur der Main-Bereich scrollt, gibt es „hässliche“ Scrollbalken innerhalb der Seite. Diese lassen sich mit der scrollbar-Eigenschaft umgestalten. Allerdings ist die Browserunterstützung noch nicht einheitlich, so dass mit Prefixes gearbeitet werden muss:
main {
scrollbar-color: firebrick beige;
}
main::-webkit-scrollbar {
width: 1em;
height: 2em;
background-color: beige;
}
main::-webkit-scrollbar:hover {
border: thin solid lightgray;
border-radius: .5em 0 .5em .5em;
}
main::-webkit-scrollbar-thumb {
background: firebrick;
border-radius: .5em 0 .5em .5em;
}
Kleine Viewports
Auf kleinen Bildschirmen bleibt unter dem feststehenden Seitenkopf zu wenig Platz für den Seiteninhalt. Daher wird in diesem Fall beim Scrollen der Seitenkopf auf das Logo und die Navigationszeile reduziert. Hierzu ist noch etwas CSS und JavaScript nötig.
Im CSS wird über eine Mediaquery festgelegt, ab welcher Viewporthöhe der header ausgeblendet werden soll. Hier wird eine Custom Property definiert, die dann im JavaScript ausgewertet wird.
body {
--viewporthoehe: hoch;
}
@media (max-height: 30em) {
body {
--viewporthoehe: niedrig;
}
}
Im JavaScript wird dann ein Handler für das resize-Event notiert, der die im CSS definierte custom property --viewporthoehe
ausliest und bei kleinem Viewport einen Eventhandler für das scroll-Event notiert. Dieser Handler gibt oder nimmt dann dem Header abhängig von der Scrollposition die Klasse niedrig
.
// Bei geringer Viewporthöhe Teile des Headers ausblenden
const header = document.querySelector("header");
const main = document.querySelector("main");
function handleScroll(event) {
if(event.target.scrollTop > 10) {
header.classList.add("niedrig");
}
else {
header.classList.remove("niedrig");
}
}
function handleResize() {
if(getComputedStyle(document.body).getPropertyValue("--viewporthoehe")=="niedrig") {
main.addEventListener("scroll",handleScroll);
}
else {
header.classList.remove("niedrig");
main.removeEventListener("scroll", handleScroll);
}
}
window.addEventListener("resize", handleResize);
handleResize();
Dem css werden dann noch Regeln für diese Klasse hinzugefügt:
header.niedrig hgroup {
display: none;
}
nav:not(header.niedrig nav) {
grid-column: 1/-1;
}
Die fertige Seite
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Schreinerei Meier, Dingenskirchen</title>
<style>
body {
max-width:60em;
margin: 0 /*1em*/ auto;
background-color: beige;
}
header {
padding: 0 1em;
margin-top: 1em;
margin-bottom: 0em;
display: grid;
grid-template-columns: 6em 1fr;
}
header p {
top: 1.7em;
transform: rotate(-10deg);
border: thin solid;
padding: 0.5em;
border-radius: 0.2em;
}
header #backlink {
display:inline-block;
text-shadow: none;
color: transparent;
background: transparent;
background-size: contain;
width: 5em;
height: 3em;
}
hgroup {
display: flex;
flex-flow: row wrap;
align-items: start;
gap: 0 3em;
}
@media (max-width: 45em) {
hgroup p {
display: none;
}
}
footer {
display: grid;
grid-template-columns: 1fr 10em;
}
footer > * {
grid-column: -2 / -1;
}
h1 {
color: brown;
font-size: 2em;
font-weight: bold;
}
h2 {
background-color: firebrick;
color: beige;
padding: 0.5em;
font-family: sans-serif;
border-radius: 0 1em 1em 1em;
}
h3 {
font-size: 1.4em; /*2.6em;*/
color: firebrick;
background-color: LightSalmon ;
border: thin solid;
font-family: cursive;
text-align: center;
}
p {
color: brown;
font-size: 1.1em;
}
@media (min-width: 30em) {
#leistungen {
display: grid;
grid-template-columns: 1fr 12em;
}
#leistungen h3 {
grid-column: 1 / -1;
}
}
#angebot {
background-color: firebrick;
padding: 1em;
width: 10em;
text-align: center;
}
#angebot h4 {
font-size: 2em;
font-variant: small-caps;
color: lightyellow;
}
#angebot p {
color: white;
}
#produkte img {
max-width: 100%;
}
/* Für One-Pager */
body {
--viewporthoehe: hoch;
}
@media (max-height: 30em) {
body {
--viewporthoehe: niedrig;
}
}
body {
height: 100vh;
display: flex;
flex-direction: column;
}
header.niedrig hgroup {
display: none;
}
nav:not(header.niedrig nav) {
grid-column: 1/-1;
}
nav ul {
list-style: none;
padding-inline: 0;
display: flex;
flex-flow: row wrap;
gap: 1em 2em;
}
main {
overflow: auto;
padding: 0 .5em;
scrollbar-color: firebrick beige;
}
main::-webkit-scrollbar {
width: 1em;
height: 2em;
background-color: beige;
}
main::-webkit-scrollbar:hover {
border: thin solid lightgray;
border-radius: .5em 0 .5em .5em;
}
main::-webkit-scrollbar-thumb {
background: firebrick;
border-radius: .5em 0 .5em .5em;
}
[aria-current=location]:before {
content: "► ";
}
</style>
<script type="module">
"use strict";
// Prüfen, welche Section sichtbar ist, und link auf diese Section hervorheben
const observer = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
const linkselector = `nav [href='#${entry.target.id}']`;
const linkelement = document.querySelector(linkselector);
if(linkelement) {
if(entry.isIntersecting) linkelement.parentNode.setAttribute("aria-current", "location");
else linkelement.parentNode.removeAttribute("aria-current");
}
});
}, { threshold: 0.1 });
document.querySelectorAll("section").forEach(element => observer.observe(element));
// Bei geringer Viewporthöhe Teile des Headers ausblenden
const header = document.querySelector("header");
const main = document.querySelector("main");
function handleScroll(event) {
if(event.target.scrollTop > 10) {
header.classList.add("niedrig");
}
else {
header.classList.remove("niedrig");
}
}
function handleResize() {
if(getComputedStyle(document.body).getPropertyValue("--viewporthoehe")=="niedrig") {
main.addEventListener("scroll",handleScroll);
}
else {
header.classList.remove("niedrig");
main.removeEventListener("scroll", handleScroll);
}
}
window.addEventListener("resize", handleResize);
handleResize();
</script>
</head>
<body>
<header>
<a id="backlink" href="#willkommen"><img src="img/logo.svg" alt="Willkommen"></a>
<hgroup>
<h1>Schreinerei Meier</h1>
<p>ihre Werkstatt für gutes Wohnen!</p>
</hgroup>
<nav>
<ul>
<li><a href="#willkommen">Willkommen</a></li>
<li><a href="#preise">Unsere Preise</a></li>
<li><a href="#produkte">Bilder von unseren Produkten</a></li>
<li><a href="#kontakt">Kontakt und Impressum</a></li>
</ul>
</nav>
</header>
<main>
<section id="willkommen">
<h2>Willkommen bei der Schreinerei Meier im Internet!</h2>
<p>Wir sind seit vielen Jahren darauf spezialisiert, alle Kundenwünsche zu erfüllen. In unserer
Werkstatt produzieren wir selbst - mit Holz aus regionaler, nachhaltiger Forstwirtschaft.</p>
<section id="leistungen">
<h3>Unsere Leistungen:</h3>
<ul>
<li>Möbel nach Ihren Wünschen
<ul>
<li>Küchenmöbel</li>
<li>Regale und Schrankwände</li>
<li>Badezimmermöbel</li>
</ul>
</li>
<li>Haustüren</li>
<li>Gartenzäune</li>
<li>Reparaturen</li>
</ul>
<aside id="angebot">
<h4>Angebot</h4>
<p>Nächste Woche 10% auf alles!</p>
</aside>
</section>
<section>
<h3>Unsere Geschichte:</h3>
<p>Die Anfänge unserer Firma reichen bis ins Mittelalter zurück, als Horst Holzmann begann seine
bisher für den Eigenbedarf hergestellten Möbel auch auf dem Markt der nächsten Stadt zu verkaufen.</p>
<p>Sein Sohn führte diese Tradition fort und nach ihm noch viele weitere Kinder und Kindeskinder.
Heute führt Schwiegersohn Harry Meier den Betrieb in der 15. Generation weiter und arbeitet bereits seinen Enkel als Nachfolger ein.</p>
</section>
</section>
<section id="preise">
<h2>Unsere nur zu Beispielzwecken erdachten Phantasie-Preise</h2>
<table>
<tr>
<th>Produkt</th>
<th>Preis</th>
</tr>
<tr>
<td>Tisch</td>
<td>50 €</td>
</tr>
<tr>
<td>Schrank</td>
<td>70 €</td>
</tr>
<tr>
<td>Bett</td>
<td>100 €</td>
</tr>
</table>
</section>
<section id="produkte">
<h2>Unsere Produkte</h2>
<p>Hier sehen Sie einen Überblick über unsere Angebote.</p>
<p>
<img src="img/cabinet.svg" alt="Kommode" loading="lazy">
<img src="img/dresser.svg" alt="Kommode mit Schubladen" loading="lazy">
<img src="img/filing-cabinet.svg" alt="Aktenschrank" loading="lazy">
<img src="img/table.svg" alt="Tisch" loading="lazy">
<img src="img/cupboard.svg" alt="Kleiderschrank" width="500" height="400" loading="lazy">
<img src="img/chair.svg" alt="Schaukelstuhl" loading="lazy">
<img src="img/desk.svg" alt="Schreibtisch" loading="lazy">
</p>
</section>
<section id="kontakt">
<h2>Impressum</h2>
<p>Angaben gem. § 5 TMG</p>
<dl>
<dt>Betreiber und Kontakt:</dt>
<dd>Schreinerei Meier</dd>
<dd>Möbelstr. 1</dd>
<dt></dt>
<dd>D-12345</dd>
<dt></dt>
<dd>Musterstadt</dd>
<dt></dt>
<dd>Germany</dd>
<dt>Tel:</dt>
<dd>+49 1234 5678</dd>
<dt>Fax:</dt>
<dd>+49 1234 5679</dd>
<dt>E-Mail-Adresse:</dt>
<dd><a href="mailto:test@example.com">test@example.com</a></dd>
</dl>
<dl>
<dt>Vertretung:</dt>
<dd>Die Schreinerei Meier wird rechtlich vertreten durch Herrn Harry Meier</dd>
</dl>
<h3>Berufsspezifische Angaben:</h3>
<p>Berufsbezeichnung: k. A. </p>
<p>Zuständige Kammer: IHK Bayern</p>
<p>Verliehen in/durch: k. A.</p>
<p>Folgende berufsrechtliche Regelungen finden Anwendung: k. A. <br>
Diese Regelungen können Sie einsehen unter: k. A. </p>
<p>Zuständige Aufsichtsbehörde: Finanzamt Musterstadt</p>
<p>Register und Registernummer: 999999 999999</p>
<p>Umsatzsteuer-ID: 999999 999999</p>
<p>Verantwortlicher für journalistisch-redaktionelle Inhalte gem. § 55 II RstV:<br />Hans Jürgen Mustermann</p>
<p>Quelle: <a href='http://www.deutsche-anwaltshotline.de/recht-auf-ihrer-website/impressum-generator'>Impressum-Generator</a> der Deutschen Anwaltshotline AG</p>
</section>
<aside>
<h3>Kontakt</h3>
<dl>
<dt>E-Mail-Adresse:</dt>
<dd>test@example.com</dd>
<dt>Tel:</dt>
<dd>+49 1234 5678</dd>
<dt>Fax:</dt>
<dd>+49 1234 5679</dd>
</dl>
</aside>
<footer>
<p>© 2021 by SELFHTML</p>
</footer>
</main>
</body>
</html>
Fazit
Hiermit haben wir einen eleganten und komfortablen OnePager für einen kleinen Webauftritt oder eine Produktseite.
Man könnte die Navigation auch dynamisch erzeugen, indem man mit JavaScript alle Kapitelüberschriften ausliest und in die Navigation einfügt.
→dynamisch erstelltes Inhaltsverzeichnis
Siehe auch
- Interactive Storytelling
im Aufbau
- Progressive Web-App
- Installierbarkeit
- Offline-Browsing
Einen anderen Ansatz verfolgen progressive Web Apps - eigentlich auch One-Pager. Jedoch werden dort die Seiteninhalte per JavaScript erzeugt und wieder entfernt.
Weblinks
- ↑ Jan Semler: Single-Page Website und wie man es richtig macht (28.01.2013)
- ↑ onepager.de (umfangreiche Erklärung aus Sicht der Werbeagenturen)
- ↑ In diesem Beispiel aus dem Jahre 2014 wird eine Seite durch einen Klick oder Tap auf den entsprechenden Verweis als Ziel (target) aktiviert und mit CSS über die strukturelle Pseudoklasse :target sichtbar gemacht.
OnePager mit :target ansehen …
body { position: relative; overflow: hidden; height: 100vh; } section { padding:3em 1em 1em; width:100%; height: 100vh; background: white; opacity: 0; position: absolute; top: 0; left: 0; transition: all 0.5s; } section:first-of-type { opacity: 1; transition: all 0.5s; } section:target { opacity: 1; }
- ↑ a11yproject.com: Scrollable Overflow containers
- ↑ MDN: Intersection Observer API
- ↑ Skip-To-Link nur mit CSS? -Ja!
Pure CSS Smooth-Scroll "Back to Top " von Stephanie Eckles - ↑ MDN: Lazy loading - Web Performance
- ↑ Enhancing HTML 5 Lazy Loading With CSS and Minimal JavaScript von Jason Knight, Oct 6, 2020
- ↑ developers.google.com: Grundlagen von JavaScript-SEO
History API anstelle von Fragmenten verwenden - ↑ Intelligent State Handling
- onepagelove: 430 One Pagers tagged with: CSS3