Signatur
Beschreibung
openKeyCursor() durchläuft die Einträge eines IDBIndex in der Reihenfolge des Index. Anders als bei openCursor() wird der gespeicherte Datensatz nicht geladen. Der Cursor ist ein einfaches IDBCursor (kein IDBCursorWithValue). Er bietet nur cursor.key (den Indexschlüssel, z. B. einen Nachnamen) und cursor.primaryKey (den Primärschlüssel des zugehörigen Datensatzes).
Das ist sinnvoll, wenn Sie nur Schlüssel brauchen, etwa um IDs zu sammeln, eindeutige Werte eines Feldes zu ermitteln, Treffer zu zählen oder Einträge seitenweise durchzugehen. Weil die Datensätze nicht deserialisiert werden, ist der Schlüssel-Cursor bei großen Objekten deutlich schneller und braucht weniger Speicher.
Die Methode arbeitet asynchron. Für jede Cursor-Position wird das success-Ereignis des zurückgegebenen Requests ausgelöst. Mit cursor.continue(), cursor.continuePrimaryKey() oder cursor.advance(n) gehen Sie zum nächsten Eintrag. Nach dem letzten Eintrag ist request.result gleich null.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $query | IDBKeyRange | any | null | null (alle Einträge) | Ein einzelner gültiger Schlüssel oder ein IDBKeyRange, der den durchlaufenen Bereich eingrenzt. Bei null oder ohne Angabe wird der ganze Index durchlaufen. |
| $direction | "next" | "nextunique" | "prev" | "prevunique" | "next" | Laufrichtung des Cursors. "next": aufsteigend. "prev": absteigend. Die Varianten mit unique überspringen doppelte Indexschlüssel; dabei bleibt jeweils der Eintrag mit dem kleinsten Primärschlüssel übrig. |
Rückgabewert
IDBRequest. Bei jedem success-Ereignis enthält result das IDBCursor-Objekt an der aktuellen Position. Gibt es keine weiteren Einträge, ist es null.Beispiele
Alle Nachnamen samt Primärschlüssel auflisten
const openReq = indexedDB.open('kontakte', 1);
openReq.onupgradeneeded = () => {
const db = openReq.result;
const store = db.createObjectStore('personen', { keyPath: 'id' });
store.createIndex('nachname', 'nachname');
store.add({ id: 1, vorname: 'Anna', nachname: 'Schmidt' });
store.add({ id: 2, vorname: 'Ben', nachname: 'Meier' });
store.add({ id: 3, vorname: 'Clara', nachname: 'Meier' });
};
openReq.onsuccess = () => {
const db = openReq.result;
const index = db.transaction('personen').objectStore('personen').index('nachname');
index.openKeyCursor().onsuccess = (event) => {
const cursor = event.target.result;
if (!cursor) return console.log('Fertig');
// cursor.value gibt es hier nicht – nur die Schlüssel
console.log(`${cursor.key} -> id ${cursor.primaryKey}`);
cursor.continue();
};
};
Eindeutige Werte eines Feldes ermitteln (nextunique)
const eindeutigeNachnamen = (db) => new Promise((resolve, reject) => {
const index = db.transaction('personen').objectStore('personen').index('nachname');
const namen = [];
const req = index.openKeyCursor(null, 'nextunique');
req.onsuccess = () => {
const cursor = req.result;
if (cursor) {
namen.push(cursor.key);
cursor.continue();
} else {
resolve(namen);
}
};
req.onerror = () => reject(req.error);
});
// Nutzung (db aus indexedDB.open):
// console.log(await eindeutigeNachnamen(db));
IDs in einem Bereich sammeln und löschen
// Alle Bestellungen vor 2023 löschen, ohne die Datensätze zu laden
const loescheAlteBestellungen = (db) => new Promise((resolve, reject) => {
const tx = db.transaction('bestellungen', 'readwrite');
const store = tx.objectStore('bestellungen');
const range = IDBKeyRange.upperBound(new Date('2023-01-01'), true);
let anzahl = 0;
store.index('datum').openKeyCursor(range).onsuccess = (event) => {
const cursor = event.target.result;
if (!cursor) return;
store.delete(cursor.primaryKey);
anzahl++;
cursor.continue();
};
tx.oncomplete = () => resolve(anzahl);
tx.onerror = () => reject(tx.error);
});
// const geloescht = await loescheAlteBestellungen(db);
// console.log(`${geloescht} Bestellungen entfernt`);
// Wichtig · Fallstricke
- Kein Datensatz verfügbar: Der Cursor ist ein reines
IDBCursor.cursor.valueistundefined, undcursor.update()bzw.cursor.delete()lösen eineInvalidStateErroraus. Löschen Sie stattdessen über den Store mitstore.delete(cursor.primaryKey). Den vollständigen Datensatz liefertopenCursor(). - Transaktion läuft ab: Rufen Sie zwischen zwei
continue()-Aufrufen keinawaitauf Fremd-Promises auf, etwafetchodersetTimeout. Sonst wird die Transaktion automatisch abgeschlossen, und der nächste Aufruf wirft eineTransactionInactiveError. - Ausnahmen:
TransactionInactiveErrorbei inaktiver Transaktion.DataError, wennquerykein gültiger Schlüssel bzw. Bereich ist.InvalidStateError, wenn der Index oder sein Store gelöscht wurde.TypeErrorbei ungültigerdirection. - Alternative: Wenn Sie nur alle Primärschlüssel eines Bereichs brauchen, ist
index.getAllKeys(query, count)meist einfacher und schneller. Der Cursor lohnt sich, wenn Sie früh abbrechen, Einträge überspringen odernextuniqueverwenden wollen. - Kompatibilität: Die Methode gehört zu IndexedDB 2.0 und wird von allen aktuellen Browsern unterstützt (Chrome, Edge, Firefox, Safari). Alte Browser wie der Internet Explorer kennen sie nicht. In manchen Browsern ist IndexedDB im privaten Modus eingeschränkt.