Signatur
Beschreibung
IDBCursor ist das Werkzeug der IndexedDB API, um mehrere Datensätze nacheinander zu durchlaufen, ohne alle auf einmal in den Speicher zu laden. Einen Cursor erhält man nicht per Konstruktor, sondern über IDBObjectStore.openCursor(), IDBObjectStore.openKeyCursor(), IDBIndex.openCursor() oder IDBIndex.openKeyCursor(). Das zugehörige IDBRequest feuert für jeden Datensatz ein success-Event; in request.result steht dann der Cursor – oder null, wenn das Ende erreicht ist.
Wichtige Eigenschaften (alle schreibgeschützt):
source– derIDBObjectStoreoderIDBIndex, über den iteriert wird.direction– Laufrichtung:"next","nextunique","prev"oder"prevunique".key– der Schlüssel des aktuellen Datensatzes (bei Index-Cursors der Index-Schlüssel).primaryKey– der Primärschlüssel des aktuellen Datensatzes.request– dasIDBRequest, mit dem der Cursor geöffnet wurde.
Wichtige Methoden:
continue(key?)– springt zum nächsten Datensatz bzw. zum ersten mit Schlüssel ≥key(bei"prev": ≤).continuePrimaryKey(key, primaryKey)– springt bei Index-Cursors zu einer bestimmten Kombination aus Index- und Primärschlüssel (ideal für Paginierung).advance(count)– überspringtcountDatensätze.update(value)– ersetzt den aktuellen Datensatz (nur inreadwrite-Transaktionen); liefert einIDBRequest.delete()– löscht den aktuellen Datensatz (nurreadwrite); liefert einIDBRequest.
Der Wert des Datensatzes selbst steht nur in der Unterklasse IDBCursorWithValue unter value zur Verfügung – diese liefert openCursor(). openKeyCursor() liefert dagegen ein reines IDBCursor ohne Wert, was bei reinen Schlüssel-Abfragen schneller und speichersparender ist.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $key | any (gültiger IndexedDB-Schlüssel) | Für continue(key): Zielschlüssel, zu dem der Cursor springen soll. Ohne Angabe geht es zum nächsten Datensatz. |
|
| $primaryKey | any (gültiger IndexedDB-Schlüssel) | Für continuePrimaryKey(key, primaryKey): Primärschlüssel, ab dem innerhalb gleicher Index-Schlüssel weitergelesen wird. Nur bei Index-Cursors mit Richtung next/prev. |
|
| $count | number | Für advance(count): positive Ganzzahl – Anzahl der zu überspringenden Datensätze. 0 wirft einen TypeError. |
|
| $value | any | Für update(value): neuer Wert des aktuellen Datensatzes. Bei In-line-Keys darf der Schlüssel nicht verändert werden. |
Rückgabewert
request.result des success-Events von openCursor()/openKeyCursor(), am Ende der Iteration ist er null. continue(), advance() und continuePrimaryKey() geben undefined zurück; update() und delete() liefern ein IDBRequest.Beispiele
Alle Datensätze eines Object Stores durchlaufen
const openReq = indexedDB.open('shop', 1);
openReq.onupgradeneeded = () => {
const db = openReq.result;
const store = db.createObjectStore('produkte', { keyPath: 'id' });
store.add({ id: 1, name: 'Tastatur', preis: 49 });
store.add({ id: 2, name: 'Maus', preis: 25 });
};
openReq.onsuccess = () => {
const db = openReq.result;
const store = db.transaction('produkte', 'readonly').objectStore('produkte');
const cursorReq = store.openCursor();
cursorReq.onsuccess = () => {
const cursor = cursorReq.result;
if (cursor) {
console.log(cursor.key, cursor.value.name);
cursor.continue(); // nächster Datensatz -> erneutes success-Event
} else {
console.log('Fertig');
}
};
};
Datensätze per Cursor ändern und löschen
const preiseAnpassen = (db) => {
const tx = db.transaction('produkte', 'readwrite');
const req = tx.objectStore('produkte').openCursor();
req.onsuccess = () => {
const cursor = req.result;
if (!cursor) return;
const produkt = cursor.value;
if (produkt.preis < 30) {
cursor.delete(); // Billigartikel entfernen
} else {
cursor.update({ ...produkt, preis: produkt.preis * 1.1 });
}
cursor.continue();
};
tx.oncomplete = () => console.log('Preise aktualisiert');
};
Rückwärts über einen Index mit Schlüsselbereich (nur Schlüssel)
// Voraussetzung: Index 'nachPreis' auf keyPath 'preis'
const teuersteProdukte = (db) => new Promise((resolve, reject) => {
const index = db.transaction('produkte').objectStore('produkte').index('nachPreis');
const range = IDBKeyRange.lowerBound(40);
const req = index.openKeyCursor(range, 'prev');
const ids = [];
req.onsuccess = () => {
const cursor = req.result;
if (cursor && ids.length < 3) {
ids.push(cursor.primaryKey); // key = Preis, primaryKey = id
cursor.continue();
} else {
resolve(ids);
}
};
req.onerror = () => reject(req.error);
});
// const ids = await teuersteProdukte(db);
Paginierung mit advance()
const seiteLaden = (db, seite, proSeite = 10) => new Promise((resolve) => {
const req = db.transaction('produkte').objectStore('produkte').openCursor();
const ergebnis = [];
let übersprungen = seite === 0;
req.onsuccess = () => {
const cursor = req.result;
if (!cursor) return resolve(ergebnis);
if (!übersprungen) {
übersprungen = true;
cursor.advance(seite * proSeite); // Anfang der Seite ansteuern
return;
}
ergebnis.push(cursor.value);
if (ergebnis.length < proSeite) cursor.continue();
else resolve(ergebnis);
};
});