template_artikel Archive - Seite 36 von 39 - andererseits.org
Zum Inhalt springen
Unsere Autorin erzählt, dass sie handysüchtig ist. Manchmal liebt sie das, manchmal stört sie es.
Sabrina Burtscher ist Informatikerin und setzt sich im Rahmen ihrer Arbeit auch kritisch mit Algorithmen und Künstlicher Intelligenz auseinander.
Immer mehr kleine Kinder haben ein Handy. Das hat Vorteile, aber birgt auch Gefahren – wie gehen Kinder und Eltern mit der Herausforderung um?
Smartphones wollen ständig unsere Aufmerksamkeit. Die meisten von uns können sie jederzeit abschalten und weglegen. Was aber, wenn das nicht geht?
Smartphones und die Abhängigkeit davon sind zu einem Teil unseres Alltags geworden. In unserem Rechercheprojekt zu Smartphones und digitaler Technologie fragen wir, was das für uns und für unsere Gesellschaft bedeutet.
Der Bruder unserer Autorin hat eine Behinderung, Trisonomie 21. Sie erzählt, wie die Gesellschaft Menschen mit Behinderungen ausschließt und fordert echte Inklusion.
Wann warst Du zum letzten mal komplett ohne Handy, ohne soziale Medien unterwegs? Die meisten machen diese Erfahrungen kaum, es ist kein Platz, keine Akzeptanz dafür in unserem Alltag. Unsere Autorinnen Lisa und Luise hat interessiert: Wie ist es das Handy einfach mal liegen zu lassen? Und haben das Experiment gewagt. Gleichzeitig haben wir Menschen gefragt, wie sich das “mit” und “ohne” für sie anfühlt. Eine Erkundung.
Seit dem ersten Lockdown gehören Livestreams für viele Künstler:innen zum Alltag. Im Gespräch mit andererseits erzählen sie, wie die Auftritte im Internet ihre Musik beeinflussen und was für und gegen sie spricht.
Raul Krauthausen, Aktivist für Inklusion und Barrierefreiheit im Gespräch über Teilhabe, Selbstermächtigung und Diskriminierung.
Wien hat eine neue Stadtregierung. So wie viele zuvor, setzen die Politiker einen Fokus auf Mobilität. Das Budget für Radwege soll vervierfacht, Straßenbahn-Verbindungen ausgebaut werden. Können wir uns eine Stadt ohne Autos vorstellen? Ein Gespräch mit Harald Frey, Wissenschaftler am Institut für Verkehrsplanung und Verkehrstechnik der Technischen Universität Wien.
Um dir ein optimales Erlebnis zu bieten, verwenden wir Technologien wie Cookies, um Geräteinformationen zu speichern und/oder darauf zuzugreifen. Wenn du diesen Technologien zustimmst, können wir Daten wie das Surfverhalten oder eindeutige IDs auf dieser Website verarbeiten. Wenn du deine Zustimmung nicht erteilst oder zurückziehst, können bestimmte Merkmale und Funktionen beeinträchtigt werden.
Die technische Speicherung oder der Zugang ist unbedingt erforderlich für den rechtmäßigen Zweck, die Nutzung eines bestimmten Dienstes zu ermöglichen, der vom Teilnehmer oder Nutzer ausdrücklich gewünscht wird, oder für den alleinigen Zweck, die Übertragung einer Nachricht über ein elektronisches Kommunikationsnetz durchzuführen.
Die technische Speicherung oder der Zugriff ist für den rechtmäßigen Zweck der Speicherung von Präferenzen erforderlich, die nicht vom Abonnenten oder Benutzer angefordert wurden.
Die technische Speicherung oder der Zugriff, der ausschließlich zu statistischen Zwecken erfolgt.
Die technische Speicherung oder der Zugriff, der ausschließlich zu anonymen statistischen Zwecken verwendet wird. Ohne eine Vorladung, die freiwillige Zustimmung deines Internetdienstanbieters oder zusätzliche Aufzeichnungen von Dritten können die zu diesem Zweck gespeicherten oder abgerufenen Informationen allein in der Regel nicht dazu verwendet werden, dich zu identifizieren.
Die technische Speicherung oder der Zugriff ist erforderlich, um Nutzerprofile zu erstellen, um Werbung zu versenden oder um den Nutzer auf einer Website oder über mehrere Websites hinweg zu ähnlichen Marketingzwecken zu verfolgen.
Zustimmung verwalten
. No dependencies.
* Keeps WordPress/MediaElement playback; uses native inputs for accessibility.
* Optional on : data-audio-title="Titel des Artikels".
*/
(() => {
"use strict";
if (window.andererseitsAudio) {
window.andererseitsAudio.scan();
return;
}
let nextId = 0;
const enhanced = new WeakSet();
const players = new Set();
const clock = (value) => {
const seconds = Math.max(0, Math.floor(value || 0));
const parts = [Math.floor(seconds / 60) % 60, seconds % 60];
if (seconds >= 3600) parts.unshift(Math.floor(seconds / 3600));
return parts.map((part) => String(part).padStart(2, "0")).join(":");
};
const spokenTime = (value) => {
const seconds = Math.max(0, Math.floor(value || 0));
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return [
hours && `${hours} ${hours === 1 ? "Stunde" : "Stunden"}`,
minutes && `${minutes} ${minutes === 1 ? "Minute" : "Minuten"}`,
`${seconds % 60} ${seconds % 60 === 1 ? "Sekunde" : "Sekunden"}`,
]
.filter(Boolean)
.join(", ");
};
function enhance(audio) {
const root = audio.closest(".mejs-container.mejs-audio.wp-audio-shortcode");
const player = audio.player;
if (!root || !player || !player.media || enhanced.has(root)) return;
const controls = root.querySelector(".mejs-controls");
const rail = root.querySelector(".mejs-time-rail");
const play = root.querySelector(".mejs-playpause-button > button");
let muteBox = root.querySelector(".mejs-volume-button");
if (!controls || !rail || !play) return;
enhanced.add(root);
const id = `ae-audio-${++nextId}`;
if (!audio.id) audio.id = `${id}-media`;
const precedingWidget =
root.closest(".elementor-widget")?.previousElementSibling;
const nearbyTitle =
precedingWidget?.matches(
".elementor-widget-text-editor, .elementor-widget-heading",
) && precedingWidget.textContent.trim().length <= 120
? precedingWidget.textContent.trim()
: "";
const explicitTitle =
audio.dataset.audioTitle ||
root.closest("[data-audio-title]")?.dataset.audioTitle ||
audio.getAttribute("aria-label");
const pageTitle = (
root.closest("article")?.querySelector("h1") ||
document.querySelector("h1")
)?.textContent.trim();
const title =
explicitTitle ||
nearbyTitle ||
(pageTitle ? `${pageTitle}, Audio ${nextId}` : "");
root.setAttribute("role", "group");
root.setAttribute(
"aria-label",
title ? `Audio: ${title}` : `Audioplayer ${nextId}`,
);
root.removeAttribute("tabindex");
const oldTitle = root.previousElementSibling;
if (oldTitle?.classList.contains("mejs-offscreen")) oldTitle.hidden = true;
// MediaElement's global shortcuts interfere with buttons/selects and
// restart paused audio when seeking. Native controls own their keys.
player.options.enableKeyboard = false;
player.options.autoRewind = false;
player.options.playText = "Wiedergeben";
player.options.pauseText = "Pausieren";
// −10 / +10 flank Play, so visual and tab order match.
const skipBack = document.createElement("div");
skipBack.className = "ae-audio-skips";
skipBack.innerHTML = `10 `;
const skipForward = document.createElement("div");
skipForward.className = "ae-audio-skips";
skipForward.innerHTML = `10 `;
const tools = document.createElement("div");
tools.className = "ae-audio-tools";
tools.innerHTML = `
1×
Lautstärke
`;
const status = document.createElement("div");
status.className = "ae-audio-status ae-audio-sr";
status.setAttribute("role", "status");
status.setAttribute("aria-live", "polite");
status.setAttribute("aria-atomic", "true");
const error = document.createElement("p");
error.className = "ae-audio-error";
error.hidden = true;
const help = document.createElement("span");
help.id = `${id}-help`;
help.className = "ae-audio-sr";
help.textContent =
"Pfeiltasten: 10 Sekunden. Bild auf oder ab: 30 Sekunden. Pos1: Anfang. Ende: Schluss.";
const seek = document.createElement("input");
seek.type = "range";
seek.className = "ae-audio-range ae-audio-seek";
seek.min = "0";
seek.max = "0";
seek.step = "1";
seek.value = "0";
seek.setAttribute("aria-label", "Wiedergabeposition");
seek.setAttribute("aria-describedby", help.id);
const tooltip = document.createElement("span");
tooltip.className = "ae-audio-tooltip";
tooltip.setAttribute("aria-hidden", "true");
const originalSlider = root.querySelector(".mejs-time-total");
originalSlider?.setAttribute("aria-hidden", "true");
originalSlider?.setAttribute("tabindex", "-1");
rail.append(seek, tooltip, help);
const inner = root.querySelector(".mejs-inner");
play.parentElement.before(skipBack);
play.parentElement.after(skipForward);
controls.append(tools);
inner.append(error, status);
if (!muteBox) {
muteBox = document.createElement("div");
muteBox.className = "mejs-button mejs-volume-button mejs-mute";
muteBox.innerHTML =
' ';
}
const volumeBox = tools.querySelector(".ae-audio-volume");
volumeBox.prepend(muteBox);
const mute = muteBox?.querySelector("button");
const volume = tools.querySelector("input");
const speed = tools.querySelector(".ae-audio-rate");
const skipButtons = [skipBack, skipForward].map((box) => box.firstChild);
let previousVolume = audio.volume || 0.8;
root.querySelectorAll("button, input, select").forEach((control) => {
control.setAttribute("aria-controls", audio.id);
});
// Retain tab order and browser/AT default behavior. No single-letter
// shortcuts, application mode, focus trapping or document key listeners.
let announcementTimer;
function announce(message) {
clearTimeout(announcementTimer);
status.textContent = "";
announcementTimer = setTimeout(() => {
if (root.isConnected) status.textContent = message;
}, 60);
}
const duration = () =>
Number.isFinite(audio.duration) && audio.duration > 0
? audio.duration
: 0;
function loadMetadata() {
if (audio.preload === "none" && !audio.readyState && !audio.error) {
audio.preload = "metadata";
audio.load();
}
}
// Respect lazy loading: only request metadata for the player a person
// enters, rather than downloading every clip on a page with many players.
root.addEventListener("focusin", loadMetadata);
root.addEventListener("pointerenter", loadMetadata, { once: true });
// Handle rejected play promises (network errors, browser restrictions)
// instead of letting MediaElement silently discard them.
play.addEventListener("click", (event) => {
event.stopPropagation();
if (!audio.paused && !audio.ended) {
audio.pause();
return;
}
if (audio.ended) audio.currentTime = 0;
const attempt = audio.play();
attempt?.catch((reason) => {
if (reason.name === "AbortError") return;
error.hidden = false;
error.textContent =
"Wiedergabe nicht möglich. Bitte erneut versuchen oder die Seite neu laden.";
announce(error.textContent);
syncButtons();
});
});
function paintRange(input, value, maximum) {
input.style.setProperty(
"--ae-fill",
`${maximum ? (value / maximum) * 100 : 0}%`,
);
}
function updateTime() {
const maximum = duration();
const current = Math.min(maximum, Math.max(0, audio.currentTime || 0));
seek.max = String(maximum);
seek.value = String(current);
seek.disabled = !maximum || !!audio.error;
seek.setAttribute(
"aria-valuetext",
maximum
? `${spokenTime(current)} von ${spokenTime(maximum)}`
: "Dauer noch nicht verfügbar",
);
paintRange(seek, current, maximum);
tooltip.textContent = clock(current);
skipButtons.forEach((button) =>
button.setAttribute("aria-disabled", String(!maximum || !!audio.error)),
);
}
function syncButtons() {
const playLabel =
audio.paused || audio.ended ? "Wiedergeben" : "Pausieren";
if (play.getAttribute("aria-label") !== playLabel)
play.setAttribute("aria-label", playLabel);
if (play.title !== playLabel) play.title = playLabel;
if (mute) {
const silent = audio.muted || audio.volume === 0;
const label = silent ? "Ton einschalten" : "Stummschalten";
if (mute.getAttribute("aria-label") !== label)
mute.setAttribute("aria-label", label);
if (mute.title !== label) mute.title = label;
muteBox.classList.toggle("mejs-mute", !silent);
muteBox.classList.toggle("mejs-unmute", silent);
}
}
function syncVolume() {
if (audio.volume > 0) previousVolume = audio.volume;
const value = audio.muted ? 0 : Math.round(audio.volume * 100);
volume.value = String(value);
volume.setAttribute(
"aria-valuetext",
`${value} Prozent${audio.muted ? ", stummgeschaltet" : ""}`,
);
paintRange(volume, value, 100);
syncButtons();
}
function seekTo(value, announcePosition = false) {
if (!duration() || audio.error) {
loadMetadata();
if (announcePosition)
announce(
audio.error
? "Audio ist nicht verfügbar."
: "Audio wird geladen. Bitte kurz warten.",
);
return;
}
try {
audio.currentTime = Math.max(0, Math.min(duration(), value));
updateTime();
if (announcePosition)
announce(
`Position: ${spokenTime(audio.currentTime)} von ${spokenTime(duration())}.`,
);
} catch {
announce("Diese Position ist noch nicht verfügbar.");
}
}
skipButtons.forEach((button) =>
button.addEventListener("click", () => {
seekTo(audio.currentTime + Number(button.dataset.seconds), true);
}),
);
seek.addEventListener("input", () => seekTo(Number(seek.value)));
seek.addEventListener("keydown", (event) => {
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
return;
const offsets = {
ArrowLeft: -10,
ArrowDown: -10,
ArrowRight: 10,
ArrowUp: 10,
PageDown: -30,
PageUp: 30,
};
const target =
event.key === "Home"
? 0
: event.key === "End"
? duration()
: event.key in offsets
? audio.currentTime + offsets[event.key]
: null;
if (target === null) return;
event.preventDefault();
event.stopPropagation();
seekTo(target);
});
seek.addEventListener("pointermove", (event) => {
const box = seek.getBoundingClientRect();
const fraction = Math.max(
0,
Math.min(1, (event.clientX - box.left) / box.width),
);
tooltip.textContent = clock(fraction * duration());
});
volume.addEventListener("input", () => {
const value = Number(volume.value) / 100;
audio.volume = value;
audio.muted = value === 0;
if (Math.abs(audio.volume - value) > 0.01) {
announce(
"Bitte die Lautstärketasten des Geräts verwenden. Dieser Browser erlaubt keinen Lautstärkeregler.",
);
}
syncVolume();
});
mute.addEventListener(
"click",
(event) => {
event.stopImmediatePropagation();
if (audio.muted || audio.volume === 0) {
if (audio.volume === 0) audio.volume = previousVolume;
audio.muted = false;
} else audio.muted = true;
syncVolume();
},
true,
);
// Native range supplies arrows/Home/End, including AT increment actions.
// Escape hides the slider shown on hover/focus (WCAG 1.4.13) and returns
// focus to the mute button; it reappears once focus or pointer re-enter.
function dismissVolume(event) {
if (event.key !== "Escape" || getComputedStyle(volume).opacity === "0")
return;
event.preventDefault();
event.stopPropagation();
volumeBox.classList.add("ae-audio-volume-dismissed");
if (document.activeElement === volume) mute.focus();
}
const restoreVolume = () =>
volumeBox.classList.remove("ae-audio-volume-dismissed");
volumeBox.addEventListener("keydown", dismissVolume);
// While hovered, focus may be elsewhere on the page.
volumeBox.addEventListener("pointerenter", () =>
document.addEventListener("keydown", dismissVolume),
);
volumeBox.addEventListener("pointerleave", () => {
document.removeEventListener("keydown", dismissVolume);
restoreVolume();
});
volume.addEventListener("focus", restoreVolume);
volumeBox.addEventListener("focusout", (event) => {
if (!volumeBox.contains(event.relatedTarget)) restoreVolume();
});
// One button cycles the tempo; the visible value is part of its name.
const rates = [1, 1.25, 1.5, 2, 0.5, 0.75];
const rateText = (rate) => `${String(rate).replace(".", ",")}×`;
function syncRate() {
speed.textContent = rateText(audio.playbackRate);
speed.setAttribute(
"aria-label",
`Abspielgeschwindigkeit ${rateText(audio.playbackRate)}`,
);
}
speed.addEventListener("click", () => {
const next =
rates[(rates.indexOf(audio.playbackRate) + 1) % rates.length];
try {
audio.playbackRate = next;
announce(
`Abspielgeschwindigkeit: ${String(audio.playbackRate).replace(".", ",")} mal.`,
);
} catch {
announce(
"Diese Abspielgeschwindigkeit wird vom Browser nicht unterstützt.",
);
}
syncRate();
});
audio.addEventListener("ratechange", syncRate);
[
"timeupdate",
"durationchange",
"loadedmetadata",
"emptied",
"seeked",
].forEach((name) => audio.addEventListener(name, updateTime));
["play", "playing", "pause", "ended", "loadedmetadata"].forEach((name) =>
audio.addEventListener(name, syncButtons),
);
// MediaElement can overwrite localized labels in a later renderer event.
// Observe only those two attributes, and write only when different.
const labelObserver = new MutationObserver(syncButtons);
[play, mute].forEach((button) =>
labelObserver.observe(button, {
attributes: true,
attributeFilter: ["aria-label", "title"],
}),
);
audio.addEventListener("volumechange", syncVolume);
audio.addEventListener("play", () => {
error.hidden = true;
for (const other of players) {
if (!other.isConnected) players.delete(other);
else if (other !== audio && !other.paused) other.pause();
}
});
audio.addEventListener("ended", () => announce("Wiedergabe beendet."));
const showError = () => {
error.hidden = false;
error.textContent =
"Audio konnte nicht geladen werden. Bitte die Seite neu laden oder es später erneut versuchen.";
announce(error.textContent);
updateTime();
};
audio.addEventListener("error", showError);
audio.addEventListener("loadstart", () => {
error.hidden = true;
});
players.add(audio);
root.classList.add("ae-audio-enhanced");
updateTime();
syncVolume();
syncRate();
if (audio.error) showError();
}
function scan() {
document.querySelectorAll("audio.wp-audio-shortcode").forEach(enhance);
}
window.andererseitsAudio = { scan };
// WordPress and Elementor may initialize/add players after this snippet.
let queued = false;
const observer = new MutationObserver((records) => {
if (
queued ||
!records.some((record) =>
[...record.addedNodes].some(
(node) =>
node.nodeType === 1 &&
(node.matches("audio, .mejs-controls") ||
node.querySelector("audio, .mejs-controls")),
),
)
)
return;
queued = true;
requestAnimationFrame(() => {
queued = false;
scan();
});
});
function start() {
scan();
observer.observe(document.body, { childList: true, subtree: true });
}
if (document.readyState === "loading")
document.addEventListener("DOMContentLoaded", start, { once: true });
else start();
})();
/* andererseits Audio-Player Erweiterung – ENDE */