Themen
Bisher nicht für die Studierenden – aktuell ein Nachschlagwerk für mich…
basierend auf dem Video-Kurs „Beginner Javascript“ von Wes Bos
Bisher nicht für die Studierenden – aktuell ein Nachschlagwerk für mich…
basierend auf dem Video-Kurs „Beginner Javascript“ von Wes Bos
// Variable, von der sich der Wert nicht ändern wird
const meinParagraph = document.querySelector('article.demo');
// Variable, deren Wert innerhalb der Scripte geändert wird
let anzahl = 0;
function loop() {
anzahl += 1; // zählt um eins hoch;
}
loop();
Zum Auswählen eines Elementes auf einer HTML-Seite.
Es können dazu die gleichen Selectoren verwendet werden wie bei CSS!
// der erste Paragraph wird ausgewählt
// querySelector holt immer das 1. Element, wenn es mehrere gibt
const p1 = document.querySelector("p");
p1.style.color = "blue";
// der zweite Paragraph
const p2 = document.querySelector("p:nth-of-type(2)");
p2.style.background = "#ddd";
// das span im dritten Paragraph
const span = document.querySelector("p span");
span.style.fontWeight = "bold";
// der Button soll die HG-Farbe ändern
const button = document.querySelector("button");
function hgFarbeAendern() {
const html = document.querySelector("html");
html.style.transition = "background-color 0.5s";
html.style.backgroundColor = "#ddd";
}
button.addEventListener("click", hgFarbeAendern);
Unterschiedliche Mouse-Events…
const meinBild = document.querySelector(".meinBild img");
const ausgabe = document.querySelector("#ausgabe");
meinBild.addEventListener("mouseenter", function(e) {
ausgabe.textContent = `mouseenter: Scr: ${e.target.src}`;
});
meinBild.addEventListener("mouseleave", function(e) {
ausgabe.textContent = `mouseenter: Alt: ${e.target.alt}`;
});
// gibt die aktuelle Posion des Mousezeigers aus
meinBild.addEventListener("mousemove", logKey);
function logKey(e) {
ausgabe.innerText = `
mousemove:
Screen X/Y: ${e.screenX}px, ${e.screenY}px
Client X/Y: ${e.clientX}px, ${e.clientY}px`;
}
Für angehende Webdesigner*innen besonders geeignet: Javascript schaltet nur eine Klasse an/aus. Den Rest (incl. Animationen) erledigt CSS!
Schaut Euch die Demo an – oder ladet das gesamte Paket herunter
See the Pen CSS-Klasse-via-Javascript-umschalten by Matthias Edler-Golla (@megolla) on CodePen.
<div id="wichtig">
<h2>Überschrift</h2>
<p>Lorem ipsum dolor…</p>
</div>
…
<footer>
<button id="meinKnopf">Klasse ändern</button>
</footer>
#wichtig {
margin: 1.5em 0;
transition: all 0.3s ease-in-out;
}
…
.betont {
font-size: 150%;
padding: 1rem;
border-radius: 10px;
background-color: #ffea9a;
transform: rotate(720deg);
}
die Klasse ".betont“ ist nicht im HTML-Code enthalten! Sie wird via Javascript bei "div#wichtig" hinzugefügt und wieder entfernt – also "getoggelt"
function klassenToggle(element, klasse) {
const ele = document.querySelector(element);
ele.classList.toggle(klasse);
}
function klickKlasseEinAus(ausloeser, element, klasse) {
document.querySelector(ausloeser).addEventListener("click", function() {
klassenToggle(element, klasse);
});
}
// Ihr müsst nur die untere Zeile (Zeile 14) anpassen und oben aufgeführten Code 1:1 übernehmen!
// Aufruf: klickKlasseEinAus(auslösendesElement, betroffenesElement, KlassenName);
klickKlasseEinAus("#meinKnopf", "#wichtig", "betont");
Funktionen können direkt innerhalb von Variablen oder Console.logs aufgerufen werden.
Die (neue) Schreibweise mit Backticks beachten! Diese befinden sich auf der Tastatur neben dem ß…
function calculateBill(wert) {
const total = wert * 1.19;
return total;
}
// holt den Netto-Wert direkt aus dem HTML
const netto = document.querySelector("#netto");
const nettoWert = netto.textContent;
// das Script wird beim Laden der Website ausgeführt
// durch das "* 100 / 100" wird auf 2 Stellen hinter Komma gerundet
const myTotal = Math.round(calculateBill(nettoWert) * 100) / 100;
// die Backticks beachten!
console.log(`Mein Wert ist $ ${myTotal}`);
const brutto = document.querySelector("#brutto");
brutto.textContent = myTotal;
// auch interessant, man kann Funktionen direkt aufrufen!
// Backticks beachten!
const verbose = document.querySelector("#verbose");
const meinWert = 220;
verbose.textContent = `
Meine Gesamtsumme bei netto €${meinWert}
ist brutto €${calculateBill(meinWert)}
`;
// anonymize functions direkt als Variable
// beachte, die Funktion hat keinen Namen!
const doctorize = function(name = "") {
return `Dr. ${name}`;
};
// Aufruf in Konsole
console.log(`Hallo ${doctorize("Heinz")}!`);
// Ein HTML-Element mit der ID "doctor"
const doctor = document.querySelector("#doctor");
doctor.textContent = `Hallo ${doctorize("Hurzelmann")}!`;
// Berechnung gleich im Return
// "5" ist der Default-Wert, falls kein Wert übergeben wird
function inchToCM(inches = "5") {
return `${inches * 2.54}cm`;
}
console.log(inchToCM(10));
// anonymous function
const inchToCM2 = function(inches = "5") {
return `${inches * 2.54}cm`;
};
console.log(`Das sind dann ${inchToCM2(100)}`);
// Arrow-functions
// kürzeste Schreibweise, wenn nur ein Parameter abgefragt wird
const inchToCM4 = inches => inches * 2.54;
console.log(`Die Umrechnung ist ${inchToCM4(12)}cm`);
Die jeweiligen Anweisungen werden jeweils nach dem angegebenen Zeitintervall (nach Laden der Seite) einmalig ausgeführt:
const myTimeOut = document.querySelector("#timeOut");
myTimeOut.style.opacity = "0";
// ext. Funktion, die bei Zeile 11 aufgerufen wird
function handleTimes() {
console.log("Hello!");
myTimeOut.style.opacity = "1";
}
// Timer Callback
setTimeout(handleTimes, 10000);
// Timer Callback mit anonymer Funktion
setTimeout(function() {
const myTimeOut2 = document.querySelector("#timeOut2");
myTimeOut2.style.fontSize = "150%";
}, 5000);
// Timer Callback mit Arrow-Funktion
setTimeout(() => {
document.body.style.backgroundColor = "#ccc";
const myPic = document.querySelector("img");
myPic.style.width = "200px";
myPic.style.borderRadius = "50%";
myPic.src = "https://picsum.photos/200";
}, 3000);
const paragraph = document.querySelector("p");
const stopButton = document.querySelector("#stop");
function austauschen() {
paragraph.textContent = "AUSGETAUSCHT!";
paragraph.classList.add("ausgetauscht");
}
const austausch = setTimeout(austauschen, 5000);
stopButton.addEventListener("click", function() {
// hier die Variable "austausch" ansprechen
clearTimeout(austausch);
paragraph.innerHTML += "<span style='color:red'>--- Timeout gestoppt!</span>";
});
// ===================== Animation des roten Kreises =====================
const kreis = document.querySelector("#kreis");
const div = document.querySelector("div");
// console.log(div.offsetWidth);
const divBreite = div.offsetWidth;
let x = 0;
function kreisAnimieren() {
x = x >= divBreite ? (x = 0) : (x = x + 50);
kreis.setAttribute("style", `--x: ${x}px;`);
}
// falls die Sache sofort ablaufen soll, einmal beim Laden aufrufen
// nächster Aufruf dann bei "setInterval"
kreisAnimieren();
const kreisAni = setInterval(kreisAnimieren, 2000);
function kreisAniStoppen() {
clearInterval(kreisAni);
kreis.textContent = "automatisch angehalten!";
}
// damit hört die kreisAni nach 20sek auf
const kreisAniStop = setTimeout(kreisAniStoppen, 20000);
Es gibt sowohl die Möglichkeit, reinen Text oder auch HTML abzufragen bzw. einzufügen
// Abfrage des ausgangsText
const ausgangsText = document.querySelector("#ausgangsText");
const ausgangsTextInhalt = ausgangsText.textContent;
console.log(ausgangsTextInhalt);
// Ausgangstext bei Zieltext als reinen Text einfügen
const zielText = document.querySelector("#zielText");
zielText.textContent = `Kopiert: ${ausgangsTextInhalt}`;
// in Ziel HTML einfügen
const zielHTML = document.querySelector("#zielHTML");
zielHTML.innerHTML = `<strong>Kopiert:</strong> <em style="background:#ccc">${ausgangsTextInhalt}</em>`;
// HTML-Tag durch outerHTML ersetzen
// Beachte, hier wird das "span"-Element durch das "em"-Element ersetzt!
const ausgangsHTML = document.querySelector("#ausgangsHTML");
const ausgangsHTMLInhalt = ausgangsHTML.outerHTML;
console.log(ausgangsHTMLInhalt);
const zielHTML2 = document.querySelector("#zielHTML2");
zielHTML2.outerHTML = ausgangsHTMLInhalt;
// bei Zieltext Text davor und danach einfügen
const zielText2 = document.querySelector("#zielText2");
zielText2.insertAdjacentText("beforebegin", "Achtung: ");
zielText2.insertAdjacentText("afterend", ": Ende!");
// bei Zieltext Text einfügen
const zielHTML3 = document.querySelector("#zielHTML3");
zielHTML3.insertAdjacentHTML("afterbegin", "<em>Achtung: </em>");
zielHTML3.insertAdjacentHTML("beforeend", "<strong> ENDE</strong>");
Viele Attribute lassen sich direkt via JS abfragen bzw. setzen! Das kann man beim jeweiligen Elemente mit folgendem Console-Aufruf abfragen:
const bild1 = document.querySelector("figure:nth-of-type(1) img");
console.dir(bild1);
const bild1 = document.querySelector("figure:nth-of-type(1) img");
const bild1figcaption = document.querySelector(
"figure:nth-of-type(1) figcaption"
);
//CSS-Breite verändern
bild1.style.width = "300px";
bild1figcaption.innerHTML = `
Klassen: ${bild1.classList}<br>
Alt-Text: ${bild1.alt}<br>
Natürliche Größe: Breite ${bild1.naturalWidth}px, Höhe ${bild1.naturalHeight}px<br>
Dataset: ${bild1.dataset.vorname}, ${bild1.dataset.nachname}, ${bild1.dataset.geburtstag}
`;
const p1 = document.querySelector("p:nth-of-type(1)");
// Schreibweise von "backgroundColor" beachten!
p1.style.backgroundColor = "rgba(0,0,0,.1)";
p1.style.color = "red";
p1.style.padding = "1em";
p1.style.borderRadius = "5px";
const div = document.querySelector("div");
// Backticks beachten -- hier wieder normale CSS-Schreibweise
// mit Bindestrichen!
div.style.cssText = `
background-color: blue;
color: #fff;
padding: 1em;
border-radius: 10px;
box-shadow: 0 0 12px 6px rgba(0,0,0,.5);
`;
const myParagraph = document.createElement("p");
myParagraph.textContent = `
Ich bin ein via JS erzeugter Paragraph,
der sich im von JS erzeugten <main>-Element befindet.
Auch das Bild ist via JS eingefügt worden
`;
myParagraph.classList.add("spezial");
console.log(myParagraph);
const myImage = document.createElement("img");
myImage.src = "https://picsum.photos/500";
myImage.alt = "Tolles Foto!";
myImage.style.width = "300px";
myImage.style.display = "block";
console.log(myImage);
// hier sollen myParagraph und myImage eingefügt werden
const myMain = document.createElement("main");
myMain.classList.add("wrapper");
console.log(myMain);
// "appendChild" fügt die erzeugten Elemente am Ende des jeweiligen Elements (hier body) an!
// die erzeugten Elemente werden innerhalb andere erzeugter Elemente "verschachtelt"
// besser das innere zuerst in das äußere verschachteln -- dann kann Browser das besser darstellen
myParagraph.appendChild(myImage);
myMain.appendChild(myParagraph);
document.body.appendChild(myMain); // erst hier wird der Browser veranlasst, die Seite neu zu zeichnen -> weniger Unruhe
// footer soll ganz unten gezeigt werden
const footer = document.createElement("footer");
footer.style.cssText = `
background-color: #000;
color: #fff;
padding: .2em 1em;
position: fixed;
left: 0;
bottom: 0;
width: 100%;
`;
footer.textContent = "Ich bin der via JS erzeugte Footer!";
document.body.insertAdjacentElement("beforeend", footer);
// hier soll der Clone eingefügt werden
const myMain = document.createElement("main");
document.body.appendChild(myMain);
// Clonen eines kompletten HTML-Elementes incl. Inhalt
const myH1 = document.querySelector("header h1"); // das Original
const myH1Clone = myH1.cloneNode(true); // der Clone incl. Inhalt und HTML-Tags
myH1Clone.style.background = "hsl(170, 50%, 90%)";
myH1Clone.style.padding = "1em";
myH1Clone.style.margin = "1em 0";
// einfügen des Clones bei myMain
myMain.insertAdjacentElement("beforeend", myH1Clone);
// verschieben eines HTML-Elementes (nicht clonen!)
myMain.insertAdjacentElement("beforeend", document.querySelector("h2"));
const meinArticle = document.querySelector("#first");
// Hiermit wird der komplette Inhalt des Artikels ausgetauscht
// mit den Backticks kann man das HTML sogar gut lesbar formatieren
const meineH3 = "generierte Überschrift";
const meinP = "Das geht ja <em>echt</em> super!";
const breite = 500; //naturalWidth des Bildes
const mySrc = `https://picsum.photos/${breite}`;
const myNames = ["Hugo", "Paula", "Sepp"];
function zufallsGanzZahl(min, max) {
return Math.round(Math.random() * (max - min) + min);
}
// beachte, dass innerhalb der Backticks auch Variablen funktionieren
// die innerhalb "normaler" Anführungsstriche eingefügt sind!
const myHTML = `
<h3 style="color:red">Ich bin eine ${meineH3}!</h3>
<ul>
<li>${myNames[0]}</li>
<li>${myNames[1]}</li>
<li>${myNames[2]}</li>
</ul>
<!-- Breite wird zufällig generiert -->
<img style="width:${zufallsGanzZahl(30,60)}vw" src="${mySrc}" alt="cooles Fotos!">
<p>${meinP}</p>
`;
// ab hier ist das generierte HTML erst im DOM eingebaut
// und kann angesprochen werden!
meinArticle.innerHTML = myHTML;
// das geht erst, wenn das Bild generiert ist!
const myImg = document.querySelector("#first img");
myImg.style.borderRadius = "50%";
const thias = document.querySelector('.thias');
// zählt alle HTML-Elemente auf, die sich bei "thias" befinden
console.log(thias.children);
// zählt alle Node-Elemtent auf - das sind auch die Textbausteine OHNE HTML-Tages!
// console.log(thias.childNodes);
// ============== Text aus dem firstElementChild ============
const myFirstElementChildText = thias.firstElementChild.textContent;
console.log(`Text des FirstElementChild: ${myFirstElementChildText}`);
// ============== Text aus dem lastElementChild ============
const myLastElementChildText = thias.lastElementChild.textContent;
console.log(`Text des LastElementChild: ${myLastElementChildText}`);
// ============ Text aus dem vorhergehenden Element =============
const prevEleSibling = thias.previousElementSibling;
const prevEleSiblingText = prevEleSibling.textContent;
console.log(`Text des vorherigen Elementes: ${prevEleSiblingText}`);
// ============ Text aus dem nachfolgendem Element ===============
// fragt erst ab, ob es das "nextEleSibling" überhaupt gibt
const nextEleSibling = thias.nextElementSibling;
let nextEleSiblingText;
// "null" heißt, dass es das Element nicht gibt
// "!= null" bedeutet, dass das Element existiert
if (nextEleSibling != null) {
nextEleSiblingText = nextEleSibling.textContent;
} else {
nextEleSiblingText = 'kein nextElementSibling gefunden!';
}
console.log(`Text des nachfolgendem Elementes: ${nextEleSiblingText}`);
// ============ HTML aus dem Parent Element ===============
const myParentElementHTML = thias.parentElement.outerHTML;
console.log(`outerHTML des ParentElementes:\n${myParentElementHTML}`);
// ============= "verschachtelte" Traverse =============
// nicht unbedingt sinnvoll, aber machbar!
const headerH1 = thias.parentElement.previousElementSibling.firstElementChild;
console.log(`headerH1 textContent: ${headerH1.textContent}`);
const myH2 = document.querySelector('h2');
myH2.remove();
das forEach loopt durch alle Elemente, die via querySelectorAll ausgewählt wurden.
// es werden hiermit nur die "ungeraden" Paragraphen angesprochen
const myPs = document.querySelectorAll("p:nth-of-type(2n-1)");
// "ereignis" ist wieder ein beliebiger Name, könnte auch "e" oder "xyz" sein!
myPs.forEach(function(ereignis) {
// Ausgabe des Inhalts jedes der Elemente
console.log(ereignis.textContent);
ereignis.style.cssText = `
border: 1px solid red;
padding: 0.5em;
background-color: #fff;
border-radius: 5px;
`;
});
Bei den via JS ausgewählten Paragraphen wird ein "Klick" eingebaut, der ein Alert auslöst und den jeweiligen Inhalt zeigt
// es werden hiermit nur die "geraden" Paragraphen angesprochen
const myPs = document.querySelectorAll("p:nth-of-type(2n)");
// hier muss nicht die gleiche Variabe "ereignis" stehen!
// man kann auch was anderes verwenden…
function tuWas(ereignis) {
// das entscheidende ist das "target"!
const myContent = ereignis.target.innerHTML;
alert(`Folgenden Inhalt gefunden: ${myContent}`);
}
// "ereignis" ist wieder ein beliebiger Name, könnte auch "e" oder "xyz" sein!
myPs.forEach(function(ereignis) {
ereignis.style.cssText = `
cursor: pointer;
border: 1px solid red;
padding: 0.5em;
background-color: #fff;
border-radius: 5px;
`;
ereignis.addEventListener("click", tuWas);
});
Darstellen der inaktiven/aktiven Button durch einfachen Klassentausch…
// alle buttons
const myButtons = document.querySelectorAll("nav button");
function klasseWeg(welche) {
// nächste Zeile so kompliziert, weil "querySelector" nach ".aktiv" sucht!
const klasseElement = document.querySelector(`.${welche}`);
// abfragen, ob es so ein Element gibt!
if (klasseElement != null) {
klasseElement.classList.remove(welche);
}
}
function buttonAction(ereignis) {
// Entfernen der Klasse "aktiv" bei dem Element, das aktuell diese Klasse hat
klasseWeg("aktiv");
// das entscheidende ist das "currentTarget"!
// console.log(ereignis.currentTarget.innerHTML);
ereignis.currentTarget.classList.add("aktiv");
}
myButtons.forEach(function(ereignis) {
ereignis.addEventListener("click", buttonAction);
});
:root {
--radius: 20px;
}
button {
border: none;
padding: 0.5em;
font-size: 90%;
background: linear-gradient(hsl(50, 90%, 80%), hsl(50, 90%, 50%));
cursor: pointer;
text-shadow: 1px 1px rgba(255, 255, 255, 0.6);
}
button:nth-of-type(1) {
border-radius: var(--radius) 0 0 var(--radius);
}
button:nth-last-of-type(1) {
border-radius: 0 var(--radius) var(--radius) 0;
}
button.aktiv {
background: linear-gradient(hsl(20, 90%, 80%), hsl(20, 90%, 50%));
}
Bei den via JS ausgewählten Ps wird ein "Klick-Button" eingebaut, der die Transparenz des jeweiligen Eltern-Elementes ändert
// es werden hiermit nur die "geraden" Paragraphen angesprochen
const myPs = document.querySelectorAll("p:nth-of-type(2n)");
// "ereignis" ist wieder ein beliebiger Name, könnte auch "e" oder "xyz" sein!
myPs.forEach(function(ereignis) {
ereignis.style.cssText = `
cursor: pointer;
border: 1px solid #ccc;
padding: 0.5em;
background-color: #fff;
border-radius: 5px;
`;
// einen Button generieren und einfügen
const myButton = document.createElement("button");
myButton.textContent = "opak";
myButton.style.cssText = `
display: block;
font-size: 80%;
border: none;
border-radius: 6px;
padding: 0.3em 1em;
margin: .5em 0;
cursor: pointer;
box-shadow: 0 0 6px 2px rgba(0,0,0,.15);
background-color: hsl(30, 80%, 80%);
`;
ereignis.appendChild(myButton);
});
// in einem 2. Schritt die Click-Funktionen der Buttons erzeugen
const myButtons = document.querySelectorAll("button");
function durchsichtigMachen(eee) {
const meinZiel = eee.target.closest("p");
meinZiel.style.transition = "opacity 0.8s";
if (meinZiel.style.opacity === "0.2") {
meinZiel.style.opacity = "1";
} else {
meinZiel.style.opacity = "0.2";
}
}
myButtons.forEach(function(aaa) {
aaa.addEventListener("click", durchsichtigMachen);
});
html {
scroll-behavior: smooth;
}
const header = document.querySelector("header");
// erzeugen der Elemente, die dann die seiten-interne Navi darstellen
const meinNav = document.createElement("nav");
const meinMenu = document.createElement("ul");
// alle Article aus "main"
const meineArticle = document.querySelectorAll("main article");
// Umwandlung in ein Array
const meineArticleArray = Array.from(meineArticle);
// Zusammenbau der Navi-Ul
for (let i = 0; i < meineArticleArray.length; i++) {
// das steht dann im Nav-Menü
const meinText = `Top ${i + 1}`; // +1 weil Array mit 0 anfängt!
// die ID des jeweiligen Articles wird hier automatisch erzeugt
const meinID = `art_${i}`;
// und im HTML eingefügt
meineArticleArray[i].id = meinID;
// gleichzeigt in dem erzeugten Navi-Menü bei den Links eingebaut
const meinLi = `<li><a href="#${meinID}">${meinText}</a></li>`;
meinMenu.innerHTML += meinLi;
}
// Einfügen der Nav-Sachen ins Nav
meinNav.appendChild(meinMenu);
// Einhängen im Header
header.insertAdjacentElement("beforeend", meinNav);
//=========== markieren des angeklickten Nav-Punktes ====
// entfernen dieser Klasse vorab,
// falls schon ein Link so markiert ist
function markierungEntfernen() {
const angeklickt = document.querySelector("header .angeklickt");
if (angeklickt !== null) {
angeklickt.classList.remove("angeklickt");
}
}
function linkMarkieren(ereignis) {
markierungEntfernen();
ereignis.target.classList.add("angeklickt");
}
function linkMarkierungEntfernen() {
markierungEntfernen();
}
const interneLinks = document.querySelectorAll("header nav a");
interneLinks.forEach(function(ereignis) {
ereignis.addEventListener("click", linkMarkieren);
});
// der Link "nach oben" sowie der Link bei "Smooth Scrolling"
const topScrollLinks = document.querySelectorAll("[href='#main']");
topScrollLinks.forEach(function(ereignis) {
ereignis.addEventListener("click", linkMarkierungEntfernen);
});
<header>
<h1><a href="#main">Smooth Scrolling</a></h1>
<!-- Navi wird via JS erzeugt! -->
</header>
<main id="main">
<!-- emmet: (article>h2{Überschrift $}+img[src="https://picsum.photos/400?random=$"]+p>lorem30)*8 -->
<article>
<h2>Überschrift 1</h2>
<div class="grid">
<img src="https://picsum.photos/400?random=1" alt="" />
<p>Lorem ipsum…</p>
</div>
</article>
<article>
<h2>Überschrift 2</h2>
<div class="grid">
…
Bis auf den IntersectionObserver weitgehend identisch mit dem vorherigen Beispiel. Nur werden die Nav-Elemente im Header nicht mehr rot, wenn man dort draufklickt, sondern wenn der dazugehörige Article sich im sichtbaren Bereich befindet. So werden die Nav-Elemente auch markiert, wenn man direkt durch das Dokument scrollt.
const header = document.querySelector("header");
// erzeugen der Elemente, die dann die seiten-interne Navi darstellen
const meinNav = document.createElement("nav");
const meinMenu = document.createElement("ul");
// alle Article aus "main"
const meineArticle = document.querySelectorAll("main article");
// Umwandlung in ein Array
const meineArticleArray = Array.from(meineArticle);
// Zusammenbau der Navi-Ul
for (let i = 0; i < meineArticleArray.length; i++) {
// das steht dann im Nav-Menü
const meinText = `T ${i + 1}`; // +1 weil Array mit 0 anfängt!
// die ID des jeweiligen Articles wird hier automatisch erzeugt
const meinID = `art_${i}`;
// und im HTML eingefügt
meineArticleArray[i].id = meinID;
// der Inhalt der jeweiligen als Title im Link
meinTitle = meineArticleArray[i].firstElementChild.textContent;
// gleichzeigt in dem erzeugten Navi-Menü bei den Links eingebaut
// der Klassenname (z.B. "art_0") ist identisch mit der ID(!) des verlinkten Artikels
// damit lässt sich dieser beim Scrollen und Abfragen via "IntersectionObserver" einfach ein/ausschalten
const meinLi = `<li><a href="#${meinID}" class="${meinID}" title="springe zu ${meinTitle}">${meinText}</a></li>`;
meinMenu.innerHTML += meinLi;
}
// Einfügen der Nav-Sachen ins Nav
meinNav.appendChild(meinMenu);
// Einhängen im Header
header.insertAdjacentElement("beforeend", meinNav);
/*
=========== Markierung entfernen des Nav-Punktes,
dessen zugehöriger Artikel gerade im sichtbaren Bereich ist =======
*/
// entfernt dieser Klasse,
// falls schon ein Link so markiert ist
function markierungEntfernen() {
const imFocus = document.querySelector("header .imFocus");
if (imFocus !== null) {
imFocus.classList.remove("imFocus");
}
}
// ===== InterSectionObserver ======
// übernommen von https://24ways.org/2019/beautiful-scrolling-experiences-without-libraries/
// verwandelt die NodeList in einem Schritt in ein Array
const sections = [...document.querySelectorAll("main article")];
// console.log(sections[1]);
// wieviel vom jeweiligen Artikel muss sichtbar sein
// noch auf kleinem Screen testen und evtl. anpassen!
const grenze = 0.8;
let options = {
rootMargin: "0px",
threshold: grenze
};
const callback = (entries, observer) => {
entries.forEach(entry => {
const { target } = entry;
// console.log(entry, target);
if (entry.intersectionRatio >= grenze) {
// target.classList.add("is-visible");
// console.log(target.id);
const myID = target.id;
const myA = document.querySelector(`header a.${myID}`);
myA.classList.add("imFocus");
} else {
// target.classList.remove("is-visible");
markierungEntfernen();
}
});
};
const observer = new IntersectionObserver(callback, options);
sections.forEach((section, index) => {
observer.observe(section);
});
Closest: sehr praktisch, um ein "Vorfahren-Element" zu finden – überspringt auch Ebenen…
// hier wird nach etwas "oberhalb im DOM" gesucht, das die Klasse "karten" hat
// praktisch, wenn das nicht direkt das parentElement ist!
const buttonParentElement = deleteButton.closest('.karten');
// Entfernt von Elementen, die die jeweiligen Klassen haben, diese Klasse
function elementKlasseEntfernen(klasse) {
const aktivElement = document.querySelector(`.${klasse}`);
// console.log(aktivKlasse);
// Abfrage, ob es so ein Element aktuell gibt
// am Anfang gibt es dies ja nicht
if (aktivElement != null) {
aktivElement.classList.remove(klasse);
}
}
// alle Buttons ==================
// hier muss nicht die gleiche Variabe "ereignis" stehen!
// man kann auch was anderes verwenden…
function tuWas(ereignis) {
// erst mal die Klasse "angeklickt" und "aktiv" entfernen
// falls diese schon vorhanden sind
// damit stellt man sicher, dass immer nur ein Artikel/Button aktiv ist
elementKlasseEntfernen("angeklickt"); // Button-Klasse
elementKlasseEntfernen("aktiv"); // Artikel-Klasse
// das entscheidende ist das "target"!
// durch "closest" wird nur der "Eltern-Artikel" angesprochen
const myArticle = ereignis.target.closest("article");
myArticle.style.transition = "all .3s ease-in-out";
myArticle.classList.add("aktiv");
const myP = ereignis.target.previousElementSibling;
const myPText = myP.textContent;
// console.log(myPText);
//const myH2 = ereignis.target.parentElement.firstElementChild;
const myH2 = myArticle.querySelector("h2");
const myH2Text = myH2.textContent;
// console.log(myH2Text);
// der Button selbst bekommt Klasse "angeklickt" getoggelt
ereignis.target.style.transition = "all .3s ease-in-out";
ereignis.target.classList.add("angeklickt");
}
const myButtons = document.querySelectorAll("button");
// console.log(myButtons);
myButtons.forEach(function(ereignis) {
// console.log(ereignis.textContent);
// const myArticle = ereignis.closest("article");
// myArticle.style.border = "1px solid red";
// console.log(myArticle.innerHTML);
//const myP = ereignis.previousElementSibling;
//console.log(myP.textContent);
ereignis.addEventListener("click", tuWas);
});
// das div.cards wird erzeugt und innerhalb des main eingefügt
const cardParent = document.createElement('div');
cardParent.classList.add('cards');
const myMain = document.querySelector('main');
myMain.insertAdjacentElement('afterbegin', cardParent);
// die jeweiligen Karten werden erzeugt
function generatePlayerCard(cardName, name, age, height) {
const myHTML = `
<div class="karten ${cardName}">
<h2>${name} - ${age} Jahre alt</h2>
<p>Diese Person ist ${height}cm groß und ${age} Jahre alt. In Hunde-Jahren wäre sie ${age * 7}!</p>
<button type="button">× löschen</button>
<div>
`;
return myHTML;
}
// das Löschen via Button "Delete Card"
function deleteButtonClick(cardName) {
const deleteButton = cardParent.querySelector(`.${cardName} button`);
// das jeweilge ParentElement (also die Karte) wird entfernt
deleteButton.addEventListener('click', function() {
// hier wird nach etwas "oberhalb im DOM" gesucht, das die Klasse "karten" hat
// praktisch, wenn das nicht direkt das parentElement ist!
const buttonParentElement = deleteButton.closest('.karten');
buttonParentElement.style.opacity = 0.1;
// buttonParentElement.remove();
});
}
function players(cardName, name, age, height) {
let myCardName = cardName;
myCardName = document.createElement('div');
// bei "outerHTML"muss diese Reihenfolge sein,
// weil sonst das Element "playerCard" noch nicht im DOM exisiert
// und somit auch nicht das "outerHTML" ausgetauscht werden kann!
cardParent.insertAdjacentElement('beforeend', myCardName);
myCardName.outerHTML = generatePlayerCard(cardName, name, age, height);
// Aufruf, die Löschfunktion einzubauen
deleteButtonClick(cardName);
}
// Aufruf zum Erzeugen der Karten
players('playerCard1', 'Estelle', 22, 179);
players('playerCard2', 'Leander', 20, 190);
players('playerCard3', 'Gina', 54, 180);
players('playerCard4', 'Thias', 55, 189);
Bei Klicken auf ein beliebiges Element wird eine rote Outline sichtbar, im "Hover" und im "console.log" wird die Bezeichnung ausgegeben.
// super praktisch, um rauszubekommen, wo man gerade draufgeklickt hat!
window.addEventListener("click", function(thias) {
// der rote Rahmen um angeklickte Elemente wird
// beim Anklicken des nächsten Elementes wieder entfernt
const clicked = document.querySelector(".clicked");
if (clicked != null) {
clicked.style.outline = "none";
clicked.classList.remove("clicked");
}
// das outerHTML des angeklickten Elementes
const infoText = thias.target.outerHTML;
// es soll nur der HTML-Code bis zum ersten ">" ausgegeben werden
const infoTextArray = infoText.split(">");
const infoTextFirstChunk = `${infoTextArray[0]}>`;
// Ausgabe im Title, um was es sich gerade handelt
thias.target.title = infoTextFirstChunk;
console.log(`gerade angeklickt: ${infoTextFirstChunk}`);
// um das angeklickte Element wird ein roter Rahmen gezogen
thias.target.style.outline = "1px solid red";
thias.target.classList.add("clicked");
});
Beim Klicken auf die Listen-Elemente werden beim Bild "Platzhalter" die Bilder ausgetauscht
const listItems = document.querySelectorAll("nav a");
function aktivEntfernen() {
const aktivListItem = document.querySelector("nav a.aktiv");
if (aktivListItem != null) {
aktivListItem.classList.remove("aktiv");
}
}
function bilderTausch(ereignis) {
// das bisher aktive Element wieder normal stellen
aktivEntfernen();
// angeklicktes Nav-Element markieren
ereignis.target.classList.add("aktiv");
// bei diesem Bild sollen die Src ausgetauscht werden
const austauschBild = document.querySelector("#austauschbild img");
const neuSrc = ereignis.target.href; // was steht in der jeweiligen Verlinkung?
austauschBild.src = neuSrc;
// figCaption anpassen
const austauschBildCaption = document.querySelector(
"#austauschbild figcaption"
);
const neuCaption = ereignis.target.title;
austauschBildCaption.textContent = neuCaption;
// damit der Browser nicht dorthin wechselt
ereignis.preventDefault();
}
listItems.forEach(function(ereignis) {
ereignis.addEventListener("click", bilderTausch);
});
Selbstablaufende Animation, die mit Klicken auf "Stop Animation" angehalten werden kann
// das zu animierende Element
const animiert = document.querySelector("#animiert");
// Breite und Höhe des Browsers
// ACHTUNG: Wird aktuell nicht abgefragt,
// wenn die Größe des Browserfensters geändert wird!
const browserBreite = window.innerWidth;
const browserHoehe = window.innerHeight;
// die Wachstumsschritte
let x = 2;
let y = 2;
function quadratAnimation() {
const rect = animiert.getBoundingClientRect();
// akt. X- und Y-Position des div#animiert
const horizontalAktuell = rect.x;
const vertikalAktuell = rect.y;
// Breite & Hoehe des div#animiert
const animiertBreite = rect.width;
const animiertHoehe = rect.height;
// console.log(animiertBreite, animiertBreite);
// wenn das Div an die Grenzen des Browsers stößt, dreht die Animation um
if (
horizontalAktuell > browserBreite - animiertBreite ||
horizontalAktuell < 0
) {
x *= -1;
}
if (vertikalAktuell > browserHoehe - animiertHoehe || vertikalAktuell < 0) {
y *= -1;
}
// Größe des div#animiert passt sich an
const horizontalNeu = horizontalAktuell + x;
const vertikalNeu = vertikalAktuell + y;
animiert.style.left = `${horizontalNeu}px`;
animiert.style.top = `${vertikalNeu}px`;
animiert.style.width = `${horizontalNeu}px`;
animiert.style.height = `${vertikalNeu}px`;
}
// nur wenn das so geschrieben ist, kann man mit "clearInterval"
// die Animation wieder anhalten
const myAni = setInterval(quadratAnimation, 50);
// Animation anhalten
function quadratAnimationStop() {
window.clearInterval(myAni);
}
const stopAniKnopf = document.querySelector("#stopAni");
stopAniKnopf.addEventListener("click", quadratAnimationStop);
Werte, die links angegeben werden, werden rechts verwendet. Via „window.setInterval“ werden viele Funktionen definiert, die automatisch alle 200ms ausgeführt werden und die Textinhalte links abfragen und rechts anwenden.
// anonyme Funktion mit vielen "Sub-Funktionen"
window.setInterval(function() {
ueberschriftInhaltAendern();
ueberschriftPostionAendern();
ueberschriftGroesseAendern();
…
}, 200);
const ueberschrift = document.querySelector("#ueberschrift");
function ueberschriftInhaltAendern() {
const inputTextInhalt = document.querySelector("#inputText").innerText;
if (inputTextInhalt !== "Hier Text eingeben") {
ueberschrift.innerText = inputTextInhalt;
}
}
function ueberschriftPostionAendern() {
const xPosition = parseInt(document.querySelector("#xPosition").innerText);
const yPosition = parseInt(document.querySelector("#yPosition").innerText);
ueberschrift.style.left = `${xPosition}px`;
ueberschrift.style.top = `${yPosition}px`;
}
function ueberschriftFarbeAendern() {
const schriftFarbe = document.querySelector("#schriftFarbe").innerText;
ueberschrift.style.color = `${schriftFarbe}`;
}
function HGFarbeAendern() {
const HGFarbe = document.querySelector("#HGFarbe").innerText;
document.querySelector("#ausgabe").style.backgroundColor = `${HGFarbe}`;
}
function HGBildAendern() {
const HGAktiv = document.querySelector(".HGBilder .aktiv");
const ImageSource = HGAktiv.getAttribute("src");
// Zerlegen der Source und finden des BildNamens
const ImageSourceArray = ImageSource.split("/");
// der Name des Img ohne Pfad
const HGBild = ImageSourceArray[3];
// das HGBild wird als HintergrundBild beim div#ausgabe festgelegt
document.querySelector(
"#ausgabe"
).style.backgroundImage = `url(../p/gross/${HGBild})`;
}
function ueberschriftGroesseAendern() {
const schriftGroesse = parseInt(
document.querySelector("#schriftGroesse").innerText
);
ueberschrift.style.fontSize = `${schriftGroesse}px`;
}
function ueberschriftDrehen() {
const rotation = parseInt(document.querySelector("#rotation").innerText);
ueberschrift.style.transform = `rotate(${rotation}deg)`;
}
function ueberschriftTransparenz() {
const transparenz = parseFloat(
document.querySelector("#transparenz").innerText
);
ueberschrift.style.opacity = transparenz;
}
// anonyme Funktion mit vielen "Sub-Funktionen"
window.setInterval(function() {
ueberschriftInhaltAendern();
ueberschriftPostionAendern();
ueberschriftGroesseAendern();
ueberschriftFarbeAendern();
HGFarbeAendern();
HGBildAendern();
ueberschriftDrehen();
ueberschriftTransparenz();
}, 200);
// ein zufällig ausgewähltes Bild wird gleich beim Start der Seite sichtbar
// ohne diese Zeile würde das Browserfenster erst mal leer bleiben
function getRandomNumberInRange(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
// zufälliges Bild beim Laden der Seite
document
.querySelector(`.HGBilder img:nth-of-type(${getRandomNumberInRange(1, 4)})`)
.classList.add("aktiv");
function klasseEntfernen(klasse) {
const aktEle = document.querySelector(`.HGBilder .${klasse}`);
aktEle.classList.remove(klasse);
}
function klickKlasseEin(ereignis) {
klasseEntfernen("aktiv");
ereignis.target.classList.add("aktiv");
}
// die Vorschau-Bilder links unten
const meineHGBilder = document.querySelectorAll(".HGBilder img");
meineHGBilder.forEach(function(ereignis) {
ereignis.addEventListener("click", klickKlasseEin);
});
Via Slider die Schriftgröße verändern…
Bei "/Users/thias/docs/thiasJobs/hm/~web/scripte/content/9-wise19/1-webdesign/7-interaction-webfonts-webtypo/18-variable-fonts-eigene-experimente/variable-font-slider" gibt es ein ausführlicheres (nicht von mir) erstelltes Beispiel…
const mySlider = document.querySelector(".slider");
const myButton = document.querySelector("button");
const myOutput = document.querySelector("#output");
const myOutputAnzeige = document.querySelector(".outputAnzeige");
// Anzeige zieht beim Start den "value"-Wert, der bei "input" eingestellt ist
myOutputAnzeige.innerHTML = `${mySlider.value} px`;
// verschiebt man den Slider, wird der Value verändert
// und entsprechend auch die Schriftgröße
function werteAnpassen() {
// statt "this" könnte man hier auch "mySlider" schreiben
myOutput.style.fontSize = `${this.value}px`;
// "thinSpace" eingefügt - schaut besser aus…
myOutputAnzeige.innerHTML = `${this.value} px`;
}
// alternative, ältere (?) Schreibweise
// mySlider.oninput = function() {};
mySlider.addEventListener("input", werteAnpassen);
// Funktion des Reset-Buttons
function wertZurueckSetzen() {
const resetWert = 20;
mySlider.value = `${resetWert}`;
myOutputAnzeige.innerHTML = `${resetWert} px`;
myOutput.style.fontSize = `${resetWert}px`;
}
myButton.addEventListener("click", wertZurueckSetzen);
// damit wird gleich beim Laden der Seite der bei "wertZurueckSetzen"
// eingestellte Wert (hier "20")für die Schriftgröße verwendet
wertZurueckSetzen();
Slideshow mit aufspringenden Groß-Bildern – noch hartes „Aufploppen“ und kein Weiterspringen zum nächsten Bild möglich!
const myFigures = document.querySelectorAll("figure");
function abDeckerWeg() {
document.querySelector(".abDecker").remove();
}
function grossBildZeigen(ereignis) {
const mySrc = ereignis.target.src;
// Pfad zu den Bildern im Ordner "gross" ändern
const newSrc = mySrc.replace("klein", "gross");
// steht bei "data-beschreibung" im HTML
const myBeschreibung = ereignis.target.dataset.beschreibung;
// Erzeugen des abdeckenden Divs, das sich über alles legt
// CSS-Angaben bei style.css
const abDecker = document.createElement("div");
abDecker.classList.add("abDecker");
const myFigure = document.createElement("figure");
const myImg = document.createElement("img");
myImg.src = `${newSrc}`; // Pfad mit "p/gross"
const myFigcaption = document.createElement("figcaption");
myFigcaption.textContent = myBeschreibung;
// ein kleiner × zum Anzeigen des Schliess-Buttons
const mySchliessX = document.createElement("p");
mySchliessX.textContent = "×";
mySchliessX.title = "Grossbild-Ansicht schließen";
mySchliessX.classList.add("schliessButton");
// die Sachen zuerst beim abDecker anhängen und dann erst am body!
abDecker.appendChild(myFigure);
myFigure.appendChild(myImg);
myFigure.appendChild(myFigcaption);
myFigure.appendChild(mySchliessX);
document.body.appendChild(abDecker);
abDecker.addEventListener("click", abDeckerWeg);
}
myFigures.forEach(function(ereignis) {
ereignis.addEventListener("click", grossBildZeigen);
});
Slideshow mit aufspringenden Groß-Bildern – eigentlich mit Gracefull Degradation, weil die Links zu den Großbildern als echtes Links eingebaut sind…
Macht die Sache aber deutlich komplizierter, weil das "href" nicht ausgelesen werden kann -- unklar warum!
const myFigures = document.querySelectorAll("main figure");
function abDeckerWeg() {
document.querySelector(".abDecker").remove();
}
function grossBildZeigen(ereignis) {
ereignis.preventDefault();
// das Abfragen der href klappt leider nicht!
// es kommt nur "undefined" raus!
// deswegen habe ich bei den Datasets "data-gross" eingefügt
// wo der Link zum Großbild eingebaut ist -- umständlich!
const testHref = ereignis.target;
console.log(testHref);
const grossBildSource = ereignis.target.dataset.gross;
console.log(grossBildSource);
// steht bei "data-beschreibung" im HTML
const myBeschreibung = ereignis.target.dataset.beschreibung;
// Erzeugen des abdeckenden Divs, das sich über alles legt
// CSS-Angaben bei style.css
const abDecker = document.createElement("div");
abDecker.classList.add("abDecker");
const myFigure = document.createElement("figure");
const myImg = document.createElement("img");
myImg.src = `${grossBildSource}`; // Pfad mit "p/gross"
const myFigcaption = document.createElement("figcaption");
myFigcaption.textContent = myBeschreibung;
// ein kleiner × zum Anzeigen des Schliess-Buttons
const mySchliessX = document.createElement("p");
mySchliessX.textContent = "×";
mySchliessX.title = "Grossbild-Ansicht schließen";
mySchliessX.classList.add("schliessButton");
// die Sachen zuerst beim abDecker anhängen und dann erst am body!
abDecker.appendChild(myFigure);
myFigure.appendChild(myImg);
myFigure.appendChild(myFigcaption);
myFigure.appendChild(mySchliessX);
document.body.appendChild(abDecker);
abDecker.addEventListener("click", abDeckerWeg);
}
myFigures.forEach(function(ereignis) {
ereignis.style.border = "2px dotted red";
const ereignisImg = ereignis.querySelector("img");
const ereignisFigcaption = ereignis.querySelector("figcaption");
console.log(ereignisImg.dataset.gross);
ereignisImg.addEventListener("click", grossBildZeigen);
// unklar, warum das nicht ohne geht -- vielleicht was mit Vererbung?
ereignisFigcaption.addEventListener("click", function(e) {
e.preventDefault();
alert("Bitte direkt auf das Foto klicken!");
});
});
Lässt erst das Weitergehen auf eine andere Seite zu, wenn der Benutzer das bestätigt hat.
const sz = document.querySelector(".sz");
sz.addEventListener("click", function(ereignis) {
// aufspringender Dialog, der entweder "true" oder "false" zurückgibt
const sollSiteSichAendern = confirm("Wirklich weitergehen?");
// console.log(sollSiteSichAendern);
// wenn auf "Cancel" geklick wurde, wird das Weitergehen geblockt!
// lange Schreibweise:
// if (sollSiteSichAendern == false)
if (!sollSiteSichAendern) {
ereignis.preventDefault();
}
});
Beim Eingeben werden verschiedene Events ausgelöst
const testInput = document.querySelector('[name="testInput"]');
const focusOutput = document.querySelector("#ausgabe1");
const blurOutput = document.querySelector("#ausgabe2");
const keydownOutput = document.querySelector("#ausgabe3");
const keyupOutput = document.querySelector("#ausgabe4");
// wenn man innerhalb des input#ausgabe klickt
testInput.addEventListener("focus", function(ereignis) {
focusOutput.textContent = testInput.value;
});
// wenn man das input#ausgabe verlässt (es den Focus verliert)
testInput.addEventListener("blur", function(ereignis) {
blurOutput.textContent = testInput.value;
});
// wenn man innerhalb input#ausgabe auf der Tastatur ein Taste RUNTER drückt
testInput.addEventListener("keydown", function(ereignis) {
keydownOutput.textContent = testInput.value;
});
// wenn man innerhalb input#ausgabe auf der Tastatur eine Taste loslässt
testInput.addEventListener("keyup", function(ereignis) {
keyupOutput.textContent = testInput.value;
});
Die verschiedenen Elemente im Browser können sowohl angeklickt als auch via "Tabbing" erreicht und ausgelöst werden
const myLink = document.querySelector("a.demo");
const myButton = document.querySelector("button[name='mybutton']");
const myDiv = document.querySelector("div");
const mySpan = document.querySelector("span");
const ausgabe = document.querySelector("#ausgabe");
function handleEvent(ereignis) {
ereignis.preventDefault();
// console.log(event.target.dataset.aussage);
if (ereignis.type === "click" || ereignis.key === "Enter") {
ausgabe.textContent = event.target.dataset.aussage;
}
}
myLink.addEventListener("click", handleEvent);
myButton.addEventListener("click", handleEvent);
myDiv.addEventListener("click", handleEvent);
mySpan.addEventListener("click", handleEvent);
myLink.addEventListener("keyup", handleEvent);
myButton.addEventListener("keyup", handleEvent);
myDiv.addEventListener("keyup", handleEvent);
mySpan.addEventListener("keyup", handleEvent);
Beginner Javascript (Wes Bos) | Etch a Sketch | Film 33
const canvas = document.querySelector("#etch-a-sketch");
const ctx = canvas.getContext("2d"); // 2D Darstellung im canvas
const shakebutton = document.querySelector(".shake");
const MOVE_AMOUNT = 80;
// Grundeinstellungen
ctx.lineJoin = "round";
ctx.lineCap = "round";
ctx.lineWidth = MOVE_AMOUNT;
// Farbe des Striches festlegen
let hue = 0;
ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
// Kursform, wenn man als Variablen die gleichen Bezeichnungen
// verwendet, die das Element auch hat
// normale Schreibweise: const width = canvas.width;
const { width, height } = canvas;
// zufällige Anfangsposition
let x = Math.floor(Math.random() * width);
let y = Math.floor(Math.random() * height);
// Anfangspunkt zeichnen
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x, y);
ctx.stroke();
// zeichen-funktion, options ausführlich
/*
function draw(options) {
console.log(options.key);
}
*/
function draw({ key }) {
// console.log(key);
// Strichfarbe anpassen
hue += 1;
ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
// das eigentliche zeichnen
ctx.beginPath();
ctx.moveTo(x, y);
switch (key) {
case "ArrowUp":
y = y - MOVE_AMOUNT;
break;
case "ArrowDown":
y = y + MOVE_AMOUNT;
break;
case "ArrowRight":
x = x + MOVE_AMOUNT;
break;
case "ArrowLeft":
x = x - MOVE_AMOUNT;
break;
default:
break;
}
ctx.lineTo(x, y);
ctx.stroke();
}
// function beim Drücken der Arrow-keys
function handleKey(ereignis) {
// damit kann man dann aber nicht mehr scrollen oder Seite neu laden!
//ereignis.preventDefault();
// nur wenn einer der "Arrow"-Keys gedrückt wurde
if (ereignis.key.includes("Arrow")) {
ereignis.preventDefault(); // kein Scrollen!
// ein Objekt, das auch mehr Optionen haben könnte -- später!
draw({ key: ereignis.key });
// console.log(ereignis.key);
}
// löschen mit der "Backspace"-Taste (rechts oben <-)
if (ereignis.key === "Backspace") {
ereignis.preventDefault();
clearCanvas();
}
// gibt die Bezeichnung (z.b. "ArrowUp" oder "Enter") aus
// console.log(ereignis.key);
}
function clearCanvas() {
// "shake" enthält eine CSS-Animation -- siehe CSS!
canvas.classList.add("shake");
// das ist super, damit kann man abwarten,
// bis die Animation zu Ende ist und dann die Klasse entfernen
// sonst geht das beim nächsten Mal nicht mehr, weil die Klasse "shake"
// ja schon gesetzt ist!
// es gibt auch "transitionend" - ausprobieren!
canvas.addEventListener(
"animationend",
function() {
console.log("Animation beended!");
canvas.classList.remove("shake");
// die Zeichnungen im Canvas ausradieren
// beachte, dass die erst ausgelöscht wird, wenn die "shake"-animation
// beendet ist!
ctx.clearRect(0, 0, width, height);
},
{ once: true } // damit läuft dieser "addEventListener" nur einmal und addiert sich nicht auf
);
}
shakebutton.addEventListener("click", clearCanvas);
// bei window, damit der key-Events überall bemerkt wird
window.addEventListener("keydown", handleKey);
Die 2. Animation startet, sobald die 1. fertig ist. Das wird via JS ('addEventListener("animationend")') abgefragt.
const button = document.querySelector("button");
const animiert1 = document.querySelector("#animiert1");
const animiert2 = document.querySelector("#animiert2");
function aniStarten() {
animiert1.classList.add("aktiv");
animiert1.addEventListener(
"animationend",
function() {
console.log("Animation 1 beended!");
animiert1.classList.remove("aktiv");
// 2. Animation fängt an, wenn die 1. fertig ist!
animiert2.classList.add("aktiv");
},
{ once: true } // damit läuft dieser "addEventListener" nur einmal und addiert sich nicht auf
);
animiert2.addEventListener(
"animationend",
function() {
console.log("Animation 2 beended!");
animiert2.classList.remove("aktiv");
},
{ once: true }
);
}
button.addEventListener("click", aniStarten);
Beim Ein- und Ausblenden des Bildes werden 2 Transitions hintereinander ausgeführt. Das Zeigen & Verbergen des Großbildes ist auch über Tabbing und Enter möglich!
const button = document.querySelector("button");
const abdecker = document.querySelector("#abdecker");
const grossBild = document.querySelector("#grossBild");
const schliesser = document.querySelector("#schliesser");
function grossBildZeigen() {
abdecker.classList.add("sichtbar");
abdecker.addEventListener(
"transitionend",
function() {
console.log("Abdecker-Transition beended!");
grossBild.classList.add("sichtbar");
},
{ once: true } // damit läuft dieser "addEventListener" nur einmal und addiert sich nicht auf
);
}
function grossBildWeg() {
grossBild.classList.remove("sichtbar");
grossBild.addEventListener(
"transitionend",
function() {
console.log("grossBild-Transition beended!");
abdecker.classList.remove("sichtbar");
},
{ once: true }
);
}
button.addEventListener("click", grossBildZeigen);
// "×" zum Schliessen des grossBild
schliesser.addEventListener("click", grossBildWeg);
abdecker.addEventListener("click", grossBildWeg);
// mit irgendeiner Taste das
schliesser.addEventListener("keyup", grossBildWeg);
Wie Beispiel vorher, nur die Elemente werden via JS generiert, die für die Großbild-Darstellung benötigt werden. Eigenes CSS-Datei (popup.css) für die Popup-Sachen…
const button = document.querySelector("button");
// ============= Elemente generieren =====================
// die für die Großbild-Darstellung benötigt werden
// CSS-Anweisungen befinden sich bei "c/popup.css"
const abdecker = document.createElement("div");
abdecker.id = "abdecker";
const grossBild = document.createElement("figure");
grossBild.id = "grossBild";
const grossBildImg = document.createElement("img");
grossBildImg.src = "https://picsum.photos/600";
grossBildImg.alt = "Cooles Bild";
const grossBildFigcaption = document.createElement("figcaption");
grossBildFigcaption.textContent = "Coole Bildunterschrift";
const schliesser = document.createElement("span");
schliesser.id = "schliesser";
schliesser.textContent = "×";
schliesser.role = "button";
schliesser.tabIndex = "0"; // Schreibweise in JS beachten!
schliesser.title = "Großbild schliessen";
// zusammenbauen und einfügen
grossBild.appendChild(grossBildImg);
grossBild.appendChild(grossBildFigcaption);
grossBild.appendChild(schliesser);
abdecker.appendChild(grossBild);
document.body.appendChild(abdecker);
// ============== Ende der Generierung ===============
function grossBildZeigen() {
abdecker.classList.add("sichtbar");
abdecker.addEventListener(
"transitionend",
function() {
console.log("Abdecker-Transition beended!");
grossBild.classList.add("sichtbar");
},
{ once: true } // damit läuft dieser "addEventListener" nur einmal und addiert sich nicht auf
);
}
function grossBildWeg() {
grossBild.classList.remove("sichtbar");
grossBild.addEventListener(
"transitionend",
function() {
console.log("grossBild-Transition beended!");
abdecker.classList.remove("sichtbar");
},
{ once: true }
);
}
button.addEventListener("click", grossBildZeigen);
// "×" zum Schliessen des grossBild
schliesser.addEventListener("click", grossBildWeg);
abdecker.addEventListener("click", grossBildWeg);
// mit irgendeiner Taste das
schliesser.addEventListener("keyup", grossBildWeg);
Slideshow mit aufspringenden Groß-Bildern – incl. stufenweiser Transitions und „Loading-Animation“! Noch kein Weiterspringen zum nächsten Bild möglich!
Leider sind nur die Bilder direkt anklickbar – nicht die figCaptions. Ist mir noch nicht klar, warum das nicht geht!
const myLinks = document.querySelectorAll("main figure");
// ============= Elemente vorab generieren =====================
/* CSS-Angaben für diese Elemente bei "popup.css" */
const abdecker = document.createElement("div");
abdecker.id = "abdecker";
// die animierte "Sanduhr", die anzeigt, dass ein großes Bild geladen wird
const abdeckerSanduhr = document.createElement("img");
abdeckerSanduhr.id = "abdeckerSanduhr";
abdeckerSanduhr.src = "p/waiting_white.svg";
const grossBild = document.createElement("figure");
grossBild.id = "grossBild";
const grossBildImg = document.createElement("img");
const grossBildFigcaption = document.createElement("figcaption");
const schliesser = document.createElement("span");
schliesser.id = "schliesser";
schliesser.textContent = "×";
schliesser.role = "button";
schliesser.tabIndex = "0"; // Schreibweise in JS beachten!
schliesser.title = "Großbild schliessen";
// zusammenbauen und einfügen
grossBild.appendChild(grossBildImg);
grossBild.appendChild(grossBildFigcaption);
grossBild.appendChild(schliesser);
abdecker.appendChild(abdeckerSanduhr);
abdecker.appendChild(grossBild);
document.body.appendChild(abdecker);
// ============== Ende der Generierung ===============
function grossBildZeigen(ereignis) {
ereignis.preventDefault();
const mySrc = ereignis.target.src;
// Pfad zu den Bildern im Ordner "gross" ändern
const newSrc = mySrc.replace("klein", "gross");
// console.log("newSrc: " + newSrc);
grossBildImg.src = newSrc;
const myBeschreibung = ereignis.target.dataset.beschreibung;
grossBildImg.alt = myBeschreibung;
grossBildFigcaption.textContent = myBeschreibung;
abdeckerSanduhr.classList.add("sichtbar");
abdecker.classList.add("sichtbar");
abdecker.addEventListener(
"transitionend",
function() {
// console.log("Abdecker-Transition beended!");
grossBild.classList.add("sichtbar");
},
{ once: true } // damit läuft dieser "addEventListener" nur einmal und addiert sich nicht auf
);
}
function grossBildWeg() {
abdeckerSanduhr.classList.remove("sichtbar");
grossBild.classList.remove("sichtbar");
grossBild.addEventListener(
"transitionend",
function() {
// console.log("grossBild-Transition beended!");
abdecker.classList.remove("sichtbar");
// "leeres" Bild - damit nicht beim nächsten "grossBildZeigen"
// noch teilweise das alte Bild gezeigt wird und dann während
// der Transition ausgetauscht wird - das stört sehr!
// besonders merkbar, wenn die Bilder vom Server geladen werden
// und eine langsame Internet-Verbindung besteht
grossBildImg.src = "";
},
{ once: true }
);
}
myLinks.forEach(function(ereignis) {
ereignis.addEventListener("click", grossBildZeigen);
});
// "×" zum Schliessen des grossBild
schliesser.addEventListener("click", grossBildWeg);
abdecker.addEventListener("click", grossBildWeg);
// mit irgendeiner Taste das
schliesser.addEventListener("keyup", grossBildWeg);
Vom Javascript und CSS identisch mit dem vorherigem Beispiel. Hier nur eine Automatisierung eingebaut, dass die Bilder automatisch via PHP eingelesen werden – egal wie viele…
<body>
<header>
<h1>Popup-Images | PHP Integration</h1>
<h2>Es werden automatisch alle Bilder des jeweiligen Ordners eingelesen</h2>
</header>
<?php
$ordner = './p/klein';
include './php/bildersammlung.php'; // externes php-script zum autom. einfuegen von bilder aus $ordner
?>
<script src="js/script.js?v=1.0"></script>
</body>
identisch mit dem vorherigem Beispiel!
<?php
// $ordner wird bei "index.php" festgelegt
$verzeichnis = dir($ordner);
// nächste Zeile sorgt dafür, dass das Array entleert wird und man es somit mehrfach verwenden kann!
$files = array();
while($eintrag=$verzeichnis->read())
$files[] = $eintrag; // ein array!
$verzeichnis->close();
sort($files); // alphabetisch sortieren
$pfad = $ordner . '/';
// echo($pfad);
$clength = count($files)-2;
echo "<p class='hinweis'>Das Verzeichnis enthält <strong style='color:red;'>$clength</strong> Bilder.</p>\n\n";
echo " <main>\n";
foreach ($files as $eintrag){
// http://www.php.net/manual/de/language.types.string.php
// naechste zwei Zeilen fragen ab, ob Eintrag mit einem "." anfängt!
$first = $eintrag[0];
if ($first != "."){
$dateinamenTeile = explode(".", $eintrag);
$anfang = $dateinamenTeile[0];
$bild = $pfad . $eintrag;
$grossbild = './p/gross/' . $eintrag;
// http://php.net/manual/de/function.exif-read-data.php
$exif = exif_read_data($bild, 0, true);
// Herauslesen eines einzelnen Wertes aus dem Array
$aufnahmeDatum = $exif['EXIF']['DateTimeOriginal'];
echo "
<figure>
<a href='$grossbild' title='Bild in Original-Größe zeigen'>
<img src='" . $bild . "' data-beschreibung='aufgenommen: " . $aufnahmeDatum . "' alt='" . $eintrag . "'>
</a>
<figcaption>$aufnahmeDatum</figcaption>
</figure>
\n";
}
}
echo " </main>\n\n";
?>
incl. selbst ablaufender Slideshow und dazugehörigen Buttons
HTML/CSS/PHP/JS-Code nicht identisch mit dem vorherigem Beispiel!
<body>
<header>
<h1>Popup-Images | PHP Integration</h1>
<h2>Es werden automatisch alle Bilder des jeweiligen Ordners eingelesen</h2>
</header>
<?php
$ordner = './p/klein';
include './assets/php/bildersammlung.php'; // externes php-script zum autom. einfuegen von bilder aus $ordner
?>
<script src="js/script.js?v=1.0"></script>
</body>
// ============== automatische Slideshow ==============
// wie lange bleibt ein Slide stehen -- Sekunden
const standDauer = 5;
// Variable legt fest, ob Slideshow automatisch abläuft oder nicht
// hier erst mal pausiert, bis User auf "Space"-Taste oder "Play"-Symbol drückt
let slideShowLaufen = false;
// globale Variable, die speichert, auf welches kleines Bild
// man gerade geklickt hat
// wird für das Weiter- und Zurückgehen bei den Grossbildern benötigt
let kleinBildID;
// ===================== eingebaute KeyEvents =====================
/*
ArrowDown, ArrowRight: Grossbild eins weiter (nur wenn GrossBild sichtbar)
ArrowUp, ArrowLeft: Grossbild eins zurück (nur wenn GrossBild sichtbar)
Escape, Backspace: Grossbild-Ansicht und Slideshow beenden
SPACE (Leertaste): automatische Slideshow starten/beenden (nur wenn GrossBild sichtbar)
*/
// =============== erst Bilder zeigen, wenn alle geladen sind ===============
const loading = document.querySelector("#loading");
const galerie = document.querySelector(".galerie");
window.addEventListener("load", function() {
loading.style.opacity = "0";
galerie.classList.add("sichtbar");
});
// ============= Elemente für GrossBild-Sachen vorab generieren =====================
const abdecker = document.createElement("div");
abdecker.id = "abdecker";
// die animierte "Sanduhr", die anzeigt, dass ein großes Bild geladen wird
const abdeckerSanduhr = document.createElement("img");
abdeckerSanduhr.id = "abdeckerSanduhr";
abdeckerSanduhr.src = "./assets/p/waiting_white.svg";
const grossBild = document.createElement("figure");
grossBild.id = "grossBild";
const grossBildImg = document.createElement("img");
const grossBildFigcaption = document.createElement("figcaption");
const schliesser = document.createElement("span");
schliesser.id = "schliesser";
schliesser.textContent = "×";
schliesser.role = "button";
schliesser.tabIndex = "0"; // Schreibweise in JS beachten!
schliesser.title = "Großbild schliessen";
const weiterSymbol = document.createElement("img");
weiterSymbol.src = "./assets/p/forward.svg";
weiterSymbol.id = "weiterSymbol";
weiterSymbol.role = "button";
weiterSymbol.tabIndex = "0";
weiterSymbol.title = "nächstes Bild zeigen";
const zurueckSymbol = document.createElement("img");
zurueckSymbol.src = "./assets/p/back.svg";
zurueckSymbol.id = "zurueckSymbol";
zurueckSymbol.role = "button";
zurueckSymbol.tabIndex = "0";
zurueckSymbol.title = "vorheriges Bild zeigen";
const playSymbol = document.createElement("img");
playSymbol.src = "./assets/p/play.svg";
playSymbol.id = "playSymbol";
playSymbol.role = "button";
playSymbol.tabIndex = "0";
playSymbol.title = "automatische Slideshow starten/anhalten";
// zusammenbauen und einfügen
grossBild.appendChild(grossBildImg);
grossBild.appendChild(grossBildFigcaption);
grossBild.appendChild(schliesser);
grossBild.appendChild(weiterSymbol);
grossBild.appendChild(zurueckSymbol);
grossBild.appendChild(playSymbol);
abdecker.appendChild(abdeckerSanduhr);
abdecker.appendChild(grossBild);
document.body.appendChild(abdecker);
// ============== Ende der Generierung ===============
function grossBildZeigen(ereignis) {
ereignis.preventDefault();
const myTitle = ereignis.currentTarget.title;
const myLink = ereignis.currentTarget.href;
// zum Zeigen des nächsten bzw. vorherigen Grossbildes
kleinBildID = ereignis.currentTarget.id;
// console.log(`ID des gerade angeklickten Links: ${kleinBildID}`);
grossBildImg.src = myLink;
grossBildImg.alt = myTitle;
grossBildFigcaption.textContent = myTitle;
abdeckerSanduhr.classList.add("sichtbar");
abdecker.classList.add("sichtbar");
abdecker.addEventListener(
"transitionend",
function() {
// console.log("Abdecker-Transition beended!");
grossBild.classList.add("sichtbar");
},
{ once: true } // damit läuft dieser "addEventListener" nur einmal und addiert sich nicht auf
);
}
function grossBildWeg() {
abdeckerSanduhr.classList.remove("sichtbar");
grossBild.classList.remove("sichtbar");
// Slideshow soll nicht weiterlaufen
slideShowLaufen = false;
playSymbol.src = "./assets/p/play.svg";
grossBild.addEventListener(
"transitionend",
function() {
// console.log("grossBild-Transition beended!");
abdecker.classList.remove("sichtbar");
// "leeres" Bild - damit nicht beim nächsten "grossBildZeigen"
// noch teilweise das alte Bild gezeigt wird und dann während
// der Transition ausgetauscht wird - das stört sehr!
// besonders merkbar, wenn die Bilder vom Server geladen werden
// und eine langsame Internet-Verbindung besteht
grossBildImg.src = "";
},
{ once: true }
);
}
// die Links, in denen sich die Bilder der Galerie befinden
const myImageLinks = document.querySelectorAll(".galerie a");
myImageLinks.forEach(function(ereignis) {
ereignis.addEventListener("click", grossBildZeigen);
});
// "×" zum Schliessen des grossBild
schliesser.addEventListener("click", grossBildWeg);
abdecker.addEventListener("click", grossBildWeg);
// ================ Weiterklicken bei sichtbaren Großbild ================
function grossBildTausch(id) {
// console.log(ele);
grossBild.classList.remove("sichtbar");
grossBild.addEventListener(
"transitionend",
function() {
// console.log("Abdecker-Transition beended!");
grossBildImg.src = myImageLinksArray[id].href;
grossBildFigcaption.textContent = myImageLinksArray[id].title;
grossBild.classList.add("sichtbar");
},
{ once: true } // damit läuft dieser "addEventListener" nur einmal und addiert sich nicht auf
);
}
function naechstesBildZeigen(ereignis) {
// sonst würde das Klicken zum "abdecker" durchgereicht,
// der das Grossbild ausblendet…
ereignis.stopPropagation();
// jeweils einen Eintrag im Array weitergehen
if (kleinBildID < myImageLinksArray.length - 1) {
kleinBildID = parseInt(kleinBildID) + 1;
} else {
kleinBildID = 0;
}
grossBildTausch(kleinBildID);
}
function vorherigesBildZeigen(ereignis) {
// sonst würde das Klicken zum "abdecker" durchgereicht,
// der das Grossbild ausblendet…
ereignis.stopPropagation();
// jeweils einen Eintrag im Array weitergehen
if (kleinBildID > 0) {
kleinBildID = parseInt(kleinBildID) - 1;
} else {
kleinBildID = myImageLinksArray.length - 1;
}
grossBildTausch(kleinBildID);
}
// in ein Array umwandeln
const myImageLinksArray = Array.from(myImageLinks);
for (let i = 0; i < myImageLinksArray.length; i++) {
// die ID des jeweiligen Links wird hier automatisch erzeugt
// ACHTUNG: das ist nur eine Ziffer (0, 1, 2 …)! Eigentlich bei IDs nicht erlaubt!
// es gab aber noch keine Probleme…
const meinID = `${i}`;
// und im HTML eingefügt
myImageLinksArray[i].id = meinID;
}
// =============== selbstablaufende Slideshow ================
// lässt die Bilder hintereinander ablaufen
function weiterGehen() {
// jeweils einen Eintrag im Array weitergehen
if (kleinBildID < myImageLinksArray.length - 1) {
kleinBildID = parseInt(kleinBildID) + 1;
} else {
kleinBildID = 0;
}
grossBildTausch(kleinBildID);
}
// startet/stoppt die Slideshow,
// ausgelöst durch "Space"-Taste oder playSymbol zum Anklicken
function slideShowAnAus() {
slideShowLaufen = !slideShowLaufen;
if (slideShowLaufen === true) {
playSymbol.src = "./assets/p/pause.svg";
} else {
playSymbol.src = "./assets/p/play.svg";
}
}
// wenn die Variable "slideShowLaufen" "true" ist, läuft die Slideshow
const cycle = setInterval(function() {
if (slideShowLaufen == true) {
weiterGehen();
}
}, standDauer * 1000);
// ===================== KeyEvents =====================
function handleKey(ereignis) {
// KeyEvents nur, wenn "abdecker" (schwarze Fläche) sichtbar ist
if (abdecker.classList.contains("sichtbar")) {
// console.log(ereignis.key);
// weiterblättern
ereignis.preventDefault();
if (ereignis.key == "ArrowRight" || ereignis.key == "ArrowDown") {
naechstesBildZeigen(ereignis);
}
// zurückblättern
if (ereignis.key == "ArrowLeft" || ereignis.key == "ArrowUp") {
vorherigesBildZeigen(ereignis);
}
// Bildershow beenden
if (ereignis.key == "Escape" || ereignis.key == "Backspace") {
grossBildWeg();
// Slideshow soll dann natürlich nicht mehr laufen
slideShowLaufen = false;
playSymbol.src = "./assets/p/play.svg";
}
// automatische Slideshow starten/anhalten bei Drücken der "Space"-Taste
if (ereignis.key == " ") {
slideShowAnAus();
}
}
}
window.addEventListener("keydown", handleKey);
const weiterButton = document.querySelector("#weiterSymbol");
weiterButton.addEventListener("click", naechstesBildZeigen);
const zurueckButton = document.querySelector("#zurueckSymbol");
zurueckButton.addEventListener("click", vorherigesBildZeigen);
const playButton = document.querySelector("#playSymbol");
playButton.addEventListener("click", function(ereignis) {
ereignis.stopPropagation();
slideShowAnAus();
});
<?php
// $ordner wird bei "index.php" festgelegt
$verzeichnis = dir($ordner);
// nächste Zeile sorgt dafür, dass das Array entleert wird und man es somit mehrfach verwenden kann!
$files = array();
while($eintrag=$verzeichnis->read())
$files[] = $eintrag; // ein array!
$verzeichnis->close();
sort($files); // alphabetisch sortieren
$pfad = $ordner . '/';
// echo($pfad);
$clength = count($files)-2;
// echo "<p class='hinweis'>Das Verzeichnis enthält <strong style='color:red;'>$clength</strong> Bilder.</p>\n\n";
echo "<article class='galerie' id='top'>\n";
foreach ($files as $eintrag){
// http://www.php.net/manual/de/language.types.string.php
// naechste zwei Zeilen fragen ab, ob Eintrag mit einem "." anfängt!
$first = $eintrag[0];
if ($first != "."){
$dateinamenTeile = explode(".", $eintrag);
$anfang = $dateinamenTeile[0];
$bild = $pfad . $eintrag;
$grossbild = './p/gross/' . $eintrag;
// http://php.net/manual/de/function.exif-read-data.php
$exif = exif_read_data($bild, 0, true);
// Herauslesen eines einzelnen Wertes aus dem Array
$aufnahmeDatum = $exif['EXIF']['DateTimeOriginal'];
echo "
<a href='$grossbild' title='$aufnahmeDatum'>
<img src='" . $bild . "' data-beschreibung='aufgenommen: " . $aufnahmeDatum . "' alt='" . $eintrag . "'>
</a>
\n";
}
}
echo " </article><!-- /galerie -->\n\n";
?>
Erst wenn das Bild komplett geladen ist, wird es (animiert) sichtbar geschaltet
const myImg = document.querySelector(".myImg");
function zufallsGanzZahl(min, max) {
return Math.round(Math.random() * (max - min) + min);
}
myImg.src = `https://picsum.photos/${zufallsGanzZahl(1000, 1600)}`;
// erst wenn das Bild aus dem Web komplett geladen ist,
// wird die Klasse "geladen" hinzugefügt
// und das Bild somit sichtbar geschaltet
// beim Inspector/Network auf "Throttling" gehen und ein langsames Netz
// simulieren – dann kann man den Ladeprozess gut sehen!
myImg.addEventListener("load", function() {
myImg.classList.add("geladen");
console.log(myImg.naturalWidth, myImg.naturalHeight);
});
document.body.addEventListener("click", function() {
myImg.classList.remove("geladen");
myImg.src = `https://picsum.photos/${zufallsGanzZahl(400, 1200)}`;
});
document.body.addEventListener("keydown", function() {
myImg.classList.remove("geladen");
myImg.src = `https://picsum.photos/${zufallsGanzZahl(400, 1200)}`;
});
.myImg {
margin-top: 1em;
transform: translateY(-200vh);
transition: all 0.2s;
border-radius: 30px;
box-shadow: 0 0 8px 3px rgba(0, 0, 0, 0.2);
}
.myImg.geladen {
transform: translateY(0);
transition: all 1s;
}
Erst wenn alle Bilder geladen sind, wird der Bereich <main> sichtbar
const main = document.querySelector("main");
const loading = document.querySelector("#loading");
// erst wenn alle Bilder aus dem Web komplett geladen ist,
// wird bei "main" die Klasse "sichtbar" hinzugefügt
// und somit sichtbar geschaltet
// beim Inspector/Network auf "Throttling" gehen und ein langsames Netz
// simulieren – dann kann man den Ladeprozess gut sehen!
// wenn man testen will, ob ALLE Inhalte der jeweiligen Seite geladen sind:
window.addEventListener("load", function() {
loading.style.opacity = "0";
main.classList.add("sichtbar");
});
document.body.addEventListener("keydown", function(ereignis) {
// console.log(ereignis.key);
if (ereignis.key === "r" || ereignis.key === "R") {
loading.style.opacity = "1";
main.classList.remove("sichtbar");
location.reload();
}
});
#loading {
text-align: center;
width: 100%;
position: absolute;
top: 50vh;
left: 0;
transition: all 0.5s;
}
main {
margin: 1.5em auto;
width: 90%;
max-width: 1200px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
grid-gap: 2vmin;
transform: translateY(-250vh);
transition: all 1.5s;
}
main.sichtbar {
transform: translateY(0);
}
Selbstablaufende Uhr (jede Sekunde aktualisiert)
zeit = document.querySelector("#zeit");
// wenn der Wert einstellig ("3") ist, wird er aufgefällt ("03")
function zahlfuellen(wert) {
// einfaches Umwandeln einer Zahl in einen String
let t = wert + "";
if (t.length < 2) {
// "0" vorne anfügen, wird damit auch zu einem String!
t = "0" + wert;
return t;
} else {
// wenn nichts geändert werden soll, einfach den Ausgangswert zurückgeben
return wert;
}
}
// lässt die Borders ober- und unterhalb jede Sekunde Farbe wechseln
function blinkingCursor() {
zeit.classList.toggle("aktiv");
}
function aktuelleZeit() {
const today = new Date();
let minute = today.getMinutes();
let stunde = today.getHours();
let sekunde = today.getSeconds();
zeit.textContent = `${zahlfuellen(stunde)}:${zahlfuellen(minute)}:${zahlfuellen(sekunde)}`;
blinkingCursor();
}
const myAni = setInterval(aktuelleZeit, 1000);
Erst wenn man ganz nach Unten gescrollt hat, wird der "zustimmen"-Button anwählbar. Damit könnte man z.B. auch dafür sorgen, dass ein Video automatisch anfängt zu spielen, sobald dies im sichtbaren Bereich des Browsers ist…
// dort werden scrollTop angezeigt
const scrollInfos = document.querySelector("#scrollInfos");
const bedingungen = document.querySelector(".bedingungen");
// sichergehen, dass es keine JS-Fehler gibt, weil das gesuchte Dokument nicht exisiert
function scrollToAccept() {
// bedingungen = document.querySelector(".bedingungen-and-conditions");
// wenn es das Element "bedingungen" nicht gibt,
// wird hier aus der Funktion gesprungen ohne dass es eine JS-Fehler gibt
if (!bedingungen) {
return;
}
bedingungen.addEventListener("scroll", function(ereignis) {
// scrollHeight ist die tatsächliche Höhe des zu scrollenden Inhalts
scrollInfos.innerHTML = `scrollTop: ${ereignis.currentTarget.scrollTop}px; scrollHeight: ${ereignis.currentTarget.scrollHeight}px`;
});
}
scrollToAccept();
// IntersectionObserver ========================
const beobachte = document.querySelector(".beobachte");
// beobachten des Img, ob es im Scrollbereich sichtbar ist oder nicht
function obCallback(payload) {
// [0] weil "beobachte" das 1. Element in der "Beobachtungsliste ist"
// sobald das gesuchte Element im sichtbaren Bereich ist, wird "true" ausgegeben
/*
console.log(
`strong.beobachte im sichtbaren Bereich: ${payload[0].isIntersecting}`
);
console.log(
`strong.beobachte intersectionRatio: ${payload[0].intersectionRatio}`
);
*/
}
const ob = new IntersectionObserver(obCallback);
ob.observe(beobachte);
// beobachten, ob ganz nach unten gescrollt wurde ==============
const zustimmenButton = document.querySelector(".zustimmen");
function zustimmenCallback(ladung) {
// [0] weil "beobachte" das 1. Element in der "Beobachtungsliste ist"
// sobald das gesuchte Element im sichtbaren Bereich ist, wird "true" ausgegeben
console.log(
`bedingungen.lastElementChild im sichtbaren Bereich: ${ladung[0].isIntersecting}`
);
console.log(
`bedingungen.lastElementChild intersectionRatio: ${ladung[0].intersectionRatio}`
);
// wenn der letzte Paragraph von "bedingungen" weitgehend
// im Scrollbereich sichtbar ist, wird der Button aktiv geschalten
if (ladung[0].intersectionRatio > 0.8) {
zustimmenButton.disabled = false;
// wenn nach einmal Runterscrollen der Button immer aktiv bleiben soll
// also auch, wenn man wieder raufscrollt:
zustimmen.unobserve(bedingungen.lastElementChild);
}
// ansonsten das "else" einbauen, dass den Button ein und ausblendet
/*
else {
zustimmenButton.disabled = true;
}
*/
}
// root: auf was soll sich der Observer beziehen
// threshold: Muss das Element komplett sichtbar sein? Dann "1"
const zustimmen = new IntersectionObserver(zustimmenCallback, {
root: bedingungen,
threshold: 0.8
});
// das letzte Element von "bedingungen"
zustimmen.observe(bedingungen.lastElementChild);
Erst wenn der Film komplett im sichtbaren Bereich ist, fängt dieser automatisch an zu spielen. Scrollt man den Film ansatzweise aus dem sichtbaren Bereich, hält er wieder an.
// beobachten, ob das Video komplett sichtbar ist
// https://css-tricks.com/an-explanation-of-how-the-intersection-observer-watches/
// https://css-tricks.com/a-few-functional-uses-for-intersection-observer-to-know-when-an-element-is-in-view/
const myVideo = document.querySelector("video");
function beobachterCallback(ladung) {
// [0] weil "video" das 1. Element in der "Beobachtungsliste ist
// console.log(`Video intersectionRatio: ${ladung[0].intersectionRatio}`);
// erst wenn das Video vollständig sichtbar ist, läuft es los
if (ladung[0].intersectionRatio >= 1) {
myVideo.play();
} else {
myVideo.pause();
}
}
// "threshold: 1" Das Element muss komplett sichtbar sein, dann läuft Video an
const beobachter = new IntersectionObserver(beobachterCallback, {
threshold: 1
});
// was soll beobachtet werden?
beobachter.observe(myVideo);
bei „wago9“ habe ich stattdessen abgefragt, ob ein Article den waagrechten Streifen mit unseren Namen überlappt. Das war eindeutiger…
Wechseln zwischen den Tabs geht auch mit „Tabbing“! Um die Sache möglichst „accessible“ zu machen, werden hier viele „aria-Codes“ verwendet…
const tabs = document.querySelector(".tabs");
const tabButtons = tabs.querySelectorAll("[role='tab']");
const tabPanels = tabs.querySelectorAll("[role='tabpanel']");
function handleTabClick(ereignis) {
// hide all tab panels – "lange" Schreibweise
/*
tabPanels.forEach(function(panel) {
panel.hidden = true;
});
*/
// hide all tab panels – Arrow Function
tabPanels.forEach(panel => {
panel.hidden = true; // alle Panels bekommen das Atrribute "hidden"
});
// mark all tabButtons as unselected
tabButtons.forEach(tabButton => {
// "aria-selected" kann nicht direkt wie z.B. "style" oder "alt" angesprochen werden!
tabButton.setAttribute("aria-selected", false);
});
// mark the clicked tab as selected
ereignis.currentTarget.setAttribute("aria-selected", true);
// find the associated panel and show it
// lange Schreibweise:
// const id = ereignis.currentTarget.id;
// mehrere Sachen auf einmal abfragen!
const { id, textContent } = ereignis.currentTarget;
// console.log(id, textContent);
const tabPanel = tabs.querySelector(`[aria-labelledby="${id}"]`);
// console.log(tabPanel);
tabPanel.hidden = false;
}
// bisherige, längere Schreibweise
/*
tabButtons.forEach(function(button) {
button.addEventListener("click", handleTabClick);
});
*/
// Arrow Function
tabButtons.forEach(button => button.addEventListener("click", handleTabClick));
Oben ist ein Zustand dargestellt, wenn "overflow: hidden" ausgeschaltet ist und die Slideshow nur 30% Breite einnimmt. Dann kann man sehen, dass:
Der vorherige Slide befindet sich nur sehr kurz ("const ueberGangDauer") an dieser Stelle und wird dann wieder nach rechts auf den Stapel geschoben.
Die Slideshow geht (bisher) nur vorwärts! Für eine funktionierende Rückwärts-Animation müssten die Slides umgekehrt positioniert werden.
Die JS- und CSS-Datei ist ausgiebig kommentiert…
// =========== grundlegende Settings ================
// wie lange bleibt ein Slide stehen -- Sekunden
const standDauer = 4.5;
// wie lange ist der Übergand zwischen den Slides -- Sekunden!
// Wert muss deutlich niedriger als der Wert von "standDauer" sein
const ueberGangDauer = 0.7;
// Variable legt fest, ob slideshow automatisch abläuft oder nicht
let slideShowLaufen = true;
/*
die ".slide"-Elemente bekommen alle eine Transition angefügt
die "ueberGangDauer" wird auch bei der setTimeout-Funktion
innerhalb von "weiterGehen()" verwendet
damit diese synchron selbstablaufen
sonst werden Teile anderer Slides sichtbar, was nicht gut aussieht!
*/
const slidesNode = document.querySelectorAll("#slideshow .slide");
slidesNode.forEach(function(ereignis) {
ereignis.style.transition = `all ${ueberGangDauer}s ease-in-out`;
});
// obere NodeList in Array unwandeln
const slides = Array.from(slidesNode);
// span-Element im Header, zeigt welcher Slide gerade gezeigt wird
const slideNummer = document.querySelector("#slideNummer");
// span-Element im Header, zeigt Anzahl der Slides
const slideAnzahl = document.querySelector("#slideAnzahl");
slideAnzahl.textContent = slides.length;
// ersten Slide sofort zeigen
// sonst komische Verzögerung über Dauer von Variable "standDauer"
slides[0].classList.add("current");
// Variable zum Hintereinander-Zeigen der Slides
// ACHTUNG: Wenn vorvorletzte Zeile ("slides[0].classList…") nicht gemacht wird
// hier "i = 0;" schreiben, sonst fängt Slideshow bei 2 an!
let i = 1;
function weiterGehen() {
const currentSlide = document.querySelector(".current");
/*
nur "null", wenn Slideshow gerade angefangen hat
ansonsten bekommt der gerade sichtbare ".slide" die Klasse ".prev"
und wird somit nach links geschoben
um das zu sehen, im Stylesheet bei "#slideshow" "overflow:hidden" auskommentieren!
*/
if (currentSlide != null) {
currentSlide.classList.remove("current");
currentSlide.classList.add("prev");
}
/*
nach einem kurzem Intervall wird bei ".prev" diese Klasse entfernt
damit "wandert" der Slide wieder rechts neben den sichtbaren Bereich
um das zu sehen, im Stylesheet bei "#slideshow" "overflow:hidden" auskommentieren!
*/
setTimeout(function() {
const prevSlide = document.querySelector(".prev");
if (prevSlide != null) {
prevSlide.classList.remove("prev");
}
}, ueberGangDauer * 1000); // Umrechnung in Millisekunden!
/*
das jeweilige "current"-Slide bekommt die Klasse "current"
und wird somit in den sichtbaren Bereich geschoben
um das zu sehen, im Stylesheet bei "#slideshow" "overflow:hidden" auskommentieren!
*/
let current = slides[i];
current.classList.add("current");
// span-Element im Header, zeigt welcher Slide gerade gezeigt wird
slideNummer.textContent = i + 1;
i++;
if (i == slides.length) {
i = 0;
}
}
// =============== selbstablaufende Slideshow ================
const cycle = setInterval(function() {
if (slideShowLaufen == true) {
weiterGehen();
}
}, standDauer * 1000);
// ============= Zustand der Slideshow ===============
// Anzeige des Status im Header
const slideShowStatus = document.querySelector("#slideShowStatus");
// steht oder läuft die Slideshow am Anfang?
if (slideShowLaufen == true) {
slideShowStatus.textContent = "läuft";
} else {
slideShowStatus.textContent = "steht";
}
// ============== Button zum Weiterklicken =================
const button = document.querySelector("button");
// ACHTUNG: Damit geht die Slideshow weiter,
// wird aber nicht gestoppt!
button.addEventListener("click", weiterGehen);
// =============== mouseenter und mouseleave ================
// slideshow stoppt, wenn Cursor auf der slideshow
// und fängt wieder an, wenn Cursor weggeht
const slideshow = document.querySelector("#slideshow");
slideshow.addEventListener("mouseenter", function() {
slideShowLaufen = false;
slideShowStatus.textContent = "steht";
});
slideshow.addEventListener("mouseleave", function() {
slideShowLaufen = true;
slideShowStatus.textContent = "läuft";
});
// ========== Slideshow via Keyboard anhalten und weiterblättern ============
// toggelt, ob Slideshow läuft oder nicht
function slideShowAnAus() {
slideShowLaufen = !slideShowLaufen;
if (slideShowLaufen == true) {
slideShowStatus.textContent = "läuft";
} else {
slideShowStatus.textContent = "steht";
}
}
window.addEventListener("keydown", function(ereignis) {
// console.log(ereignis.key);
if (ereignis.key === " ") {
slideShowAnAus();
// console.log("Space-Taste!");
}
// Pfeiltaste n ach rechts blättern jeweils ein Slide weiter
if (ereignis.key === "ArrowRight") {
slideShowLaufen = false;
slideShowStatus.textContent = "steht";
weiterGehen();
}
// Pfeiltaste nach links blättern jeweils ein Slide zurück
// ACHTUNG, das geht noch nicht!
/*
if (ereignis.key === "ArrowLeft") {
slideShowLaufen = false;
button.textContent = "Ani steht";
slideShowWeiter("zurueck");
}
*/
});
Selbstablaufende Bildershow, die nur bei mouseenter die Slides anhält
// =========== grundlegende Settings ================
// wie lange bleibt ein Slide stehen -- Sekunden
const standDauer = 6.5;
// wie lange ist der Übergand zwischen den Slides -- Sekunden!
// Wert muss deutlich niedriger als der Wert von "standDauer" sein
const ueberGangDauer = 0.7;
// Variable legt fest, ob slideshow automatisch abläuft oder nicht
let slideShowLaufen = true;
/*
die ".slide"-Elemente bekommen alle eine Transition angefügt
die "ueberGangDauer" wird auch bei der setTimeout-Funktion
innerhalb von "weiterGehen()" verwendet
damit diese synchron selbstablaufen
sonst werden Teile anderer Slides sichtbar, was nicht gut aussieht!
*/
const slidesNode = document.querySelectorAll("#slideshow .slide");
slidesNode.forEach(function(ereignis) {
ereignis.style.transition = `all ${ueberGangDauer}s ease-in-out`;
});
// NodeList in Array unwandeln
const slides = Array.from(slidesNode);
// ersten Slide sofort zeigen
// sonst komische Verzögerung über Dauer von Variable "standDauer"
slides[0].classList.add("current");
// Variable zum Hintereinander-Zeigen der Slides
// ACHTUNG: Wenn vorvorletzte Zeile ("slides[0].classList…") nicht gemacht wird
// hier "i = 0;" schreiben, sonst fängt Slideshow bei 2 an!
let i = 1;
function weiterGehen() {
const currentSlide = document.querySelector(".current");
/*
nur "null", wenn Slideshow gerade angefangen hat
ansonsten bekommt der gerade sichtbare ".slide" die Klasse ".prev"
und wird somit nach links geschoben
um das zu sehen, im Stylesheet bei "#slideshow" "overflow:hidden" auskommentieren!
*/
if (currentSlide != null) {
currentSlide.classList.remove("current");
currentSlide.classList.add("prev");
}
/*
nach einem kurzem Intervall wird bei ".prev" diese Klasse entfernt
damit "wandert" der Slide wieder rechts neben den sichtbaren Bereich
um das zu sehen, im Stylesheet bei "#slideshow" "overflow:hidden" auskommentieren!
*/
setTimeout(function() {
const prevSlide = document.querySelector(".prev");
if (prevSlide != null) {
prevSlide.classList.remove("prev");
}
}, ueberGangDauer * 1000); // Umrechnung in Millisekunden!
/*
das jeweilige "current"-Slide bekommt die Klasse "current"
und wird somit in den sichtbaren Bereich geschoben
um das zu sehen, im Stylesheet bei "#slideshow" "overflow:hidden" auskommentieren!
*/
let current = slides[i];
current.classList.add("current");
i++;
if (i == slides.length) {
i = 0;
}
}
// =============== selbstablaufende Slideshow ================
const cycle = setInterval(function() {
if (slideShowLaufen == true) {
weiterGehen();
}
}, standDauer * 1000);
// =============== mouseenter und mouseleave ================
// header soll roten HG bekommen, wenn mouseenter Slideshow anhält
const header = document.querySelector("header");
// slideshow stoppt, wenn Cursor auf der slideshow
// und fängt wieder an, wenn Cursor weggeht
const slideshow = document.querySelector("#slideshow");
slideshow.addEventListener("mouseenter", function() {
slideShowLaufen = false;
header.style.background = "red";
});
slideshow.addEventListener("mouseleave", function() {
slideShowLaufen = true;
header.style.background = "black";
});
Das hier eingebaute Zufallsprinzip wird bei jedem Aufruf des nächsten Bildes aufgerufen! Damit kann es auch passieren, dass ein Bild nie (oder sehr oft) gezeigt wird. Auf dem nächsten Slide ist eine Lösung gezeigt, wo jeweils zu Beginn eine komplette, zufällige Liste aller Bilder erzeugt wird. Erst nach Abarbeiten der kompletten Liste wird erneut eine neue, zufällige Liste erzeugt!
// =========== grundlegende Settings ================
// wie lange bleibt ein Slide stehen -- Sekunden
const standDauer = 2.5;
// wie lange ist der Übergand zwischen den Slides -- Sekunden!
// Wert muss deutlich niedriger als der Wert von "standDauer" sein
const ueberGangDauer = 0.7;
// Variable legt fest, ob slideshow automatisch abläuft oder nicht
let slideShowLaufen = true;
// legt fest, ob die Slideshow in der Reihenfolge der Slides
// oder zufällig abläuft
let zufall = true;
// ==== kontrolliertes Loading beim Starten der Site ==========
// gut, weil die Bilder der Slideshow unfangreich sein können
// und via PHP importiert werden
const main = document.querySelector("main");
const loading = document.querySelector("#loading");
// erst wenn alle Bilder aus dem Web komplett geladen ist,
// wird bei "main" die Klasse "sichtbar" hinzugefügt
// und somit sichtbar geschaltet
// beim Inspector/Network auf "Throttling" gehen und ein langsames Netz
// simulieren – dann kann man den Ladeprozess gut sehen!
// wenn man testen will, ob ALLE Inhalte der jeweiligen Seite geladen sind:
window.addEventListener("load", function() {
loading.style.opacity = "0";
main.classList.add("sichtbar");
});
// ===== ab hier Sachen für die eigentliche Slideshow ============
/*
die ".slide"-Elemente bekommen alle eine Transition angefügt
die "ueberGangDauer" wird auch bei der setTimeout-Funktion
innerhalb von "weiterGehen()" verwendet
damit diese synchron selbstablaufen
sonst werden Teile anderer Slides sichtbar, was nicht gut aussieht!
*/
const slidesNode = document.querySelectorAll("#slideshow .slide");
slidesNode.forEach(function(ereignis) {
ereignis.style.transition = `all ${ueberGangDauer}s ease-in-out`;
});
// NodeList in Array unwandeln
const slides = Array.from(slidesNode);
// ersten Slide sofort zeigen
// sonst komische Verzögerung über Dauer von Variable "standDauer"
// erstes Bild zeigen
// slides[0].classList.add("current");
// zufällig ausgewähltes Bild zeigen
if (zufall === true) {
slides[Math.floor(Math.random() * slides.length)].classList.add("current");
} else {
slides[0].classList.add("current");
}
// Variable zum Hintereinander-Zeigen der Slides
// ACHTUNG: Wenn vorvorletzte Zeile ("slides[0].classList…") nicht gemacht wird
// hier "i = 0;" schreiben, sonst fängt Slideshow bei 2 an!
let i = 1;
function weiterGehen() {
const currentSlide = document.querySelector(".current");
/*
nur "null", wenn Slideshow gerade angefangen hat
ansonsten bekommt der gerade sichtbare ".slide" die Klasse ".prev"
und wird somit nach links geschoben
um das zu sehen, im Stylesheet bei "#slideshow" "overflow:hidden" auskommentieren!
*/
if (currentSlide != null) {
currentSlide.classList.remove("current");
currentSlide.classList.add("prev");
}
/*
nach einem kurzem Intervall wird bei ".prev" diese Klasse entfernt
damit "wandert" der Slide wieder rechts neben den sichtbaren Bereich
um das zu sehen, im Stylesheet bei "#slideshow" "overflow:hidden" auskommentieren!
*/
setTimeout(function() {
const prevSlide = document.querySelector(".prev");
if (prevSlide != null) {
prevSlide.classList.remove("prev");
}
}, ueberGangDauer * 1000); // Umrechnung in Millisekunden!
/*
das jeweilige "current"-Slide bekommt die Klasse "current"
und wird somit in den sichtbaren Bereich geschoben
um das zu sehen, im Stylesheet bei "#slideshow" "overflow:hidden" auskommentieren!
*/
let current = slides[i];
current.classList.add("current");
/*
zufällige Reihenfolge, zur Sicherheit Abfrage,
ob gleicher Wert für ehemaliges i und i rauskommt
falls das passiert, einfach i hochzählen
*/
let ehem_i = i;
if (zufall === true) {
i = Math.floor(Math.random() * slides.length);
} else {
i = i + 1;
}
if (i === ehem_i) {
console.log(`gleiches Bild: ${i} / ${ehem_i}`);
i++;
}
// einfaches Hochzählen der Slides
// i++;
if (i == slides.length) {
i = 0;
}
}
// =============== selbstablaufende Slideshow ================
const cycle = setInterval(function() {
if (slideShowLaufen == true) {
weiterGehen();
}
}, standDauer * 1000);
// =============== Anhalten/Weiterspielen bei mouseenter/mouseleave ================
function playPauseZeigen() {
if (slideShowLaufen === true) {
playPauseAnzeige.textContent = "PLAY";
} else {
playPauseAnzeige.textContent = "PAUSE";
}
playPauseAnzeige.classList.add("sichtbarAni");
playPauseAnzeige.addEventListener(
"animationend",
function() {
// console.log("Animation beended!");
playPauseAnzeige.classList.remove("sichtbarAni");
},
{ once: true } // damit läuft dieser "addEventListener" nur einmal und addiert sich nicht auf
);
}
// Anzeige-Element für "PAUSE" und "PLAY" generieren
const playPauseAnzeige = document.createElement("p");
playPauseAnzeige.id = "playPauseAnzeige";
document.body.append(playPauseAnzeige);
playPauseZeigen();
// =============== mouseenter und mouseleave ================
// header soll roten HG bekommen, wenn mouseenter Slideshow anhält
const header = document.querySelector("header");
// slideshow stoppt, wenn Cursor auf der slideshow
// und fängt wieder an, wenn Cursor weggeht
const slideshow = document.querySelector("#slideshow");
slideshow.addEventListener("mouseenter", function() {
slideShowLaufen = false;
header.classList.add("sichtbar");
playPauseZeigen();
});
slideshow.addEventListener("mouseleave", function() {
slideShowLaufen = true;
header.classList.remove("sichtbar");
playPauseZeigen();
});
// === Anzeige der Bildanzahl im Header =====
// "bildAnzahl" befindet sich im header
// "bildZaehler" in Main und wird via PHP befüllt
const bildAnzahl = document.querySelector("#bildAnzahl");
const bildZaehler = document.querySelector("#bildZaehler");
bildAnzahl.innerHTML = bildZaehler.innerHTML;
<?php
// $ordner wird bei "index.php" festgelegt
$verzeichnis = dir($ordner);
// nächste Zeile sorgt dafür, dass das Array entleert wird und man es somit mehrfach verwenden kann!
$files = array();
while($eintrag=$verzeichnis->read())
$files[] = $eintrag; // ein array!
$verzeichnis->close();
sort($files); // alphabetisch sortieren
$pfad = $ordner . '/';
$clength = count($files)-2;
echo "<div id='slideshow'>\n";
foreach ($files as $eintrag){
// http://www.php.net/manual/de/language.types.string.php
// naechste zwei Zeilen fragen ab, ob Eintrag mit einem "." anfängt!
$first = $eintrag[0];
if ($first != "."){
$bild = $pfad . $eintrag;
echo "
<figure class='slide'>
<img src='" . $bild . "' alt=''>
</figure>
\n";
}
}
echo " </div><!-- /slideshow -->\n\n";
echo " <p class='hinweis'>Das Verzeichnis enthält <strong style='color:red;'>$clength</strong> Bilder.</p>\n\n";
?>
Hier wird jeweils zu Beginn eine komplette, zufällige Liste aller Bilder erzeugt wird. Erst nach Abarbeiten der kompletten Liste wird eine neue, zufällige Liste erzeugt. Somit werden grundsätzlich alle Bilder (gleich oft) gezeigt…
Code nicht komplett – hier nur die Sachen, die für das „Zufallsarray“ wichtig sind! Rest identisch mit vorherigem Beispiel…
// =========== grundlegende Settings ================
// wie lange bleibt ein Slide stehen -- Sekunden
const standDauer = 2.5;
// wie lange ist der Übergand zwischen den Slides -- Sekunden!
// Wert muss deutlich niedriger als der Wert von "standDauer" sein
const ueberGangDauer = 0.7;
// Variable legt fest, ob slideshow automatisch abläuft oder nicht
let slideShowLaufen = true;
// legt fest, ob die Slideshow in der Reihenfolge der Slides
// oder zufällig abläuft
let zufall = true;
// ===== ab hier Sachen für die eigentliche Slideshow ============
// Zufalls-Funktion bei den Slides
// https://www.w3resource.com/javascript-exercises/javascript-array-exercise-17.php
function shuffle(myArray) {
let l = myArray.length;
let temp;
let index;
// While there are elements in the array
while (l > 0) {
// Pick a random index
index = Math.floor(Math.random() * l);
// Decrease l by 1
l--;
// And swap the last element with it
temp = myArray[l];
myArray[l] = myArray[index];
myArray[index] = temp;
}
return myArray;
}
/*
die ".slide"-Elemente bekommen alle eine Transition angefügt
die "ueberGangDauer" wird auch bei der setTimeout-Funktion
innerhalb von "weiterGehen()" verwendet
damit diese synchron selbstablaufen
sonst werden Teile anderer Slides sichtbar, was nicht gut aussieht!
*/
const slidesNode = document.querySelectorAll("#slideshow .slide");
slidesNode.forEach(function(ereignis) {
ereignis.style.transition = `all ${ueberGangDauer}s ease-in-out`;
});
// NodeList in Array unwandeln
const slides = Array.from(slidesNode);
// hier die Slides zufällig sortieren und in eine neues Array "zufallsSlides" einfügen
// das Array "zufallsSlides" wird komplett abgearbeitet, so dass alle Bilder gezeigt werden
// erst dann wird "zufallsSlidesGenerieren()" und "relevanteSlidesAuswaehlen();" erneut aufgerufen
// damit läuft bei der nächsten Runde die Slideshow wieder in einer anderen Reihenfolge
let zufallsSlides;
function zufallsSlidesGenerieren() {
// Achtung: nicht Array "slides" verwenden, sonst wird dies auch zufällig!
// deswegen hier Rückgriff auf "slidesNode"
zufallsSlides = shuffle(Array.from(slidesNode));
}
zufallsSlidesGenerieren();
// abhängig, ob zufall "true" oder "false" ist, wird entweder das Array "slides"
// oder das Array "zufallsSlides" verwendet
let relevanteSlides;
function relevanteSlidesAuswaehlen() {
if (zufall === true) {
relevanteSlides = zufallsSlides;
} else {
relevanteSlides = slides;
}
}
relevanteSlidesAuswaehlen();
// ersten Slide sofort zeigen
// sonst komische Verzögerung über Dauer von Variable "standDauer"
relevanteSlides[0].classList.add("current");
// Variable zum Hintereinander-Zeigen der Slides
// ACHTUNG: Wenn vorvorletzte Zeile ("slides[0].classList…") nicht gemacht wird
// hier "i = 0;" schreiben, sonst fängt Slideshow bei 2 an!
let i = 1;
function weiterGehen() {
const currentSlide = document.querySelector(".current");
/*
nur "null", wenn Slideshow gerade angefangen hat
ansonsten bekommt der gerade sichtbare ".slide" die Klasse ".prev"
und wird somit nach links geschoben
um das zu sehen, im Stylesheet bei "#slideshow" "overflow:hidden" auskommentieren!
*/
if (currentSlide != null) {
currentSlide.classList.remove("current");
currentSlide.classList.add("prev");
}
/*
nach einem kurzem Intervall wird bei ".prev" diese Klasse entfernt
damit "wandert" der Slide wieder rechts neben den sichtbaren Bereich
um das zu sehen, im Stylesheet bei "#slideshow" "overflow:hidden" auskommentieren!
*/
setTimeout(function() {
const prevSlide = document.querySelector(".prev");
if (prevSlide != null) {
prevSlide.classList.remove("prev");
}
}, ueberGangDauer * 1000); // Umrechnung in Millisekunden!
/*
das jeweilige "current"-Slide bekommt die Klasse "current"
und wird somit in den sichtbaren Bereich geschoben
um das zu sehen, im Stylesheet bei "#slideshow" "overflow:hidden" auskommentieren!
*/
let current = relevanteSlides[i];
current.classList.add("current");
// einfaches Hochzählen der Slides
i++;
if (i == relevanteSlides.length) {
// prozess von vorne starten, Liste zufällig neu bestücken
zufallsSlidesGenerieren();
relevanteSlidesAuswaehlen();
i = 0;
}
}
// =============== selbstablaufende Slideshow ================
const cycle = setInterval(function() {
if (slideShowLaufen == true) {
weiterGehen();
}
}, standDauer * 1000);
// Zufalls-Funktion
// https://www.w3resource.com/javascript-exercises/javascript-array-exercise-17.php
function shuffle(myArray) {
let l = myArray.length;
let temp;
let index;
// While there are elements in the array
while (l > 0) {
// Pick a random index
index = Math.floor(Math.random() * l);
// Decrease l by 1
l--;
// And swap the last element with it
temp = myArray[l];
myArray[l] = myArray[index];
myArray[index] = temp;
}
return myArray;
}
function listeZufaelligSortieren() {
// eine NodeList
const listNodes = document.querySelectorAll("#ausgangsListe li");
// Umwandlung in Array
const listArray = Array.from(listNodes);
// hier das Array zufällig sortieren und in eine neues Array "zufallsArray" einfügen
const zufallsArray = shuffle(listArray);
// console.log(zufallsListe[0].outerHTML);
// hier soll das neu sortierte Array eingefügt werden
const zufallsListeAusgabe = document.querySelector("#zufallsListeAusgabe");
// AusgabeListe erst mal leeren, sonst mehrfache Listen
// wenn man mehrmals auf den button#shuffle klickt
zufallsListeAusgabe.innerHTML = "";
// for-Schleife geht durch alle Listenelemente des Array "zufallsArray"
// und fügt diese bei zufallsListeAusgabe hintereinander ein
// wichtig ist das "+="
for (let i = 0; i < zufallsArray.length; i++) {
zufallsListeAusgabe.innerHTML += zufallsArray[i].outerHTML;
}
// unmittelbares Kopieren der neuen Liste in den Arbeitsspeicher
// - ohne weiteren Button
// kopieren("#zufallsListeAusgabe");
// erst jetzt den Copy-Button sichtbar machen
// wenn bei "zufallsListeAusgabe" Inhalt vorhanden ist
copyButton.style.display = "block";
}
const shuffleButton = document.querySelector("#shuffle");
shuffleButton.addEventListener("click", listeZufaelligSortieren);
// die neu erzeugte Liste zuerst in ein Input-Feld kopieren
// und dann in den Arbeitsspeicher
function kopieren(ele) {
const tempElement = document.createElement("input");
tempElement.setAttribute("type", "text");
tempElement.setAttribute("contenteditable", true);
tempElement.id = "tempElement";
// sonst wird das Element im Browser sichtbar
tempElement.style.translate = "-1000vw";
const copyListe = document.querySelector(ele);
tempElement.value = copyListe.outerHTML;
document.body.append(tempElement);
tempElement.select();
document.execCommand("copy");
tempElement.remove();
}
function neueListeKopieren() {
kopieren("#zufallsListeAusgabe");
}
const copyButton = document.querySelector("#copy");
copyButton.addEventListener("click", neueListeKopieren);
// Zufalls-Funktion
// https://www.w3resource.com/javascript-exercises/javascript-array-exercise-17.php
function shuffle(myArray) {
let l = myArray.length;
let temp;
let index;
// While there are elements in the array
while (l > 0) {
// Pick a random index
index = Math.floor(Math.random() * l);
// Decrease l by 1
l--;
// And swap the last element with it
temp = myArray[l];
myArray[l] = myArray[index];
myArray[index] = temp;
}
return myArray;
}
function listeZufaelligSortieren() {
// das Div mit der Ausgangsliste
const ausgangsBlock = document.querySelector("#ausgangsBlock");
ausgangsBlock.classList.remove("sichtbar");
// das Div mit der zufallsListe
const zufallBlock = document.querySelector("#zufallBlock");
ausgangsBlock.addEventListener(
"transitionend",
function() {
// console.log("ausgangsBlock-Transition beended!");
zufallBlock.classList.add("sichtbar");
},
{ once: true } // damit läuft dieser "addEventListener" nur einmal und addiert sich nicht auf
);
// eine NodeList
const listNodes = document.querySelectorAll("#ausgangsListe li");
// Umwandlung in Array
const listArray = Array.from(listNodes);
// hier das Array zufällig sortieren und in eine neues Array "zufallsArray" einfügen
const zufallsArray = shuffle(listArray);
// console.log(zufallsListe[0].outerHTML);
// hier soll das neu sortierte Array eingefügt werden
const zufallsListeAusgabe = document.querySelector("#zufallsListeAusgabe");
// AusgabeListe erst mal leeren, sonst mehrfache Listen
// wenn man mehrmals auf den button#shuffle klickt
zufallsListeAusgabe.innerHTML = "";
// for-Schleife geht durch alle Listenelemente des Array "zufallsArray"
// und fügt diese bei zufallsListeAusgabe hintereinander ein
// wichtig ist das "+="
for (let i = 0; i < zufallsArray.length; i++) {
zufallsListeAusgabe.innerHTML += zufallsArray[i].outerHTML;
}
// unmittelbares Kopieren der neuen Liste in den Arbeitsspeicher
// - ohne weiteren Button
// kopieren("#zufallsListeAusgabe");
}
const shuffleButton = document.querySelector("#shuffle");
shuffleButton.addEventListener("click", listeZufaelligSortieren);
// die neu erzeugte Liste zuerst in ein Input-Feld kopieren
// und dann in den Arbeitsspeicher
function kopieren(ele) {
const tempElement = document.createElement("input");
tempElement.setAttribute("type", "text");
tempElement.setAttribute("contenteditable", true);
tempElement.id = "tempElement";
// sonst wird das Element im Browser sichtbar
tempElement.style.translate = "-1000vw";
const copyListe = document.querySelector(ele);
tempElement.value = copyListe.outerHTML;
document.body.append(tempElement);
tempElement.select();
document.execCommand("copy");
tempElement.remove();
}
function neueListeKopieren() {
kopieren("#zufallsListeAusgabe");
}
const copyButton = document.querySelector("#copy");
copyButton.addEventListener("click", neueListeKopieren);
// der Button "Neuauslosen" macht die normale Liste wieder sichtbar
// und lässt das Auslosen von vorne anfangen
const neuAuslosen = document.querySelector("#neuAuslosen");
neuAuslosen.addEventListener("click", function() {
// das Div mit der Ausgangsliste
const ausgangsBlock = document.querySelector("#ausgangsBlock");
// das Div mit der zufallsListe
const zufallBlock = document.querySelector("#zufallBlock");
zufallBlock.classList.remove("sichtbar");
zufallBlock.addEventListener(
"transitionend",
function() {
ausgangsBlock.classList.add("sichtbar");
},
{ once: true }
);
});
/* CSS lokale Variable! */
.fish {
width: 300px;
--x: 0;
--y: 0;
/* legt fest, ob Fish nach links oder rechts schaut */
--rotateY: 0;
/* legt fest, ob Fish nach oben oder unten schaut */
--rotate: 0;
/* Reihenfolge wichtig! */
transform: translateX(var(--x)) translateY(var(--y)) rotateY(var(--rotateY))
rotate(var(--rotate));
/* damit glatter beim Animieren via Tastatur */
transition: transform 0.2s;
}
const fish = document.querySelector(".fish");
// zum Verschieben des Fish
let x = 0;
let y = 0;
// Wie groß sind die Sprünge
const speed = 50;
// zum waagrechten Umdrehen des Fishes
let flipped = false;
// zum senkrechten Umdrehen des Fishes
let rotate = 0;
function handleKeydown(ereignis) {
// falls etwas anderes als die Arrow-Tasten gedrückt wird
// soll JS hier einfach aussteigen
if (!ereignis.key.includes("Arrow")) return;
//console.log(ereignis.key);
switch (ereignis.key) {
case "ArrowUp":
y = y - 1;
rotate = -90;
break;
case "ArrowDown":
y = y + 1;
rotate = 90;
break;
case "ArrowRight":
x = x + 1;
flipped = false;
rotate = 0;
break;
case "ArrowLeft":
x = x - 1;
flipped = true;
rotate = 0;
break;
default:
console.log("ungültige Taste!");
break;
}
// Ternary statt if/else
// wenn "flipped" true ist, 180deg drehen sonst 0
fish.setAttribute(
"style",
`
--rotate: ${rotate}deg;
--rotateY: ${flipped ? "180deg" : "0"};
--x: ${x * speed}px;
--y: ${y * speed}px;
`
);
}
window.addEventListener("keydown", handleKeydown);
https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX/Getting_Started
document.querySelector("#ajaxButton").addEventListener("click", makeRequest);
// diese Datei wird geladen! Aktuell im Ordner "ajax"…
const datei = "ajax/test.html";
const warnung = document.querySelector("#warnung");
let httpRequest;
function makeRequest() {
httpRequest = new XMLHttpRequest();
if (!httpRequest) {
warnung.textContent = "Giving up :( Cannot create an XMLHTTP instance!";
return false;
}
httpRequest.onreadystatechange = externenContentLaden;
httpRequest.open("GET", datei);
httpRequest.send();
}
function externenContentLaden() {
let ajaxContent;
const ajaxRein = document.querySelector("#ajaxRein");
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
ajaxContent = httpRequest.responseText;
ajaxRein.innerHTML = ajaxContent;
ajaxRein.classList.add("sichtbar");
} else {
warnung.textContent = `Die Datei ${datei} konnte nicht geladen werden!`;
}
}
}
// ============= Elemente vorab generieren =====================
/* CSS-Angaben für diese Elemente bei "popup.css" */
const abdecker = document.createElement("div");
abdecker.id = "abdecker";
// die animierte "Sanduhr", die anzeigt, dass ein großes Bild geladen wird
const abdeckerSanduhr = document.createElement("img");
abdeckerSanduhr.id = "abdeckerSanduhr";
abdeckerSanduhr.src = "./assets/p/waiting_white.svg";
const ajaxContentArticle = document.createElement("article");
ajaxContentArticle.id = "ajaxContentArticle";
// hier soll der Content der externen Datei reingeladen werden
const ajaxContentArticleDiv = document.createElement("div");
ajaxContentArticleDiv.id = "ajaxContentArticleDiv";
const schliesser = document.createElement("span");
schliesser.id = "schliesser";
schliesser.textContent = "×";
schliesser.role = "button";
schliesser.tabIndex = "0"; // Schreibweise in JS beachten!
schliesser.title = "Popup schliessen";
// zusammenbauen und einfügen
ajaxContentArticle.appendChild(schliesser);
ajaxContentArticle.appendChild(ajaxContentArticleDiv);
abdecker.appendChild(abdeckerSanduhr);
abdecker.appendChild(ajaxContentArticle);
document.body.appendChild(abdecker);
// ====================== Ajax-Funktionalität ======================
let httpRequest;
let datei; // diese Datei soll via AJAX geladen werden
let ajaxContent;
function popupFuellen(ereignis) {
ereignis.preventDefault();
// gesuchte Dabei aus dem aktuell angeklickten Link herausholen
datei = ereignis.currentTarget.href;
// console.log(datei);
httpRequest = new XMLHttpRequest();
if (!httpRequest) {
ajaxConten.textContent =
"Sorry! Es konnte keine XMLHTTP instance etabliert werden!";
return false;
}
httpRequest.onreadystatechange = externenContentLaden;
httpRequest.open("GET", datei);
httpRequest.send();
}
function externenContentLaden() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
ajaxContent = httpRequest.responseText;
} else {
ajaxContent = `<h2 style="color:red;padding:2em;">Sorry: Die Datei ${datei} konnte nicht geladen werden!</h2>`;
}
ajaxContentArticleDiv.innerHTML = ajaxContent;
abdeckerSanduhr.classList.add("sichtbar");
abdecker.classList.add("sichtbar");
abdecker.addEventListener(
"transitionend",
function() {
ajaxContentArticle.classList.add("sichtbar");
},
{ once: true } // damit läuft dieser "addEventListener" nur einmal und addiert sich nicht auf
);
}
}
// alle <a>-Tags innerhalb der ul.ajaxLinks
const ajaxLinks = document.querySelectorAll(".ajaxLinks a");
// console.log(ajaxLinks);
ajaxLinks.forEach(function(ereignis) {
ereignis.addEventListener("click", popupFuellen);
});
function popupWeg() {
abdeckerSanduhr.classList.remove("sichtbar");
ajaxContentArticle.classList.remove("sichtbar");
ajaxContentArticle.addEventListener(
"transitionend",
function() {
abdecker.classList.remove("sichtbar");
},
{ once: true }
);
}
// "×" zum Schliessen des popups
schliesser.addEventListener("click", popupWeg);
abdecker.addEventListener("click", popupWeg);
<main>
<!--
moderne Browser halten sich an maxlength,
bei älteren muss man zusätzlich auf Server Abfrage einbauen
JS holt sich diesen Wert ebenfalls für die Anzeige
-->
<textarea rows="5" maxlength="400" id="message"></textarea>
<p id="count">
Anzahl Zeichen: <span id="counter">0</span> von maximal
<span id="maxLength"></span> Zeichen
</p>
</main>
const messageArea = document.querySelector("#message");
const counterSpan = document.querySelector("#counter");
const maxLengthSpan = document.querySelector("#maxLength");
// Angabe bei "#message" wird abgefragt
const maxLength = message.getAttribute("maxlength");
maxLengthSpan.innerHTML = `${maxLength}`;
messageArea.addEventListener("input", function(ergebnis) {
const currentLength = ergebnis.target.value.length;
counterSpan.innerHTML = `${currentLength}`;
});
Mit dieser neuen Schreibweise ist es viel einfacher, Animationen zu erstellen, die hintereinander ablaufen sollen
im Source-Code der Demo-JS-Datei ist die Animation als Kommentar auch in der bisherigen Version zu sehen…
const go = document.querySelector(".go");
// warteFunktion, "resolve" könnte auch "thias" heissen…
function warte(ms = 0) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
// "async" ist ganz wichtig, sonst geht es nicht!
async function animate2(ereignis) {
const el = ereignis.currentTarget;
el.textContent = "Go!";
await warte(700);
el.classList.add("circle");
await warte(500);
el.classList.add("red");
await warte(200);
el.classList.remove("circle");
await warte(350);
el.classList.remove("red");
el.classList.add("purple");
await warte(350);
el.classList.add("hidden");
}
go.addEventListener("click", animate2);
geht nur in modernen Browsern!
// Text wird ins Clipboard kopiert
const inputEl = document.querySelector("#to-copy");
const writeBtn = document.querySelector(".write-btn");
writeBtn.addEventListener("click", () => {
const inputValue = inputEl.value.trim();
if (inputValue) {
navigator.clipboard
.writeText(inputValue)
.then(() => {
inputEl.value = "";
if (writeBtn.innerText !== "kopiert!") {
const originalText = writeBtn.innerText;
writeBtn.innerText = "✔ kopiert!";
setTimeout(() => {
writeBtn.innerText = originalText;
}, 1500);
}
})
.catch(err => {
console.log("Something went wrong", err);
});
}
});
Die verwendete Textarea wird via JS erzeugt, aber nicht im Browser angezeigt…
geht nur in modernen Browsern!
// der Code-Bereich, aus dem kopiert werden soll
const meinCode = document.querySelector("code");
// der Copy-Button
const copyCodeButton = document.querySelector(".copyCodeButton");
copyCodeButton.addEventListener("click", () => {
// textarea erzeugen und textContent des Code-Blocks einfügen
const inputTextArea = document.createElement("textarea");
inputTextArea.value = meinCode.textContent;
const inputValue = inputTextArea.value.trim();
if (inputValue) {
navigator.clipboard
.writeText(inputValue)
.then(() => {
inputTextArea.value = "";
if (copyCodeButton.innerText !== "kopiert!") {
const originalText = copyCodeButton.innerText;
copyCodeButton.innerText = "✔ kopiert!";
setTimeout(() => {
copyCodeButton.innerText = originalText;
}, 1500);
}
})
.catch(err => {
console.log(
"Das hat leider nicht funktioniert, vielleicht ein zu alter Browser?",
err
);
});
}
});