Technische Referenz
Hochgeladene interaktive HTML-Inhalte mit LearningView verbinden
Gib deinem Bot einfach die URL https://learningview.org/embed-api.php und bitte ihn, die hier beschriebene Schnittstelle in deinen generierten, interaktiven Lerninhalt einzubauen. So kann der Inhalt mit LearningView kommunizieren, zum Beispiel Lernstände speichern und laden oder eine Klassenübersicht anzeigen.
Die technischen Details kannst du bei Bedarf unten aufklappen.
Mit generativen Werkzeugen lassen sich heute einfach eigene interaktive Lerninhalte als HTML-Inhalte erstellen – beispielsweise Übungen, Lernspiele, Simulationen, Quizze oder kleine Lernprogramme. Solche Inhalte können direkt in LearningView eingesetzt werden. Wähle dazu bei einer Aufgabe den Typ „Anbieter“ und anschliessend „Generiert“ aus.
Mit generativen Werkzeugen erstellte Inhalte müssen vor dem Einsatz mit Schülerinnen und Schülern fachlich und didaktisch genau überprüft werden. Sie dürfen nicht unreflektiert eingesetzt werden. Die Verantwortung für Korrektheit, Eignung und altersgerechte Gestaltung liegt bei der Lehrperson.
Ein vom Nutzer in LearningView hochgeladener HTML-Inhalt wird in einem eingebetteten
Fenster geöffnet.
Diese Seite erklärt, wie der erstellte Inhalt über die Browser-Schnittstelle
window.parent.postMessage() mit LearningView kommunizieren kann. Dadurch kann
der Inhalt Informationen zum Abschluss melden, seinen Bearbeitungsstand speichern und
laden, als Lehrperson eine Klassenübersicht darstellen oder einen Dateidownload auslösen.
Technische Hinweise für die Kommunikation zwischen interaktivem Inhalt und LearningView
01 · Dateiformat
Interaktiven Inhalt möglichst als einzelne HTML-Datei erzeugen
Ein generatives Werkzeug sollte einen interaktiven Inhalt idealerweise als eine einzelne,
eigenständig lauffähige .html-Datei erzeugen. HTML, CSS, JavaScript und
möglichst auch alle weiteren benötigten Inhalte sollen direkt in dieser Datei integriert
sein. Dadurch kann der Nutzer den Inhalt besonders einfach in LearningView hochladen.
- Bevorzugt eine einzelne, vollständige und eigenständig lauffähige
.html-Datei erzeugen. - Styles, Skripte und nach Möglichkeit auch Bilder oder weitere Daten direkt in die HTML-Datei integrieren.
- Werden mehrere Dateien benötigt, ein vollständiges Projekt mit relativen Dateipfaden anlegen.
- Das Mehrdatei-Projekt muss vor dem Hochladen in LearningView als ZIP-Datei verpackt werden.
- Um diesen Schritt zu vereinfachen, den fertigen Inhalt nach Möglichkeit direkt als
.zip-Datei bereitstellen.
Eine ZIP-Datei ist insbesondere sinnvoll, wenn der Inhalt separate Bilder, Audio- oder Videodateien, Datendateien, Stylesheets oder JavaScript-Dateien benötigt. Alle benötigten Projektdateien müssen im ZIP enthalten und über relative Pfade miteinander verknüpft sein.
02 · Grundlagen
Nachrichten an das Elternfenster senden
Nachrichten werden immer vom HTML-Inhalt an window.parent gesendet. Der
folgende kleine Helfer verhindert lediglich, dass beim direkten Öffnen der Datei eine
Nachricht an das eigene Fenster gesendet wird.
const LEARNINGVIEW_ORIGIN = "https://learningview.org";
function sendToLearningView(message) {
if (window.parent === window) {
console.warn("Dieser HTML-Inhalt läuft nicht in LearningView.");
return;
}
window.parent.postMessage(message, LEARNINGVIEW_ORIGIN);
}
Kindfenster → LearningView
Der HTML-Inhalt ruft window.parent.postMessage(...) auf.
LearningView → Kindfenster
Antworten werden über ein message-Ereignis am eigenen Fenster empfangen.
https://learningview.org verwendet. Der Browser stellt
die Nachricht dadurch nur dem Elternfenster zu, wenn dessen Origin exakt übereinstimmt.
Beim Empfangen sollten sowohl event.source === window.parent als auch
event.origin === LEARNINGVIEW_ORIGIN geprüft werden.
localStorage in eingebetteten Inhalten:
HTML-Inhalte werden in LearningView in einer Sandbox ausgeführt. Deshalb steht
window.localStorage dem eingebetteten Inhalt nicht zur Verfügung; ein Zugriff
kann fehlschlagen oder eine Ausnahme auslösen. Generierte Anwendungen sollten
localStorage nicht verwenden und dürfen für ihre Funktion nicht davon
abhängig sein. Persistente Zustände sollen stattdessen über die nachfolgend beschriebene
LearningView-Schnittstelle mit saveAnswerData und
getAnswerData gespeichert und geladen werden.
03 · Persistente Antwortdaten
Daten speichern und laden
saveAnswerData / getAnswerDataMit den Antwortdaten kann ein interaktiver HTML-Inhalt seinen Zustand im Aufgabenstatus der Schülerin oder des Schülers ablegen. LearningView speichert genau eine Zeichenkette. Strukturierte Zustände werden deshalb als JSON serialisiert.
LearningView speichert und lädt die Daten für jede Schülerin und jeden Schüler eines Kurses einzeln. Bei Partner- und Gruppenarbeiten wird der gespeicherte Datenstand für alle Gruppenmitglieder als separate Kopie abgelegt.
Einzelne Antwortdaten können mit einem Schülerkonto gelesen und gespeichert werden. Beim Anzeigen einer Schülerlösung durch eine Lehrperson ist nur Lesen möglich. Für die Übersicht über den ganzen Kurs gibt es getClassAnswerData.
Daten speichern
Das Feld data muss eine Zeichenkette sein. Sobald LearningView die Daten
erfolgreich gespeichert hat, sendet es die Zeichenkette "SAVED" an den
HTML-Inhalt zurück. Der Listener muss vor der Speicheranfrage registriert werden.
function saveAnswerData(state) {
return new Promise((resolve, reject) => {
const timeout = window.setTimeout(() => {
window.removeEventListener("message", receiveSaveConfirmation);
reject(new Error("Keine Speicherbestätigung von LearningView"));
}, 15000);
function receiveSaveConfirmation(event) {
if (event.source !== window.parent) return;
if (event.origin !== LEARNINGVIEW_ORIGIN) return;
if (event.data?.type === "saveAnswerDataError") {
window.clearTimeout(timeout);
window.removeEventListener("message", receiveSaveConfirmation);
reject(new Error(event.data.error));
return;
}
if (event.data !== "SAVED") return;
window.clearTimeout(timeout);
window.removeEventListener("message", receiveSaveConfirmation);
resolve();
}
window.addEventListener("message", receiveSaveConfirmation);
sendToLearningView({
type: "saveAnswerData",
data: JSON.stringify(state)
});
});
}
const state = {
version: 1,
currentQuestion: 3,
answers: ["Bern", "Zürich", "Genf"]
};
saveAnswerData(state).then(() => {
console.log("Daten wurden gespeichert.");
});
"SAVED" enthält keine Anfrage-ID. Deshalb sollte erst nach
der Bestätigung die nächste Speicheranfrage gesendet werden. Häufige Änderungen können
zusätzlich gebündelt werden (Debounce).
saveAnswerData.datadarf höchstens 1 MB (1.048.576 Bytes in UTF-8) gross sein. Die Anzahl der JavaScript-Zeichen ist dafür nicht massgeblich. Beispielsweise benötigen Umlaute und Emojis mehrere Bytes.- Grössere Zustände werden verworfen und nicht gespeichert. LearningView zeigt einmal pro geöffnetem Inhalt einen Hinweis. Ein zuvor akzeptierter, noch gepufferter Zustand bleibt erhalten.
- Die erste Speicheranfrage wird sofort übertragen. Danach startet höchstens alle 2 Sekunden eine weitere Übertragung. Während einer laufenden Übertragung startet keine zweite.
- In der Zwischenzeit hält LearningView nur den neuesten akzeptierten Zustand im Puffer. Neue Zustände ersetzen den gepufferten Zustand, ohne den Timer erneut zu starten. Dies ist eine Begrenzung mit nachfolgendem Speichern, kein unbegrenzt aufschiebendes Debounce.
- Die Bestätigung
"SAVED"erfolgt erst, wenn der neueste akzeptierte Zustand erfolgreich gespeichert ist. Überschriebene Zwischenstände erhalten keine eigene Bestätigung. - Beim normalen Schliessen des Inhalts wird ein ausstehender Zustand nach Ablauf der Wartezeit weiterhin gespeichert. Die abschliessende Ergebnisübernahme wartet darauf. Das Schliessen oder Neuladen der gesamten App bzw. Browserseite kann eine noch ausstehende Speicherung abbrechen.
Bei zu grossen Daten erhält der Inhalt
{type: "saveAnswerDataError", error: "ANSWER_DATA_TOO_LARGE", maxBytes: 1048576}.
Schlägt eine Übertragung fehl, lautet der Fehler SAVE_FAILED.
Ein Fehler ist keine Speicherbestätigung. Der Inhalt sollte ihn anzeigen und bei einem
Übertragungsfehler eine erneute Speicherung anbieten. Bereits gepufferte neuere Daten
werden weiterhin nach den genannten Regeln übertragen.
Daten laden
Zuerst wird der Listener registriert, danach wird getAnswerData gesendet.
LearningView antwortet asynchron mit einem Objekt vom Typ sendAnswerData.
Wenn noch nichts gespeichert wurde, ist data eine leere Zeichenkette.
function loadAnswerData() {
return new Promise((resolve, reject) => {
const timeout = window.setTimeout(() => {
window.removeEventListener("message", receiveAnswerData);
reject(new Error("Keine Antwort von LearningView"));
}, 5000);
function receiveAnswerData(event) {
if (event.source !== window.parent) return;
if (event.origin !== LEARNINGVIEW_ORIGIN) return;
if (!event.data || event.data.type !== "sendAnswerData") return;
window.clearTimeout(timeout);
window.removeEventListener("message", receiveAnswerData);
resolve(event.data.data); // immer eine Zeichenkette
}
window.addEventListener("message", receiveAnswerData);
sendToLearningView({ type: "getAnswerData" });
});
}
loadAnswerData().then((savedText) => {
if (!savedText) return;
const state = JSON.parse(savedText);
console.log("Gespeicherter Zustand:", state);
});
Alternativ zu type akzeptiert LearningView auch das Feld
action. Die Ladeanfrage darf ausserdem direkt als Zeichenkette gesendet werden:
sendToLearningView({
action: "saveAnswerData",
data: "ein einfacher Textwert"
});
sendToLearningView("getAnswerData");
Neben der oben beschriebenen Einbindung über „Anbieter“ → „Generiert“ kann ein HTML-Inhalt auch als Anhang einer Aufgabe hinzugefügt werden. Soll er in diesem Sonderfall Antwortdaten speichern oder laden, gelten folgende Voraussetzungen:
- Der HTML-Inhalt wurde als Anhang bei einer Aufgabe hochgeladen.
-
Die Aufgabe muss in LearningView zwingend die Abgabeform keine verwenden.
Eine zusätzliche Abgabeform kann nicht gleichzeitig genutzt werden, weil
saveAnswerDatadasselbe Antwortfeld der Aufgabe verwendet. -
Pro Aufgabe darf nur ein interaktiver Inhalt
saveAnswerDataverwenden. Mehrere solche Inhalte teilen dasselbe Antwortfeld und können ihre gespeicherten Daten deshalb gegenseitig überschreiben. - LearningView akzeptiert Lade- und Speichernachrichten nur vom tatsächlich eingebetteten HTML-Fenster.
04 · Klassenansicht
Gespeicherte Daten des ganzen Kurses auswerten
getClassAnswerDataÖffnet eine Lehrperson einen generierten Inhalt innerhalb eines Kurses, ohne eine einzelne Schülerin oder einen einzelnen Schüler auszuwählen, kann der Inhalt eine Klassenübersicht anzeigen. LearningView liefert die individuellen Speicherstände mit Namen. Welche Werte, Tabellen oder Diagramme daraus entstehen, entscheidet der HTML-Inhalt selbst, denn nur er kennt die Struktur seiner gespeicherten Daten. Dies gilt für HTML-Dateien und die HTML-Startdatei eines ZIP-Pakets unter „Anbieter“ → „Generiert“.
Startparameter: window.learningView.mode
LearningView setzt window.learningView vor dem ersten Skript des eingebetteten
HTML-Dokuments. Der Parameter ist ein JavaScript-Objekt, kein URL-Queryparameter: Die Inhalte
laufen in einem isolierten Blob-Dokument. ZIP-Ressourcen werden weiterhin relativ zur Startdatei aufgelöst.
// Beim Start verfügbar, keine postMessage-Anfrage erforderlich:
const mode = window.learningView?.mode || "student";
const isClassView = mode === "class";
class: Lehrperson öffnet eine Aufgabe im Kurs ohne ausgewählte Person. Klassenübersicht anzeigen undgetClassAnswerDataanfordern.student: Schülerkonto bearbeitet den Inhalt.getAnswerDataundsaveAnswerDataverwenden.studentReview: Lehrperson betrachtet eine einzelne Schülerlösung.getAnswerDataverwenden und die Darstellung schreibgeschützt halten.preview: Vorschau ohne Schüler- oder passenden Kurskontext. Keine Antwortdaten anfordern oder speichern.- Ausserhalb von LearningView und bei älteren Versionen kann
window.learningViewfehlen. Bestehende Inhalte ohne Modusprüfung funktionieren weiterhin als Einzelansicht; eine Klassenübersicht entsteht erst durch deren Implementierung.
Kontext per Nachricht abfragen: getEmbedContext
Direkt eingebettete Webseiten können denselben Startmodus über postMessage
abfragen. Diese Anfrage liefert keine Namen, IDs oder Aufgabenstände und benötigt keine
Datenfreigabe. Sie steht auch generierten HTML-Inhalten zur Verfügung.
sendToLearningView({ type: "getEmbedContext" });
// Antwort (Beispiel):
{ type: "sendEmbedContext", mode: "studentReview" }
Listener vor der Anfrage registrieren und Quelle sowie Ursprung der Antwort prüfen.
Mögliche Modi: class, student, studentReview, preview.
Der Modus allein ist keine Berechtigung; Datenanfragen werden zusätzlich geprüft.
Dialoggrösse anfordern: setDialogSize
Ein eingebetteter Inhalt kann die maximal verfügbare Dialogfläche anfordern, etwa für ein breites Panorama. Wie bei den Editoren nutzt der Dialog die gesamte Bildschirmfläche; für die blauen Kursbuttons bleibt unten kein reservierter Abstand. Die sicheren Bildschirmränder werden weiterhin berücksichtigt. Dies vergrössert den Dialog innerhalb von LearningView und aktiviert keinen Browser-Vollbildmodus. Ohne Anfrage bleibt die bisherige Standardgrösse erhalten. Die Funktion ist unabhängig von Datenfreigaben sowohl für externe Webseiten als auch generierte HTML-Inhalte verfügbar.
// Möglichst viel Platz nutzen:
window.parent.postMessage({type: "setDialogSize", size: "max"}, "https://learningview.org");
// Zur Standardgrösse zurückkehren:
window.parent.postMessage({type: "setDialogSize", size: "auto"}, "https://learningview.org");
Akzeptiert werden nur max und auto, keine beliebigen CSS-Werte.
LearningView prüft das konkrete iframe-Fenster und bei Webseiten dessen Ursprung.
Beim Wechsel werden manuelle Verschiebungen und Grössenänderungen zurückgesetzt.
Es gibt keine Bestätigungsnachricht.
Nur die Kursliste anfordern: getClassStudents
Für die Anbindung externer Anwendungen kann die Lehrpersonen-Klassenansicht ausschliesslich
Namen und IDs abfragen, ohne individuelle Aufgabenstände zu lesen. Es gelten dieselben
Kontextbedingungen wie für getClassAnswerData: eingebetteter Inhalt im aktuellen
Kurs, keine ausgewählte Schülerlösung und Abgabeform keine.
sendToLearningView({ type: "getClassStudents" });
// Antwort:
{
type: "sendClassStudents",
data: [{ studentId: "123", name: "Ada", surname: "Müller" }]
}
Ein leerer Kurs liefert data: []. Bei fehlender Berechtigung oder einem
zwischenzeitlichen Kurswechsel enthält die Antwort zusätzlich
error: "CLASS_STUDENTS_UNAVAILABLE". Auch hier müssen Quelle und Ursprung
eingehender Nachrichten geprüft werden.
Beide Klassenabfragen stehen generierten HTML-Inhalten und direkt als HTTP(S)-URL eingebetteten Webseiten zur Verfügung. Bei externen Webseiten prüft LearningView neben dem konkreten iframe auch den Ursprung der eingebetteten URL und antwortet nur an diesen Ursprung. Es gibt keine anbieterspezifische Freigabeliste; die Lehrperson entscheidet über die Datenfreigabe.
Ausgewählte Person anfordern: getSelectedStudent
Beim Anzeigen einer einzelnen Schülerlösung (studentReview) kann ein Inhalt
Name und LearningView-ID genau dieser Person anfragen, etwa um sie in einer externen Anwendung
auszuwählen. LearningView erlaubt dies nur einer Lehrperson im zugehörigen aktuellen Kurs,
wenn die Person zum Kurs gehört und die Aufgabe die Abgabeform keine hat.
Eine vom iframe gesendete Schüler-ID wird nicht verwendet. Aufgabenstände werden nicht gelesen.
sendToLearningView({ type: "getSelectedStudent" });
// Antwort nach Zustimmung:
{
type: "sendSelectedStudent",
data: { studentId: "123", name: "Ada", surname: "Müller" }
}
Bei fehlender Berechtigung oder ungültig gewordenem Kontext lautet die Antwort
{type: "sendSelectedStudent", data: null, error: "SELECTED_STUDENT_UNAVAILABLE"}.
Die Anfrage steht generiertem HTML und direkt eingebetteten HTTP(S)-Webseiten zur Verfügung.
Die Klassenabfragen bleiben in dieser Einzelansicht gesperrt.
Eine externe Klasse dauerhaft zuordnen
Mit includeConnectionKey: true kann eine freigegebene Datenanfrage zusätzlich eine
zufällige Kurskennung anfordern. Das gilt für getClassStudents,
getSelectedStudent und getClassAnswerData innerhalb ihrer jeweiligen Berechtigungen.
sendToLearningView({ type: "getClassStudents", includeConnectionKey: true });
// Antwort nach Zustimmung:
{
type: "sendClassStudents",
data: [{ studentId: "123", name: "Ada", surname: "Müller" }],
context: { key: "…64 zufällige Hex-Zeichen…" }
}
LearningView merkt die Kennung lokal je Benutzerkonto, Kurs-ID und vollständiger iframe-URL beziehungsweise HTML-Inhaltsidentität. Ganze Kursansicht und einzelne Schülerlösungen desselben Inhalts erhalten dieselbe Kennung. Andere Kurse, Konten oder Inhalte erhalten eigene Kennungen. Die Kennung enthält weder die interne Kurs-ID noch den Kursnamen oder die Benutzer-ID. Der Anbieter kann damit seine eigene Klassenzuordnung wiederfinden; sie ist kein Zugangstoken und ersetzt weder Anmeldung noch Datenfreigabe.
Ohne diese Option bleibt das Antwortformat unverändert. Bei verweigerter Freigabe wird keine
Kennung erzeugt oder übermittelt. Ohne Browser-Speicher bleibt sie nur bis zum Schliessen erhalten;
nach Löschen des Speichers entsteht eine neue Zuordnung. Fehlt sichere Zufallserzeugung, fehlt
context in der Antwort. Anwendungen, die eine feste Zuordnung benötigen, müssen dann
anhalten. Das Zurücksetzen der Datenfreigabe entfernt die Klassenzuordnung nicht;
der Anbieter sollte dafür eine eigene Funktion anbieten.
Zustimmung der Lehrperson
Vor der ersten Übertragung fragt LearningView die Lehrperson, ob sie die angeforderten
Daten mit diesem Inhalt teilen möchte. Der Dialog nennt die eingebettete Adresse
beziehungsweise den generierten Inhalt, den Kurs und die Datenart.
getClassStudents, getClassAnswerData und getSelectedStudent
werden getrennt freigegeben. Bei getSelectedStudent gilt eine gemerkte Zustimmung
für die jeweils ausgewählte Person im selben Kurs, sodass beim Wechsel zur nächsten
Schülerlösung nicht erneut gefragt wird.
Ohne Zustimmung werden keine Namen, IDs oder Aufgabenstände übermittelt.
- Mit der nicht vorausgewählten Option Für diesen Inhalt und Kurs in diesem Browser merken kann eine Zustimmung in LearningViews eigenem
localStoragegespeichert werden. Dies ist eine lokale Erlaubnis, kein Zugangstoken für den Inhalt. - Die Freigabe gilt getrennt nach Lehrperson, Kurs, Datenart und vollständiger Webseiten-URL. Bei generiertem HTML verwendet LearningView stattdessen einen SHA-256-Fingerabdruck aus HTML und Ressourcenbasis. Geänderter HTML-Inhalt benötigt erneut Zustimmung; die gemeinsame Sandbox-Herkunft
nullist keine Identität. - Ohne Merken gilt die Entscheidung nur für den geöffneten Inhalt. Eine Ablehnung wird dort ebenfalls berücksichtigt, damit wiederholte Anfragen keine neuen Dialoge auslösen. Nach erneutem Öffnen kann wieder gefragt werden.
- Das Schildsymbol Datenfreigaben zurücksetzen in LearningViews iframe-Titelleiste erscheint nur, wenn eine gespeicherte Zustimmung für diesen Inhalt, Kurs und die aktuelle Lehrperson vorliegt. Darüber können alle drei Freigaben gelöscht werden; anschliessend verschwindet der Button. Bereits übermittelte Daten werden dadurch beim Empfänger nicht gelöscht. Nicht gemerkte Entscheidungen enden beim Schliessen des Inhalts.
- Wenn Browser-Speicher oder ein sicherer HTML-Fingerabdruck nicht verfügbar sind, bleibt eine Zustimmung auf den geöffneten Inhalt beschränkt. Die Entscheidung wird nicht zwischen Geräten oder Browsern synchronisiert.
Während LearningView die Freigabe prüft, sendet es
{type: "classDataConsentPending", request: "getClassStudents"}
beziehungsweise request: "getClassAnswerData" oder request: "getSelectedStudent". Inhalte müssen ein laufendes
Antwort-Zeitlimit dann aussetzen: Die Lehrperson darf für die Entscheidung länger brauchen.
Anschliessend folgt die normale Antwort. Bei Ablehnung enthält diese
data: [] (bei getSelectedStudent: data: null) und error: "CLASS_DATA_CONSENT_DENIED".
Eine ungültige Kursberechtigung liefert weiterhin den jeweiligen …_UNAVAILABLE-Fehler.
Anfrage und Antwort für Aufgabenstände
sendToLearningView({ type: "getClassAnswerData" });
// Antwort von LearningView (Beispiel):
{
type: "sendClassAnswerData",
data: [
{ studentId: "123", name: "Ada", surname: "Müller", data: '{"version":1,"count":4}' },
{ studentId: "456", name: "Lin", surname: "Meier", data: "" }
]
}
datader Antwort ist ein Array mit einem Eintrag pro Lernendem im geöffneten Kurs, unabhängig von einem Personenfilter der LearningView-Oberfläche. Ein leerer Kurs liefert[].studentIdist die ID als Zeichenkette;nameist der Vorname undsurnameder Nachname. Namen können leer sein. Zum Zuordnen die ID verwenden, nicht den Namen.dataeines Eintrags ist dieselbe Zeichenkette wie beigetAnswerData. Ein interner Score-Präfix wird nicht mitgeliefert. Ohne gespeicherten Zustand ist sie"", auch wenn bereits ein Score vorliegt.- JSON wird nicht automatisch ausgewertet. Jeden Eintrag einzeln mit Fehlerbehandlung parsen; ein ungültiger oder älterer Zustand darf nicht die ganze Übersicht verhindern.
- Die Antwort verwendet die aktuell in LearningView geladenen Aufgabenstände. Es gibt keine Push-Updates; zum Aktualisieren erneut anfragen. Bei Gruppenarbeiten bleibt es bei einem Eintrag pro Person, auch bei identischen Daten.
- Keine Kurs-, Aufgaben- oder Schüler-ID mitsenden: Der geöffnete Dialog bestimmt den Kontext. Die Abfrage ist nur in der Lehrpersonen-Klassenansicht für diese Aufgabe mit Abgabeform keine verfügbar. Sie verändert keine Daten.
- Alternativ werden
{action: "getClassAnswerData"}und die Zeichenkette"getClassAnswerData"akzeptiert.
Bei einer nicht verfügbaren Klassenansicht antwortet LearningView mit
{type: "sendClassAnswerData", data: [], error: "CLASS_ANSWER_DATA_UNAVAILABLE"}.
Das Fehlerfeld unterscheidet eine abgelehnte Anfrage von einem leeren Kurs.
Nachrichten fremder Fenster werden ignoriert. Das Ändern des Startparameters gibt keine zusätzlichen Berechtigungen.
function loadClassAnswerData() {
return new Promise((resolve, reject) => {
const timeout = window.setTimeout(() => {
window.removeEventListener("message", receive);
reject(new Error("Keine Klassenantwort von LearningView"));
}, 5000);
function receive(event) {
if (event.source !== window.parent) return;
if (event.origin !== LEARNINGVIEW_ORIGIN) return;
if (event.data?.type === "classDataConsentPending" && event.data.request === "getClassAnswerData") {
window.clearTimeout(timeout); // Auf die Entscheidung warten, nicht auf Netzwerkzeit begrenzen.
return;
}
if (!event.data || event.data.type !== "sendClassAnswerData") return;
window.clearTimeout(timeout);
window.removeEventListener("message", receive);
if (event.data.error) reject(new Error(event.data.error));
else resolve(event.data.data);
}
window.addEventListener("message", receive);
sendToLearningView({ type: "getClassAnswerData" });
});
}
if (window.learningView?.mode === "class") {
loadClassAnswerData().then((students) => {
// Hier die zum eigenen Inhalt passende Übersicht erstellen.
console.log(students);
}).catch(console.error);
}
getAnswerData, saveAnswerData noch Scores senden. Stattdessen
getClassAnswerData laden und eine zum eigenen Datenformat passende Übersicht
mit Namen, leeren Zuständen und verständlicher Fehleranzeige rendern. Namen und gespeicherte
Texte mit textContent ausgeben, nicht als HTML interpretieren.
05 · Auswertung
Ergebnis als xAPI-Score melden
result.scoreEin HTML-Inhalt kann einen Prozentwert melden. LearningView übernimmt den Wert bei Schülerkonten, wenn der eingebettete Inhalt geschlossen wird. Der gemeldete Prozentwert wird anschliessend auf der Aufgabenkachel angezeigt.
Empfohlen: Wert zwischen 0 und 1
// Meldet 80 %
sendToLearningView({
result: {
score: {
scaled: 0.8
}
}
});
Alternative: Rohwert und Maximalwert
// Meldet ebenfalls 80 %
sendToLearningView({
result: {
score: {
raw: 8,
max: 10
}
}
});
scaledwird mit 100 multipliziert und auf eine ganze Prozentzahl gerundet.- Ohne
scaledwirdraw / max × 100berechnet. - Weitere xAPI-Felder sind erlaubt, werden für diese Auswertung aber nicht benötigt.
- Es gibt keine Antwort- oder Bestätigungsnachricht.
scaled: 0 beziehungsweise raw: 0 mit einem gültigen
max wird als 0 % übernommen.
06 · Datei
Download auslösen
DOWNLOADFILE|
Der Downloadbefehl ist eine Zeichenkette. Direkt nach dem Trennzeichen |
folgt die herunterzuladende Adresse. LearningView verwendet den letzten Teil des
URL-Pfads als Dateinamen.
const fileUrl = "https://example.org/material/arbeitsblatt.pdf";
sendToLearningView("DOWNLOADFILE|" + fileUrl);
- Die Datei muss für LearningView erreichbar sein.
- Eine URL mit einem eindeutigen Dateinamen und einer passenden Endung wird empfohlen.
- Der Befehl liefert keine Bestätigungsnachricht an den HTML-Inhalt zurück.
07 · Kopiervorlage
Minimaler vollständiger HTML-Inhalt
Dieses Beispiel lädt einen Zähler, speichert ihn nach jeder Änderung und meldet bei fünf Klicks 100 %. Lehrpersonen sehen im Klassenmodus die Zählerstände aller Lernenden; einzelne Schülerlösungen bleiben schreibgeschützt.
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Interaktive Aufgabe</title>
</head>
<body>
<button id="counter" type="button">Lade …</button>
<p id="save-status" aria-live="polite"></p>
<ul id="class-results"></ul>
<script>
const button = document.querySelector("#counter");
const saveStatus = document.querySelector("#save-status");
const LEARNINGVIEW_ORIGIN = "https://learningview.org";
const mode = window.learningView?.mode || "student";
const classResults = document.querySelector("#class-results");
button.hidden = mode === "class";
button.disabled = true;
let count = 0;
function sendToLearningView(message) {
if (window.parent !== window) {
window.parent.postMessage(message, LEARNINGVIEW_ORIGIN);
}
}
function render() {
button.textContent = `Klicks: ${count} von 5`;
}
window.addEventListener("message", (event) => {
if (event.source !== window.parent) return;
if (event.origin !== LEARNINGVIEW_ORIGIN) return;
if (event.data?.type === "sendClassAnswerData" && mode === "class") {
classResults.replaceChildren();
if (event.data.error) {
saveStatus.textContent = event.data.error === "CLASS_DATA_CONSENT_DENIED"
? "Die Klassendaten wurden nicht freigegeben."
: "Klassendaten sind nicht verfügbar.";
return;
}
saveStatus.textContent = event.data.data.length ? "Klassenübersicht" : "Keine Lernenden im Kurs.";
for (const student of event.data.data) {
const row = document.createElement("li");
let value = "Noch kein Speicherstand";
if (student.data) {
try {
const saved = JSON.parse(student.data);
value = saved && Number.isFinite(saved.count)
? `${saved.count} von 5 Klicks` : "Unbekanntes Datenformat";
} catch {
value = "Ungültiger Speicherstand";
}
}
row.textContent = `${student.name} ${student.surname}: ${value}`;
classResults.append(row);
}
return;
}
if (event.data?.type === "saveAnswerDataError" && mode === "student") {
saveStatus.textContent = event.data.error === "ANSWER_DATA_TOO_LARGE"
? "Nicht gespeichert: Der Zustand ist grösser als 1 MB."
: "Speichern fehlgeschlagen. Bitte erneut versuchen.";
button.disabled = false;
return;
}
if (event.data === "SAVED" && mode === "student") {
saveStatus.textContent = "Gespeichert";
button.disabled = false;
return;
}
if (!event.data || event.data.type !== "sendAnswerData") return;
if (mode !== "student" && mode !== "studentReview") return;
if (event.data.data) {
try {
const saved = JSON.parse(event.data.data);
count = Number(saved.count) || 0;
} catch (error) {
console.warn("Gespeicherte Daten sind ungültig", error);
}
}
render();
button.disabled = mode !== "student";
});
button.addEventListener("click", () => {
if (mode !== "student") return;
count = Math.min(count + 1, 5);
render();
button.disabled = true;
saveStatus.textContent = "Speichert …";
sendToLearningView({
type: "saveAnswerData",
data: JSON.stringify({ version: 1, count })
});
sendToLearningView({
result: { score: { scaled: count / 5 } }
});
});
render();
if (mode === "class") {
saveStatus.textContent = "Lade Klassenübersicht …";
sendToLearningView({ type: "getClassAnswerData" });
} else if (mode === "preview") {
saveStatus.textContent = "Vorschau ohne gespeicherte Daten";
} else {
sendToLearningView({ type: "getAnswerData" });
}
</script>
</body>
</html>
version, damit spätere Versionen des Inhalts alte Daten migrieren können.
08 · Maschinenlesbare Zusammenfassung
Kurzreferenz für Entwickler:innen und KI-Assistenten
Alle Nachrichten werden aus dem eingebetteten HTML-Dokument mit
window.parent.postMessage(payload, "https://learningview.org") gesendet.
| Funktion | Payload an LearningView | Antwort / Wirkung |
|---|---|---|
| Antwortdaten speichern | {type: "saveAnswerData", data: string} |
"SAVED" nach erfolgreichem Speichern |
| Antwortdaten laden | {type: "getAnswerData"} |
{type: "sendAnswerData", data: string} |
| Klassendaten laden | {type: "getClassAnswerData"} |
{type: "sendClassAnswerData", data: [{studentId, name, surname, data}]}; bei Ablehnung zusätzlich error |
| Nur Kursliste laden | {type: "getClassStudents"} |
{type: "sendClassStudents", data: [{studentId, name, surname}]}; bei Ablehnung zusätzlich error: "CLASS_STUDENTS_UNAVAILABLE" |
| Ausgewählte Person laden | {type: "getSelectedStudent"} |
{type: "sendSelectedStudent", data: {studentId, name, surname}}; bei Ablehnung data: null und error |
| Kontext abfragen | {type: "getEmbedContext"} |
{type: "sendEmbedContext", mode: "studentReview"} (Beispiel) |
| Startmodus lesen | window.learningView?.mode (keine Nachricht) |
class, student, studentReview oder preview |
| Dialoggrösse wählen | {type: "setDialogSize", size: "max"} beziehungsweise size: "auto" |
Verfügbare Dialogfläche ausfüllen beziehungsweise Standardgrösse wiederherstellen; keine Bestätigung |
| Ergebnis melden | {result: {score: {scaled: number}}} |
Der Prozentwert wird beim Schliessen übernommen und auf der Aufgabenkachel angezeigt |
| Datei herunterladen | "DOWNLOADFILE|" + url |
LearningView startet den Download |
- Bevorzugt eine einzelne, eigenständig lauffähige
.html-Datei mit integrierten Inhalten erzeugen; Mehrdatei-Projekte vollständig als ZIP-Datei bereitstellen. - In eingebetteten Inhalten kein
localStorageverwenden oder voraussetzen; der Zugriff ist aufgrund der Sandbox nicht verfügbar und kann fehlschlagen. - Listener vor
saveAnswerDataregistrieren und auf"SAVED"warten. - Listener vor
getAnswerDataregistrieren. - Bei eingehenden Nachrichten
event.source === window.parentundevent.origin === "https://learningview.org"prüfen. - Für
saveAnswerData.dataausschliesslich eine Zeichenkette mit höchstens 1.048.576 UTF-8-Bytes senden. LearningView überträgt höchstens alle 2 Sekunden den neuesten akzeptierten Zustand.saveAnswerDataErrorbehandeln. - Objekte mit
JSON.stringify()speichern und mitJSON.parse()laden. - Antwortdaten werden für jede Schülerin und jeden Schüler eines Kurses einzeln gespeichert und geladen.
- Bei Partner- und Gruppenarbeiten werden die Daten als Kopie für alle Gruppenmitglieder gespeichert.
- Individuelle Daten: Schülerkonten dürfen laden und speichern, Lehrpersonen eine Schülerlösung nur lesen.
- Für die Klassenübersicht zuerst
window.learningView?.mode === "class"prüfen, dann Listener registrieren undgetClassAnswerDatasenden. Im Klassenmodus keine individuellen Daten oder Scores speichern. - Beim Sonderfall Anhang die Aufgabe auf die Abgabeform keine stellen.
- Beim Sonderfall Anhang pro Aufgabe nur einen interaktiven Inhalt mit
saveAnswerDataeinsetzen. - Für Score und Download wird keine Bestätigung gesendet.