Webrelevantes: VSCode, querySelector, VideoPlayer / 2022-04-05 / Matthias Edler-Golla, CC BY-SA 4.0



Themen heute

  • VSCode/VSCodium und gute Extensions
  • Emmet = weniger Tippen
  • Einbinden von Javascript
  • querySelector() und querySelectorAll()
  • Hotspots auf Bildern und Karten
  • Anzeige, wie weit man gescrollt hat
  • ein in Javascript erstellter Video-Player
  • Wireframes für die Website

VSCodium

https://vscodium.com/

Microsoft’s vscode source code is open source (MIT-licensed), but the product available for download (Visual Studio Code) is licensed under this not-FLOSS license and contains telemetry/tracking.

VSCodium ist identisch mit Visual Studio Code, enthält aber keine Software, die Telemetry/Tracking ermöglicht.

Installation

https://github.com/VSCodium/vscodium/releases

Mac-Datei, bei Windows weiß ich nicht, was Ihr nehmen müsst…

Visual Studio Code

Alternativ könnt Ihr ganz traditionell Visual Studio Code installieren!

Die Funktionsweise beider Programme ist komplett identisch!


VSCode/VSCodium Extensions

Ihr findet die Extensions, wenn Ihr direkt in VSCode/VSCodium danach sucht – siehe Screenshot

Folgende Extensions/Erweiterungen kann ich Euch empfehlen:

  1. Five Server <-- Live Server, zeigt Website beim Editieren im Browser, ähnlich wie Ihr es von Brackets kennt
  2. Prettier <-- formatiert automatisch Euren HTML/CSS/JS-Code, zeigt Fehler an
  3. vscode-icons <-- zeigen kleine Icons passend zu den jeweiligen Dateien, was zur Übersichtlichkeit beiträgt

VSCode/VSCodium Tutorial


Emmet = weniger tippen!

Emmet is a plugin for many popular text editors which greatly improves HTML & CSS workflow

Emmet ist bei VSCode/VSCodium schon eingebaut und kann Euch viel Tipperei ersparen!

Hier ein paar Beispiele

HTML

<!-- 
leeres HTML-Dokument, 
dann nur Ausrufezeichen und TAB-Taste drücken
-->
!

HTML

<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

</body>
</html>

HTML

<!-- lorem12 = 12 Blindtext-Worte -->
article>h2+p>lorem12

HTML

<article>
    <h2></h2>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Et adipisci placeat a?</p>
</article>

HTML

<!-- folgendes direkt in einem HTML-Dokument eingeben 
und dann die TAB-Taste drücken -->
nav>ul>li*5>a{Punkt $}

HTML

<nav>
    <ul>
        <li><a href="">Punkt 1</a></li>
        <li><a href="">Punkt 2</a></li>
        <li><a href="">Punkt 3</a></li>
        <li><a href="">Punkt 4</a></li>
        <li><a href="">Punkt 5</a></li>
    </ul>
</nav>

HTML

header>h1{Titel}+p>lorem5

HTML

<header>
    <h1>Titel</h1>
    <p>Lorem ipsum dolor sit amet.</p>
</header>

… und hier noch ein komplexes Beispiel:

HTML

<!-- 
    5 Artikel, Überschrift wird durchnummeriert ("$")
    dort jeweils ein Zufallsbild von "picsum.com" in der Größe 600px*400px
    Danach jeweils ein <p>-Element mit 12 Worten Zufallstext
    Beachtet, dass sich <p> auf der gleichenen Hierarchie-Ebene wie <figure> befindet,
    deswegen die 2 "^"-Zeichen, die "eine Ebene hochspringen" bedeuten
 -->
main>article*5>h2{Überschrift $}+figure>img[src="https://picsum.photos/600/400?random=$"]+figcaption>lorem5^^p>lorem12

HTML

<main>
    <article>
        <h2>Überschrift 1</h2>
        <figure>
            <img src="https://picsum.photos/600/400?random=1" alt="">
            <figcaption>Lorem ipsum dolor sit amet.</figcaption>
        </figure>
        <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Amet ducimus eius voluptate!</p>
    </article>
    <article>
        <h2>Überschrift 2</h2>
        <figure>
            <img src="https://picsum.photos/600/400?random=2" alt="">
            <figcaption>Sequi commodi sint odio quo.</figcaption>
        </figure>
        <p>Porro officiis iusto cumque accusamus natus assumenda facilis similique odio ea saepe?</p>
    </article>
    <article>
        <h2>Überschrift 3</h2>
        <figure>
            <img src="https://picsum.photos/600/400?random=3" alt="">
            <figcaption>Ea consectetur molestias unde optio!</figcaption>
        </figure>
        <p>Tempora, obcaecati animi sed dolorum cum maxime deserunt earum dolor! Ipsum, expedita.</p>
    </article>
    <article>
        <h2>Überschrift 4</h2>
        <figure>
            <img src="https://picsum.photos/600/400?random=4" alt="">
            <figcaption>Nobis exercitationem dolorem libero voluptatem.</figcaption>
        </figure>
        <p>Quasi sequi et totam iste sint assumenda? Cupiditate perferendis id maiores nihil.</p>
    </article>
    <article>
        <h2>Überschrift 5</h2>
        <figure>
            <img src="https://picsum.photos/600/400?random=5" alt="">
            <figcaption>Ab illum fugiat similique sapiente!</figcaption>
        </figure>
        <p>Corporis, rerum soluta exercitationem dolore error magni recusandae perferendis at officiis illo.</p>
    </article>
</main>

Detaillierte Dokumentation

docs.emmet.io


Emmet geht auch mit CSS

Emmet geht auch bei CSS – schaut Euch das Tutorial an!

CSS

/* am Ende wieder die TAB-Taste drücken!*/
article {
    w100p+ma75r+p0.5r+m3r-a
}

CSS

article {
    width: 100%;
    max-width: 75rem;
    padding: 0.5rem;
    margin: 3rem auto;
}

CSS

div {
    fz.90p+p2e1e+m1.5r0+bgc:#ccc+c:white
}

CSS

div {
    font-size: 0.9%;
    padding: 2em 1em;
    margin: 1.5rem 0;
    background-color: #ccc;
    color: white;
}

Einbinden von Javascript

vergleichbar mit dem Einbinden von CSS

HTML

<!-- direkt im HTML-Dokument -->
<script>
    // hier kommen die Javascript-Anweisungen direkt rein…
    function demo(){
        alert('Hallo Welt!');
    }
</script>

<!-- als externe Datei (meist die bessere Lösung!) -->
<script src="js/demo.js"></script>

je nach Situation kann das Einbinden im <head>- oder <body>-Bereich sinnvoll sein. Bei den hier gezeigten Beispielen ist Javascript jeweils am Ende des <body>-Bereichs eingebunden:

HTML

[…]
    <script src="js/script.js"></script>
  </body>
</html>

HTML-Elemente via Javascript ansprechbar machen

Um HTML-Elemente via Javascript ansprechen zu können, müssen diese zuerst in Variablen definiert werden.

HTML

<body>
    <main>
        <!-- soll via Javascript angesprochen werden  -->
        <div id="demo">
            […]
        </div>
    </main>
    <footer>
        <!-- soll via Javascript angesprochen werden  -->
        <button id="meinKnopf">[…]</button>
    </footer>
</body>

Javascript

// hiermit kann Javascript dann mit den HTML-Elementen arbeiten:
// die Auswahl funktioniert wie bei CSS
const hauptTeil = document.querySelector("main");
const fussKnopf = document.querySelector("footer button");

const demo = document.querySelector("#demo");
const meinKnopf = document.querySelector("#meinKnopf");

// Übersetzt: Wenn jemand auf "meinKnopf" klickt,
// soll "demo" die Klasse "betont" angefügt bekommen
meinKnopf.addEventListener("click", function() {
    demo.classList.add("betont");
});

Klasse via Javascript umschalten

Javascript schaltet nur eine Klasse an/aus. Den Rest (incl. Animationen) erledigt CSS!

Mit dem einfachen Ein- und Ausschalten von Klassen kommt Ihr schon sehr weit – schaut Euch dazu auch die weiteren Beispiele an!

Online-Demo

Schaut Euch die Demo an – und ladet Euch zum Experimentieren die Zip-Datei runter!

HTML

        […]
        <div id="demo">
            <h2>Überschrift</h2>
            <p>Lorem ipsum dolor…</p>
        </div>
        […]
        <footer>
            <button id="meinKnopf">Klasse ändern</button>
        </footer>
        <script src="js/script.js"></script>
    </body>
</html>

CSS

#demo {
    margin: 1.5em 0;
    transition: all 0.3s ease-in-out;
}

/*
diese Klasse ist nicht im HTML-Code enthalten!
sie wird via Javascript bei "div#demo" hinzugefügt und wieder entfernt
– also "getoggelt"
*/
.betont {
    font-size: 150%;
    padding: 1rem;
    border-radius: 10px;
    background-color: #ffea9a;
    transform: rotate(720deg);
}

Javascript

// der auslösende Button
const meinKnopf = document.querySelector("#meinKnopf");

// das DIV mit der ID "demo"
const demo = document.querySelector("#demo");

// hier das "toggle" – also abwechselnd Klasse "betont" dazu oder wieder weg…
meinKnopf.addEventListener("click", function() {
    demo.classList.toggle("betont");
});

Nachtmodus via Javascript

Um in den Nachtmodus umzuschalten, muss nur das <html>-Element die Klasse „nacht“ via Javascript zugewiesen bekommen – den Rest übernimmt wieder CSS!

Online-Demo

Schaut Euch die Demo an – und ladet Euch zum Experimentieren die Zip-Datei runter!

HTML

<header>
    <h1>Tag-/Nachtmodus</h1>
    <!-- Klicken auf den Button löst das Umschalten aus -->
    <button id="nachtansicht">Nachtansicht an/aus</button>
</header>

CSS

html {
    […]
    color: #444;
    background-color: #fff;

    /* damit werden die Übergänge zwischen Tag- und Nachtansicht animiert gezeigt! */
    transition: all 0.3s ease-in-out;
}

[…]

/* die Klasse "nacht" wird via Javascipt eingefügt! */
html.nacht {
  background-color: #000;
  color: rgba(255, 255, 255, 0.3);
}

[…]

html.nacht main {
  font-size: 150%;
}

Javascript

// der Button mit der ID "nachtansicht"
const nachtansichtButton = document.querySelector("#nachtansicht");

// das gesamte HTML-Dokument
const html = document.querySelector("html");

nachtansichtButton.addEventListener("click", function() {
      // HTML bekommt die Klasse "nacht" oder sie wird wieder genommen
      html.classList.toggle("nacht");
});

Menü animiert via Javascript ein- und ausblenden

Das Menü wird sichtbar/unsichtbar, wenn Ihr auf den „Burger“ clickt!

Online-Demo

Schaut Euch die Demo an – und ladet Euch zum Experimentieren die Zip-Datei runter!

HTML

<header>
    […]

    <button id="naviButton"><img src="burger.svg" /></button>

    <nav>
        <ul>
            <li><a href="portfolio.html">Portfolio</a></li>
            <li><a href="ueber_mich.html">Über mich</a></li>
            […]
        </ul>
    </nav>
</header>

CSS

/*
button nicht zu klein machen,
damit man diesen mit dem Finger gut "anklicken" kann!
*/
#naviButton {
    background: none;
    border: none;
    width: 44px;
    height: 44px;
    position: absolute;
    right: 0;
    top: 0.3rem;
}

/* das eigentliche Symbol kann dann kleiner sein! */
#naviButton img {
    width: 24px;
    transition: all 0.3s;
}

/* Klasse "gedreht" wird via Javascript gesetzt */
#naviButton.gedreht img {
    transform: rotate(-90deg);
}

/* ================= nav ================= */

nav {
    width: 10em;
    position: absolute;
    right: 13px;
    /* damit aus dem sichtbaren Bereich hinausgeschoben */
    top: -250px;
    /* Transition definieren */
    transition: all 0.3s ease-in-out;
}

/*
erst wenn via Javascript "nav" die Klasse "sichtbar"
hinzugefügt wird, wird "nav" in den sichtbaren Bereich hinausgeschoben
*/
nav.sichtbar {
    top: 40px;
}

Javascript

// das nav-Element
const nav = document.querySelector("nav");

// der Button mit der ID "naviButton"
const naviButton = document.querySelector("#naviButton");

/*
Klickt man auf den Button "#naviButton",
wird von "nav" die Klasse "sichtbar" ein- oder ausgeschaltet
damit wird ebenfalls die Transition ausgelöst!
siehe dazu auch bei "style.css" die Anweisungen für "nav"
*/
naviButton.addEventListener("click", function() {
      // das Nav-Menü sichtbar/unsichtbar machen
      nav.classList.toggle("sichtbar");

      // das SVG im Button um 90° drehen
      naviButton.classList.toggle("gedreht");
});

Gleiche Elemente via Javascript auswählen

Hat man viele gleiche Elemente, die man mit Javascript „behandeln“ möchte, kann man von diesen eine Liste erstellen:

Demo

Schaut Euch die Demo an – oder ladet Euch diese herunter!

HTML

<main>
    <article>
        <h2>Lorem, ipsum.</h2>
        <p>
            Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas in
            odio repudiandae ipsum! Repellat.
        </p>
    </article>

    <article>
        <h2>Laudantium, reprehenderit?</h2>
        <p>
            Voluptas odit modi aliquam laborum eveniet nam voluptatibus
            repudiandae, possimus alias quam voluptate illo.
        </p>
    </article>
    […]
</main>

Javascript

// Liste (Array) aller <p>-Elemente im "main"
const mainPs = document.querySelectorAll('main p');
// console.log(mainPs);

// das <div>, in dem der Inhalt der angeklickten <p>s gezeigt wird
const result = document.querySelector('#result');

function textAusgabe(ereignis) {
    // der angeklickte Text wird in Variable "clickText" gespeichert
    let clickText = ereignis.currentTarget.textContent;

    // das <div> mit der ID "result" bekommt als Text den Inhalt der Variable "clickText"
    result.textContent = clickText;

    // bei einem vorher angeklickten <p> wird die Klasse "angeklickt" entfernt
    const angeklickt = document.querySelector('.angeklickt');
    if (angeklickt) {
        angeklickt.classList.remove('angeklickt');
    }

    // das gerade angeklickte <p> bekommt die Klasse "angeklickt" und wird damit rot
    ereignis.currentTarget.classList.add('angeklickt');
}

// Clickt man auf eines der <p>-Elemente, wird die Funktion "textAusgabe" (Zeile 8) gestartet
mainPs.forEach(function (ereignis) {
    ereignis.addEventListener('click', textAusgabe);
});

Hotspots auf Bild

Auch hier wird die Anweisung querySelectorAll() verwendet, um alle Hotspots einheitlich via Javascript anzusprechen.

Achtet beim Positionieren der Hotspots, dass Ihr diese mit Prozentangaben positioniert, damit die Position auch stimmt, wenn die Größe des Browsers verändert wird!

Demo

Schaut Euch die Demo an – oder ladet Euch diese herunter!

HTML

<main>
    <!-- Das Bild, auf dem die Hotspots plaziert sind -->
    <figure>
        <img src="./p/simssee.jpg" alt="Der Simssee" />
    </figure>

    <!-- Heuberg -->
    <figure class="hotspot" id="heuberg">
        <img src="./assets/p/hotspot.svg" alt="" />
        <!-- Diese Figcaption wird erst sichtbar,
        wenn man auf den zugehörigen Kreis geklickt hat -->
        <figcaption>Der <strong>Heuberg</strong>iebtes Ausflugsziel</figcaption>
    </figure>

    […]
</main>

CSS

.hotspot figcaption {
    background-color: rgba(0, 0, 0, 0.8);
    color: #fff;
    padding: 0.5rem;
    width: 1000%;
    position: absolute;
    top: 0;
    left: clamp(10px, 3vw, 40px);
    /* erst mal aus dem sichtbaren Bereich nach links verschoben */
    transform: translateX(-200vw);
    /* Element soll schnell verschwinden */
    transition: transform 0.5s;
}

/* "sichtbar" wird via Javascript gesetzt! */
.hotspot figcaption.sichtbar {
    /* animiert zurück in den sichtbaren Bereich */
    transform: translateX(0);
    /* Element soll langsam sichtbar werden */
    transition: transform 0.8s;
}

/* Prozent-Angaben, damit Abbildung responsiv ist! */
#heuberg {
    left: 37%;
    top: 41%;
}

Javascript

// ================= figCaptionSichtbarMachen =================

function figCaptionSichtbarMachen(ereignis) {
    // das schon sichtbare <figcaption> unsichtbar machen
    // somit ist immmer nur ein <figcaption> sichtbar
    const sichtbar = document.querySelector('.sichtbar');
    if (sichtbar) {
        sichtbar.classList.remove('sichtbar');
    }
    // <figcaption> ist das 2. Kind von figure, <img> das 1.…
    // in JS ist der 1. Eintrag [0]!
    ereignis.currentTarget.children[1].classList.add('sichtbar');
}

// Liste aller <figure>-Elemente mit der Klasse "hotspot"
const hotspots = document.querySelectorAll('.hotspot');

// Clickt man auf eines der Kreise, wird die Funktion "figCaptionsSichtbarMachen" gestartet
// damit wird der Text neben dem Kreis sichtbar
hotspots.forEach(function (ereignis) {
    ereignis.addEventListener('click', figCaptionSichtbarMachen);
});

// =================== figCaptionUnsichtbarmachen ===================

// klickt man auf das gerade sichtbare <figcaption>, wird dieses unsichtbar
// da es sich "innerhalb" der "hotspots" befindet,
// würde die function "figCaptionSichtbarMachen" beim Klicken ebenfalls ausgeführt
// "ereignis.stopImmediatePropagation()" verhindert dies

function figCaptionUnsichtbarmachen(ereignis) {
    ereignis.stopImmediatePropagation();
    ereignis.currentTarget.classList.remove('sichtbar');
}

// alle <figcaption>-Elemente innerhalb von ".hotspot"
const hotspotFigcaptions = document.querySelectorAll('.hotspot figcaption');

// klickt man auf das gerade sichtbare <figcaption>, wird dieses unsichtbar
hotspotFigcaptions.forEach(function (ereignis) {
    ereignis.addEventListener('click', figCaptionUnsichtbarmachen);
});

Hotspots auf Karte

Erneut wird hier die Anweisung querySelectorAll() verwendet, um alle Hotspots einheitlich via Javascript anzusprechen.

Achtet beim Positionieren der Hotspots, dass Ihr diese mit Prozentangaben positioniert, damit die Position auch stimmt, wenn die Größe des Browsers verändert wird!

Demo

Schaut Euch die Demo an – oder ladet Euch diese herunter!

HTML

<main>
  <!-- Das Bild, auf dem die Hotspots plaziert sind -->
  <figure id="karte">
    <img src="./p/openStreetMap_Munich.jpg" alt="OpenStreetMap Munich" />
    <figcaption>
      <a href="https://openstreetmap.de/karte/">OpenStreetMap Munich</a>
    </figcaption>
  </figure>

  <!-- Emmet: article>img+div>h2+figure>img+ficaption^p>cite -->

  <!-- Glyptothek -->
  <article id="Glyptothek">
    <img class="sternchen" src="./assets/p/sternchen.svg" alt="" />
    <div>
      <h2>Die Glyptothek</h2>
      <figure>
        <img src="./p/Glyptothek.jpg" alt="Glyptothek" />
        <ficaption
          >Von Diego Delso,
          <a
            href="https://creativecommons.org/licenses/by-sa/3.0"
            title="Creative Commons Attribution-Share Alike 3.0"
            >CC BY-SA 3.0</a
          >,
          <a href="https://commons.wikimedia.org/w/index.php?curid=18929888"
            >Link</a
          ></ficaption
        >
      </figure>
      <p>
        Die Glyptothek im Kunstareal München ist ein unter Ludwig I.
        errichtetes Museum für die Sammlung antiker Skulpturen und wurde von
        1816 bis 1830 nach Plänen Leo von Klenzes am Königsplatz in München
        errichtet.
        <cite
          ><a href="https://de.wikipedia.org/wiki/Glyptothek_(M%C3%BCnchen)"
            >Wikipedia</a
          ></cite
        >
      </p>
    </div>
  </article>

  […]
</main>

CSS

main .sternchen {
  width: 44px;
  position: relative;
  /* wichtig, sonst liegen die Sternchen "hinter" den <article>-Elementen
  und können nicht angeklickt werden! */
  z-index: 5;
}

article figure {
  font-size: 80%;
  margin-bottom: 1rem;
}

main article {
  width: clamp(300px, 25vw, 450px);
  position: absolute;
  cursor: pointer;
}

main article div {
  width: 100%;
  position: relative;
  background-color: rgba(255, 255, 255, 0.8);
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 0.5em;
  /* nach oben aus dem sichtbaren Bereich verschoben */
  transform: translateY(-300vh);
  transition: transform 0.4s;
}

/* wird via Javascript gesetzt! */
main article div.sichtbar {
  /* in den sichtbaren Bereich geschoben */
  transform: translateY(0);
  transition: transform 0.8s;
  /* wichtig, sonst sind die Sternchen vor dem <article> */
  z-index: 10;
}

/* ================ die positionierten Article ================ */

#Theatinerkirche {
  left: 48%;
  top: 35%;
}

Javascript

// ================= articleDivSichtbarMachen =================

function articleDivSichtbarMachen(ereignis) {
  // console.log(ereignis.currentTarget.nextElementSibling);

  // das schon sichtbare <figcaption> unsichtbar machen
  // somit ist immmer nur ein <figcaption> sichtbar
  const sichtbar = document.querySelector(".sichtbar");
  if (sichtbar) {
    sichtbar.classList.remove("sichtbar");
  }

  // <article><div> ist der "next Sibling" vom img.sternchen
  // in JS ist der 1. Eintrag [0]!
  ereignis.currentTarget.nextElementSibling.classList.add("sichtbar");
}

// Liste aller Sternchen-Elemente
const sternchen = document.querySelectorAll(".sternchen");
// console.log(sternchen);

// Clickt man auf eines der Sternchen, wird die Funktion "articleDivSichtbarMachen" gestartet
// damit wird Text und Bild unter dem Sternchen sichtbar
sternchen.forEach(function (ereignis) {
  ereignis.addEventListener("click", articleDivSichtbarMachen);
});

// =================== articleDivUnsichtbarmachen ===================

// klickt man auf das gerade sichtbare <figcaption>, wird dieses unsichtbar
// da es sich "innerhalb" der "hotspots" befindet,
// würde die function "articleDivSichtbarMachen" beim Klicken ebenfalls ausgeführt
// "ereignis.stopImmediatePropagation()" verhindert dies

function articleDivUnsichtbarmachen(ereignis) {
  ereignis.stopImmediatePropagation();
  ereignis.currentTarget.classList.remove("sichtbar");
}

// alle <div>-Elemente innerhalb von "<article>"
const articleDivs = document.querySelectorAll("article div");
// console.log(articleDivs);

// klickt man auf das gerade sichtbare <figcaption>, wird dieses unsichtbar
articleDivs.forEach(function (ereignis) {
  ereignis.addEventListener("click", articleDivUnsichtbarmachen);
});

Aufspringendes Großbild

Klickt man auf eines der Bilder in der Galerie, wird davon eine größere Version gezeigt. Dabei wird keine neue Seite aufgerufen, das große Bild legt sich direkt über die Galerie.

Online-Demo

Schaut Euch die Demo an – und ladet Euch zum Experimentieren die Zip-Datei runter!

Beachtet, dass es neben dem style.css auch noch die Datei popup.css gibt, in der alle CSS-Anweisungen gesammelt sind, die für das aufspringende Großbild nötig sind!

Ebenso gibt es neben dem script.js auch noch eine Datei popup.js, die Ihr nicht verändern müsst, wenn Ihr meinen Code unverändert verwenden möchtet.

Wenn Ihr den Code für Eure eigene Website verwenden möchtet, müsst Ihr „nur“ die Inhalte der figure-Elemente anpassen, die richtigen Links zu den Großbilder erstellen und sowohl die CSS-Dateien als auch die Javascript-Dateien einbinden.

HTML

<!DOCTYPE html>
<html lang="de">
<head>
    […]
    <!-- das "normale Stylesheet" -->
    <link rel="stylesheet" href="c/style.css" />

    <!-- CSS-Angaben für das Popup in eigenes CSS-Datei! -->
    <link rel="stylesheet" href="c/popup.css" />
</head>

<body>
    […]

    <main>
    <a href="p/gross/IMG_20181028_090340.jpg">
        <figure>
        <img
            src="p/klein/IMG_20181028_090340.jpg"
            data-beschreibung="Grappe schaut skeptisch"
            alt="cooles Bild"
        />
        <figcaption>Grappa</figcaption>
        </figure>
    </a>
    […]
    </main>

    <script src="js/popup.js"></script>
    <script src="js/script.js"></script>
</body>
</html>

Javascript

// das ist zu ausführlich, um hier gezeigt zu werden – schaut Euch im Demo-Paket den Code an!
// Ich habe ihn ausführlich kommentiert…

Anzeige, wie weit man schon gescrollt hat

Am unteren Rand des Headers wird beim Scrollen ein roter Balken sichtbar, der anzeigt, wie viel der Seite man schon heruntergescrollt ist.

Online-Demo

Schaut Euch die Demo an – oder ladet Euch diese herunter!

HTML

<header>

    <h1>Scroll Anzeige</h1>
    <p>zeigt an, wie weit schon gescrollt wurde…</p>

    <!-- <span> im div#scrollAnzeige wird via JS in der Breite angepasst -->
    <div id="scrollAnzeige"><span></span></div>

</header>

CSS

/* 
    die Breite soll abhängig von der Scrollposition 
    von <main> angezeigt werden
    dies wird in JS berechnet! 
*/
div#scrollAnzeige span {
    display: block;
    height: 100%;
    width: 0%;
    background-color: red;
}

Javascript

// bodyHoehe abhängig, wie breit das Fenster ist
// und entsprechend länger oder kürzer gescrollt werden muss
const bodyHoeheBerechnen = () => {
    bodyScrollHeight = body.scrollHeight;

    // innere Höhe des Browserfensters weg,
    // weil ab da auch nicht mehr gescrollt werden kann
    bodyHoehe = bodyScrollHeight - window.innerHeight;
};

const prozentAnzeige = () => {
    let scrollYPos = window.scrollY;
    let prozentAngezeigt = (scrollYPos / bodyHoehe) * 100;

    // hier wird die CSS-Angabe "width" angepasst
    scrollAnzeigeSpan.style.width = `${prozentAngezeigt}%`;
};

const body = document.querySelector('body');
const scrollAnzeigeSpan = document.querySelector('#scrollAnzeige span');
let bodyHoehe;

// beim Starten
bodyHoeheBerechnen();

// beim Größe-Ändern des Browserfensters
window.addEventListener('resize', bodyHoeheBerechnen);

// beim Scrollen wird angezeigt, wie weit man schon gescrollt hat
document.addEventListener('scroll', prozentAnzeige);

Video Player

Videos lassen sich seit HTML5 wunderbar einfach in einer Website integrieren! Auf den nächsten Slides will ich Euch schrittweise zeigen, wie Video und Website miteinander interagieren können.


Video Player: HTML und CSS

HTML und CSS sind ganz unkompliziert – Ihr müsst nur auf die korrekten, relativen Pfade achten…

Achtung LINUX!

Auf den in der Ausstellung verwendeten iMacs läuft Ubuntu/Linux. Aus Lizenzgründen laufen darauf keine mp4-Filme! Ihr müsst deswegen Eure Filme sowohl im mp4- als auch im ogg-Format abspeichern und als Quelle hinterlegen!

Demo

Schaut Euch die Demo an!

HTML

<!-- 
    ACHTUNG: 'autoplay' (Video startet automatisch) geht nur in Kombination mit 'muted'! 
    Für Entwicklung gut, dann weg: 'controls="true"', 
    damit werden die im Browser eingebauten Steuerelemente des Videos sichtbar
    poster wird nur sichtbar, wenn es kein 'autoplay' gibt
    Beachtet auch, die 2 source-Angaben für die Videos!
    Die Browser sind klug genug, sich die für sie geeigente Quelle selbst rauszusuchen…
-->
<video
    id="meinVideo"
    muted
    autoplay
    preload="auto"
    width="1280"
    height="720"
    poster="v/SRS_Stegaufbau.jpg"
>
    <source src="v/SRS_Stegaufbau.mp4" type="video/mp4" />
    <source src="v/SRS_Stegaufbau.ogg" type="video/ogv" />
</video>

CSS

/* allgemeines Reset */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* video kann wie image einfach "responsive" gemacht werden */
img,
video {
    width: 100%;
    height: auto;
}

Video Player: Steuerelemente via Javascript

Play/Pause, Sound an/aus, Film loop/unloop lassen sich einfach via Javascipt implementieren – und somit auch individuell positionieren und gestalten!

Demo

Schaut Euch die Demo an!

HTML

<main>
  <video
    id="meinVideo"
    muted
    autoplay
    preload="auto"
    width="1280"
    height="720"
    poster="v/SRS_Stegaufbau.jpg"
  >
    <source
      src="v/SRS_Stegaufbau.mp4"
      type="video/mp4"
    />
<source
      src="v/SRS_Stegaufbau.ogg"
      type="video/opv"
    />
  </video>

  <div class="steuerelemente">
    <button id="videoPlay">▶</button>
    <button id="videoLoop">♻</button>
    <button id="videoSound">♫</button>
  </div>
</main>

CSS

.steuerelemente {
  position: relative;
  z-index: 100;
  width: 150px;
  margin: 0.2rem 0 0 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, 40px);
  gap: 0.5rem;
}

.steuerelemente button {
  border: none;
  padding: 0.6rem 0;
  color: white;
  background-color: rgb(87, 87, 87);
  background-image: linear-gradient(rgb(102, 102, 102), rgb(48, 48, 48));
  font-size: 140%;
  line-height: 1;
  border-radius: 3px;
  cursor: pointer;
  transition: all 0.1s;
}
.steuerelemente button.aktiv {
  background-color: red;
  background-image: linear-gradient(rgb(224, 91, 91), rgb(153, 4, 4));
}

.steuerelemente button:hover {
  opacity: 0.9;
}

Javascript

const body = document.querySelector("body");
const meinVideo = document.querySelector("#meinVideo");

// ================= die Buttons unterhalb des Videos =================

const videoPlay = document.querySelector("#videoPlay");
const videoLoop = document.querySelector("#videoLoop");
const videoSpeed = document.querySelector("#videoSpeed");
const videoSound = document.querySelector("#videoSound");

// =============== VideoPlay-Button entsprechend anzeigen ===============

function videoZeitAbfragen() {
  // läuft das Video gerade?
  if (meinVideo.paused) {
    videoPlay.innerHTML = "▶";
    videoPlay.classList.remove("aktiv");
  } else {
    videoPlay.innerHTML = "❚❚";
    videoPlay.classList.add("aktiv");
  }
}
// frägt laufend (alle 100ms) Abspielzeit ab und stellt den Button entsprechend dar
// später kann damit auch das Aussehen des Browsers geändern werden
// je nachdem, an welcher Stelle sich das Video befindet
setInterval(videoZeitAbfragen, 100);

// ========================= videoSound an/aus =========================

function videoSoundAnAus() {
  if (meinVideo.muted) {
    meinVideo.muted = false;
    videoSound.classList.add("aktiv");
  } else {
    meinVideo.muted = true;
    videoSound.classList.remove("aktiv");
  }
}

// Clickt man auf den Button "videoSound", wird Sound "getoggelt"
videoSound.addEventListener("click", videoSoundAnAus);

// ========================= videoLooping =========================

// stellt ein, ob das Video als Loop oder nur einmal abläuft
function videoLooping() {
  if (meinVideo.loop == true) {
    meinVideo.loop = false;
    videoLoop.classList.remove("aktiv");
  } else {
    meinVideo.loop = true;
    videoLoop.classList.add("aktiv");
  }
}

// Clickt man auf den Button "videoLoop", wird "getoggelt",
// ob der Film  nur einmal oder als Endlosschleife abgespielt wird
videoLoop.addEventListener("click", videoLooping);

// ======================== videoStarten ========================

function videoStarten() {
  if (meinVideo.paused) {
    meinVideo.play();
  } else {
    meinVideo.pause();
  }
}

// Clickt man auf den Button "videoPlay", wird der Film abgespielt oder angehalten
videoPlay.addEventListener("click", videoStarten);

// clickt man auf das Video, wird dieses gestartet oder angehalten
meinVideo.addEventListener("click", videoStarten);

Video Player: Kapitel

Damit ist es möglich, direkt an bestimmte Stellen im Film zu springen…

Demo

Schaut Euch die Demo an!

HTML

<main>
  <video id="meinVideo" […]>
    <source […] />
    <source […] />
  </video>

  <div class="steuerelemente">
    […]
  </div>

  <div id="startpunkte">
    <h2>Kapitel</h2>
    <section>
      <ol>
        <!-- 
            bei "data-startpunkt" angeben, wohin das Video springen soll 
            Zeitangabe in Sekunden vom Beginn des Videos
        -->
        <li><button data-startpunkt="27">Morgenstimmung</button></li>
        <li><button data-startpunkt="147">Steg fixieren</button></li>
        <li><button data-startpunkt="227">Weststeg fast fertig</button></li>
        <li><button data-startpunkt="300">Traktor hilft</button></li>
        <li><button data-startpunkt="366">Prozession</button></li>
      </ol>
    </section>
  </div>
</main>

CSS

[…]

#startpunkte {
  margin-top: 1.5rem;
}

#startpunkte button {
  font-size: 100%;
  border: none;
  background: none;
  cursor: pointer;
}

/* wird via JS gesetzt */
#startpunkte .current {
  font-weight: bold;
}

Javascript

// ================== Liste mit verschiedenen Video-Startpunkten ==================

// alle Buttons in der Section "#startpunkte"
const startPunkte = document.querySelectorAll("#startpunkte button");

function videoStartpunktFestlegen(ereignis) {
  // fragt im HTML-Dokument den Wert von "data-startpunkt" ab
  let startpunkt = ereignis.target.dataset.startpunkt;

  // fragt im HTML-Dokument den Text (z.B. "Morgenstimmung") ab
  let beschreibung = ereignis.target.textContent;

  // verschiebt den Startpunkt des Videos auf die gewünschte Stelle
  meinVideo.currentTime = startpunkt;

  // ======= ab hier "Kosmetik" der Kapitelliste… =======
  // bisher schon angeklickter Kapitelname wieder normal darstellen
  const currentButton = document.querySelector("#startpunkte button.current");
  if (currentButton != null) {
    currentButton.classList.remove("current");
  }

  // aktuell angeklickter Kapitelname wird fett dargestellt
  ereignis.target.classList.add("current");
}

// Klickt man auf einen der Buttons bei "#startpunkte",
// wird die Funktion "videoStartpunktFestlegen" aufgerufen
startPunkte.forEach(function (ereignis) {
  ereignis.addEventListener("click", videoStartpunktFestlegen);
});

Video Player: Zusatzinfos einblenden

Zu interessanten Stellen des Videos können Zusatzinformationen eingeblendet werden

Demo

Schaut Euch die Demo an!

HTML

<main>
  <video id="meinVideo" […]>
    <source […] />
    <source […] />
  </video>

  <div class="steuerelemente">
    […]
  </div>

  <div id="startpunkte">
    […]
  </div>

  <!-- Zusatzinfo, wird erst an gewünschter Filmstelle eingebunden -->

  <section class="zusatzinfo" id="morgenstimmung">
    <h2>Simssee</h2>
    <p>…</p>
  </section>

  <section class="zusatzinfo" id="traktor">
    <h2>Traktor</h2>
    <p>…</p>
  </section>

  <section class="zusatzinfo" id="prozession">
    <h2>Prozession</h2>
    <p>…</p>
  </section>
</main>

CSS

/* Sectionen, die erst sichtbar werden, wenn Film 
zu einer bestimmten Stelle gelaufen ist */

.zusatzinfo {
  width: 30%;
  background-color: rgba(0, 0, 0, 0.8);
  color: white;
  padding: 1rem;
  z-index: 40;
  position: absolute;
  top: 8rem;
  right: 2rem;
  /* aus dem sichtbaren Bereich verschoben */
  transform: translateX(-150vw);
  transition: all 1s;
  cursor: pointer;
}

.zusatzinfo h2 {
  margin-top: 0.5rem;
}

/* wird via JS gesetzt */
.angezeigt {
  /* in den sichtbaren Bereich geschoben */
  transform: translateX(0);
}

Javascript

// die Zusatzinfos an bestimmten Stellen des Videos ein-/ausblenden
// "anfang" und "ende" sind die Sekunden des Videos, 
// zwischen denen die Zusatzinfos sichtbar sein sollen
function zusatzInfosSichtbar(welches, anfang, ende) {
  if (meinVideo.currentTime > anfang && meinVideo.currentTime < ende) {
    welches.classList.add("angezeigt");
  } else {
    welches.classList.remove("angezeigt");
  }
}

// diese Funktion wird schon für die Steuerelemente verwendet und hier erweitert!
function videoZeitAbfragen() {
  // läuft das Video gerade?
  if (meinVideo.paused) {
    videoPlay.innerHTML = "▶";
    videoPlay.classList.remove("aktiv");
  } else {
    videoPlay.innerHTML = "❚❚";
    videoPlay.classList.add("aktiv");
  }

  // Zusatzinfos einblenden, wenn Video an entsprechender Stelle
  const morgenstimmung = document.querySelector("#morgenstimmung");
  zusatzInfosSichtbar(morgenstimmung, 27, 31);

  const traktor = document.querySelector("#traktor");
  zusatzInfosSichtbar(traktor, 301, 310);

  const prozession = document.querySelector("#prozession");
  zusatzInfosSichtbar(prozession, 367, 371);
}

// frägt laufend (alle 100ms) Abspielzeit ab und stellt den Button entsprechend dar
setInterval(videoZeitAbfragen, 100);

Video Player: Anzeige Videozeit

Der am oberen Rand des Videos sichtbare Kreis zeigt die aktuelle Abspielposition des Videos an.

… im Header werden rechts zusätzlich die gleichen Informationen digital angezeigt …

Demo

Schaut Euch die Demo an!

HTML

<main>
    <!-- Abspielanzeiger -->
    <div id="balken">
    <div id="groove">
        <span id="anzeiger"></span>
    </div>
    </div>

    <video id="meinVideo" […]>
        <source […] />
        <source […] />
    </video>

    <div class="steuerelemente">
        […]
    </div>

    <div id="startpunkte">
        […]
    </div>
</main>

CSS

#balken {
  width: 100%;
  height: 6px;
  margin: 4px 0 0 0;
  background-color: black;
  position: relative;
}

#groove {
  position: relative;
  width: 98%;
}

#anzeiger {
  background-color: red;
  background-image: radial-gradient(
    at 7px 7px,
    rgb(245, 59, 59) 8px,
    rgb(192, 2, 2)
  );
  width: 26px;
  height: 26px;
  position: absolute;
  left: 0px;
  top: -11px;
  border-radius: 50%;
  box-shadow: 2px 2px 15px rgba(0, 0, 0, 0.2);
  cursor: pointer;
}

Javascript

// roter Kreis, der sich waagrecht bewegt, wenn Video läuft
// ACHTUNG: Wird auch weiter unten als "draggable Element" genutzt!
const anzeiger = document.querySelector("#anzeiger");

// die Anzeige von Dauer und Abspielzeit im Header
const abgespieltSpan = document.querySelector("#abgespielt");
const filmdauerSpan = document.querySelector("#filmdauer");

// Abfrage, ob die MouseTaste gedrück ist
// dann soll "anzeiger" (roter Kreis) nicht die aktuelle Filmposition anzeigen
// weil er sich sonst nicht "draggen" lässt
// wird hier noch nicht gebraucht!
let mouseGedrueckt = false;

// "#anzeiger" (roter kreis) zeigt an, wie weit der Film schon gelaufen ist
function videoAbspielPosition() {
  const filmDauer = meinVideo.duration;
  let abgespielt = meinVideo.currentTime;

  // "Math.round" rundet auf ganze Sekunden
  abgespieltSpan.textContent = Math.round(abgespielt);
  filmdauerSpan.textContent = Math.round(filmDauer);

  // hier berechnen, wie weit der rote Kreis wandern soll
  // je nachdem wie weit das Video vorangeschritten ist
  // Umrechnung in Prozent, die dann in CSS verwendet werden können
  // siehe "anzeiger.style.left = `${abgespieltProzent}%`;"
  let abgespieltProzent = (abgespielt * 100) / filmDauer;
  // console.log(filmDauer, abgespielt, abgespieltProzent);

  if (mouseGedrueckt === false) {
    anzeiger.style.left = `${abgespieltProzent}%`;
  }
}

// diese Funktion wird schon für die Steuerelemente verwendet und hier erweitert!
function videoZeitAbfragen() {

    // bis hier die bisher verwendeten Sachen…

    // "#anzeiger" (roter kreis) zeigt an, wie weit der Film schon gelaufen ist
    videoAbspielPosition();
}

// frägt laufend (alle 100ms) Abspielzeit ab und stellt den Button entsprechend dar
setInterval(videoZeitAbfragen, 100);

Video Player: Verschiebbarer Positionsknopf

Der am oberen Rand des Videos sichtbare Kreis kann auch via „Drag & Drop“ waagrecht verschoben werden. Damit springt die Abspielposition des Video an die neue Stelle.

Demo

Schaut Euch die Demo an!

HTML

<!-- wie beim vorherigem Beispiel -->

CSS

/* wie beim vorherigem Beispiel */

Javascript

function videoAufAnzeigerPositionieren(aktPosition) {
    let grooveLaenge = groove.offsetWidth;
    let prozentAbgespielt = (aktPosition * 100) / grooveLaenge;
    const filmDauer = meinVideo.duration;
    let neueFilmPos = (filmDauer * prozentAbgespielt) / 100;
    meinVideo.currentTime = neueFilmPos;
}

// auf die Breite von "groove" soll sich die max. xPos von "anzeiger" beziehen!
const groove = document.querySelector('#groove');

// falls "anzeiger" links oder rechts außerhalb des gewünschten Bereiches liegt
// wird es wieder korrekt positioniert
function raenderKontrolle() {
    let xPos = anzeiger.offsetLeft;

    if (xPos < 0) {
        //   console.log('xPos < 10!');
        anzeiger.style.left = '0';
    }

    // berechnet rechten Rand minus Breite des "anzeiger"
    // "offsetWidth" ist "responsiv!"
    // let maxXPos = groove.offsetWidth - anzeiger.offsetWidth;
    let maxXPos = groove.offsetWidth;

    if (xPos > maxXPos) {
        //   console.log('xPos > 590!');
        anzeiger.style.left = maxXPos + 'px';
    }
}

/*
Script so angepasst, dass sich Element nur noch waagrecht verschieben lässt!
https://www.w3schools.com/howto/howto_js_draggable.asp
*/

function dragElement(elmnt) {
    let pos1 = 0,
        pos2 = 0;

    elmnt.onmousedown = dragMouseDown;

    function dragMouseDown(e) {
        e = e || window.event;
        e.preventDefault();

        // get the mouse cursor position at startup:
        pos2 = e.clientX;

        // call a function whenever the cursor moves:
        document.onmousemove = elementDrag;

        // Aktion, wenn die Mouse wieder losgelassen wird:
        document.onmouseup = closeDragElement;
    }

    function elementDrag(e) {
        e = e || window.event;
        e.preventDefault();

        mouseGedrueckt = true;

        let xPos = elmnt.offsetLeft - pos1;

        // hier nicht einschränken, wie weit das anzeiger verschoben werden kann!
        // Korrektur der xPos stattdessen bei "closeDragElement" machen

        // calculate the new cursor position:
        pos1 = pos2 - e.clientX;
        // clientX ist die waagrechte Koordinate des Cursors innerhalb des Browserfensters!
        pos2 = e.clientX;

        // set the element's new X-position:
        elmnt.style.left = elmnt.offsetLeft - pos1 + 'px';
    }

    function closeDragElement() {
        // console.log('closeDragElement ausgelöst!');

        // befindet sich "anzeiger" an den richtigen Randpositionen?
        raenderKontrolle();

        mouseGedrueckt = false;

        // Video an die Pos. vor-/zurückspulen, an der sich der Anzeiger befindet
        videoAufAnzeigerPositionieren(anzeiger.offsetLeft);

        // stop moving when mouse button is released:
        document.onmouseup = null;
        document.onmousemove = null;
    }
}

dragElement(anzeiger);

// auch beim Ändern des Browserfensters muss die Position wieder stimmen!
window.addEventListener('resize', raenderKontrolle);

Video Player: Download der Beispiele


Wireframes für Website

Auf den iMacs läuft in der Ausstellung eine Website, die so aufgebaut sein muss, dass die Besucher:innen diese intuitiv benutzen und alle Inhalte leicht finden können – sie haben dafür nur eine Mouse zur Verfügung!

Die Website wird im Fullscreen-Modus gestartet, so dass vom Browser selbst nichts zu sehen sein wird…

Wireframes

Entwerft (z.B. in Figma) Wireframes, wie die Website aufgebaut sein könnte! Wir sprechen beim nächsten Treffen über Eure Layouts.

Wichtige Punkte

  • Soll das Video Browser-füllend laufen oder ist noch Platz für weitere Informationen?
  • Wie werden die Benutzer:innen auf alle Filme/Inhalte aufmerksam?
  • Gibt es eine Startseite oder ist es egal, auf welcher Seite man startet?
  • Gibt es einen Navigationsbereich – und wenn ja, wo befindet sich dieser?
  • Was passiert, wenn der Bildschirm lange nicht benutzt wird?

User-Tests

  • Wen könntet Ihr als Tester einsetzen?
  • Wie und wann könnt Ihr testen?

Danke!

Alle Scripte durchsuchen

Weitere Vorträge: