Signatur
Beschreibung
openCursor() durchläuft die Datensätze eines Object Stores in der Reihenfolge eines Index, also sortiert nach dem Index-Schlüssel (z. B. Nachname, Datum, Kategorie) statt nach dem Primärschlüssel. Die Methode gibt sofort ein IDBRequest zurück; das eigentliche Öffnen läuft asynchron. Das success-Ereignis wird für jeden Schritt des Cursors erneut ausgelöst.
In request.result steht ein IDBCursorWithValue: cursor.key ist der Index-Schlüssel, cursor.primaryKey der Primärschlüssel des Datensatzes und cursor.value der vollständige Datensatz. Mit cursor.continue() geht es zum nächsten Eintrag, mit cursor.advance(n) überspringt man Einträge. Ist das Ende erreicht, ist request.result null.
Verwende openCursor(), wenn du große Datenmengen schrittweise verarbeiten, Einträge unterwegs ändern bzw. löschen (cursor.update(), cursor.delete() – nur in readwrite-Transaktionen) oder Paginierung und umgekehrte Sortierung umsetzen willst. Brauchst du nur die Schlüssel, ist openKeyCursor() sparsamer; brauchst du alle Werte auf einmal, ist getAll() einfacher.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $query | any | IDBKeyRange | null | null (alle Einträge) | Ein einzelner gültiger Index-Schlüssel oder ein IDBKeyRange, der den Bereich eingrenzt. null oder weggelassen bedeutet: alle Einträge. |
| $direction | "next" | "nextunique" | "prev" | "prevunique" | "next" | Laufrichtung. "next" aufsteigend, "prev" absteigend. Die unique-Varianten liefern bei doppelten Index-Schlüsseln nur den ersten Datensatz (mit kleinstem Primärschlüssel) je Schlüssel. |
Rückgabewert
IDBRequest, dessen result bei jedem success-Ereignis der aktuelle IDBCursorWithValue ist oder null, wenn keine (weiteren) Einträge vorhanden sind.Beispiele
Alle Kunden nach Nachname sortiert ausgeben
const openReq = indexedDB.open('shop', 1);
openReq.onupgradeneeded = () => {
const db = openReq.result;
const store = db.createObjectStore('kunden', { keyPath: 'id' });
store.createIndex('nachname', 'nachname');
store.add({ id: 1, vorname: 'Anna', nachname: 'Schulz' });
store.add({ id: 2, vorname: 'Ben', nachname: 'Albers' });
store.add({ id: 3, vorname: 'Clara', nachname: 'Meier' });
};
openReq.onsuccess = () => {
const db = openReq.result;
const index = db
.transaction('kunden', 'readonly')
.objectStore('kunden')
.index('nachname');
index.openCursor().onsuccess = (event) => {
const cursor = event.target.result;
if (!cursor) {
console.log('Fertig');
return;
}
console.log(`${cursor.key}: ${cursor.value.vorname} (id ${cursor.primaryKey})`);
cursor.continue();
};
};
Bereich einschränken und absteigend sortieren
// Annahme: db ist geöffnet, Store 'bestellungen' hat Index 'datum' (ISO-Strings)
const neuesteBestellungen = (db, ab) =>
new Promise((resolve, reject) => {
const index = db
.transaction('bestellungen')
.objectStore('bestellungen')
.index('datum');
const range = IDBKeyRange.lowerBound(ab);
const ergebnis = [];
const req = index.openCursor(range, 'prev');
req.onsuccess = () => {
const cursor = req.result;
if (cursor && ergebnis.length < 10) {
ergebnis.push(cursor.value);
cursor.continue();
} else {
resolve(ergebnis);
}
};
req.onerror = () => reject(req.error);
});
const liste = await neuesteBestellungen(db, '2024-01-01');
console.log(liste.length);
Einträge mit bestimmtem Index-Wert aktualisieren
// Alle Aufgaben mit status 'offen' als 'archiviert' markieren
const tx = db.transaction('aufgaben', 'readwrite');
const index = tx.objectStore('aufgaben').index('status');
let anzahl = 0;
index.openCursor('offen').onsuccess = (event) => {
const cursor = event.target.result;
if (!cursor) return;
cursor.update({ ...cursor.value, status: 'archiviert' });
anzahl++;
cursor.continue();
};
tx.oncomplete = () => console.log(`${anzahl} Aufgaben archiviert`);
Nur eindeutige Kategorien ermitteln
const kategorien = [];
const index = db.transaction('produkte').objectStore('produkte').index('kategorie');
index.openCursor(null, 'nextunique').onsuccess = (e) => {
const cursor = e.target.result;
if (cursor) {
kategorien.push(cursor.key);
cursor.continue();
} else {
console.log(kategorien);
}
};
// Wichtig · Fallstricke
- Transaktion läuft ab: Rufe
cursor.continue()synchron imsuccess-Handler auf. Einawaitauffetch()o. Ä. dazwischen lässt die Transaktion automatisch committen – der nächste Aufruf wirft dannTransactionInactiveError. continue()darf prosuccess-Ereignis nur einmal aufgerufen werden, sonst folgtInvalidStateError.- Ein ungültiger Schlüssel oder Bereich als
query(z. B.undefinedim Bereich, Objekt,NaN) führt zuDataError; wurde der Index oder Store gelöscht, zuInvalidStateError. cursor.update()/delete()erfordern einereadwrite-Transaktion, sonstReadOnlyError.- Bei
multiEntry-Indizes taucht ein Datensatz für jedes Array-Element einmal auf. - Für reine Schlüssel-Abfragen ist
openKeyCursor()performanter, davaluenicht deserialisiert wird. - Kompatibilität: in allen modernen Browsern und Web Workern verfügbar; im privaten Modus einiger älterer Browser (z. B. Firefox vor Version 115) war IndexedDB eingeschränkt.