Signatur
Beschreibung
openCursor() startet eine asynchrone Iteration über die Datensätze eines IDBObjectStore. Die Methode gibt sofort ein IDBRequest zurück. Dessen success-Event feuert einmal für jeden gefundenen Datensatz. In request.result steht dann ein IDBCursorWithValue, der Primärschlüssel (cursor.key/cursor.primaryKey) und Wert (cursor.value) enthält. Sind alle Datensätze durchlaufen, ist result gleich null.
Ein Cursor eignet sich, wenn du große Datenmengen verarbeiten willst, ohne sie wie mit getAll() komplett in den Speicher zu laden. Er ist auch das Mittel der Wahl, wenn du beim Durchlaufen Datensätze gezielt ändern (cursor.update()) oder löschen (cursor.delete()) willst. Mit einem IDBKeyRange grenzt du den Bereich ein, und mit direction legst du die Reihenfolge fest: aufsteigend oder absteigend nach Primärschlüssel.
Nach jedem Datensatz musst du cursor.continue() (oder advance()/continuePrimaryKey()) aufrufen, damit der nächste success-Event ausgelöst wird. Brauchst du nur die Schlüssel, ist openKeyCursor() sparsamer. Wenn du nach einem anderen Feld als dem Primärschlüssel suchst, nutze IDBIndex.openCursor().
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $query | IDBKeyRange | any | null | null | Ein einzelner gültiger Schlüssel oder ein IDBKeyRange, der den Bereich festlegt. Bei null oder ohne Angabe werden alle Datensätze durchlaufen. |
| $direction | "next" | "nextunique" | "prev" | "prevunique" | "next" | Legt die Richtung fest: "next" geht aufsteigend, "prev" absteigend. Die *unique-Varianten verhalten sich im Object Store genauso, weil Primärschlüssel dort ohnehin eindeutig sind. |
Beispiele
Alle Datensätze eines Stores durchlaufen
const openReq = indexedDB.open('shop', 1);
openReq.onupgradeneeded = () => {
const store = openReq.result.createObjectStore('produkte', { keyPath: 'id' });
store.add({ id: 1, name: 'Tastatur', preis: 49 });
store.add({ id: 2, name: 'Maus', preis: 25 });
store.add({ id: 3, name: 'Monitor', preis: 199 });
};
openReq.onsuccess = () => {
const db = openReq.result;
const store = db.transaction('produkte', 'readonly').objectStore('produkte');
store.openCursor().onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
console.log(cursor.key, cursor.value.name);
cursor.continue(); // nächsten Datensatz anfordern
} else {
console.log('Fertig');
}
};
};
Bereich einschränken und absteigend sortieren
const zeigeBereich = (db) => {
const store = db.transaction('produkte').objectStore('produkte');
const range = IDBKeyRange.bound(2, 3); // IDs 2 bis 3 (inklusive)
store.openCursor(range, 'prev').onsuccess = (event) => {
const cursor = event.target.result;
if (!cursor) return;
console.log(`${cursor.value.name}: ${cursor.value.preis} €`);
cursor.continue();
};
};
Datensätze beim Durchlaufen ändern und löschen
const preiseAnpassen = (db) => new Promise((resolve, reject) => {
const tx = db.transaction('produkte', 'readwrite');
const store = tx.objectStore('produkte');
store.openCursor().onsuccess = (event) => {
const cursor = event.target.result;
if (!cursor) return;
const produkt = cursor.value;
if (produkt.preis < 30) {
cursor.delete(); // Billigartikel entfernen
} else {
cursor.update({ ...produkt, preis: Math.round(produkt.preis * 1.1) });
}
cursor.continue();
};
tx.oncomplete = () => resolve('Preise aktualisiert');
tx.onerror = () => reject(tx.error);
});
// Nutzung: preiseAnpassen(db).then(console.log);
// Wichtig · Fallstricke
- Kein Iterator: Den Cursor kannst du nicht mit
for…ofoderawaitin einer Schleife abarbeiten. Du musstcontinue()aufrufen, sonst endet die Iteration nach dem ersten Datensatz. - Transaktion bleibt nicht offen: Wenn du im
success-Handler auf einawaitwartest (zum Beispiel auffetch), wird die Transaktion automatisch beendet. Der nächstecontinue()-Aufruf wirft dann einenTransactionInactiveError. continue()darf prosuccess-Event nur einmal aufgerufen werden, sonst wird einInvalidStateErrorgeworfen.update()unddelete()funktionieren nur in einerreadwrite-Transaktion. Inreadonlywerfen sie einenReadOnlyError.- Ein ungültiger Schlüssel als
queryführt zu einemDataError. Ist der Store gelöscht, kommt einInvalidStateError. - Für einfache Massenabfragen sind
getAll()odergetAllKeys()meist schneller. Nimm den Cursor, wenn es um den Speicherverbrauch geht oder du einzelne Datensätze änderst. - Die Methode wird von allen modernen Browsern unterstützt, auch in Web Workern. In manchen Browsern ist IndexedDB im privaten Modus eingeschränkt.