Start · Sprachen · JavaScript · Referenz · IDBObjectStore.openCursor()

IDBObjectStore.openCursor()

Methode

Öffnet einen Cursor über die Datensätze eines Object Stores und liefert per <code>IDBRequest</code> ein <code>IDBCursorWithValue</code> zum schrittweisen Durchlaufen.

seit JavaScript Baseline: alle Browser (IndexedDB 2.0) Kategorie: webapi

Signatur

IDBObjectStore.prototype.openCursor(query, direction)

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');
    }
  };
};
1 Tastatur 2 Maus 3 Monitor 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();
  };
};
Monitor: 199 € Maus: 25 €

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);
Preise aktualisiert

// Wichtig · Fallstricke

  • Kein Iterator: Den Cursor kannst du nicht mit for…of oder await in einer Schleife abarbeiten. Du musst continue() aufrufen, sonst endet die Iteration nach dem ersten Datensatz.
  • Transaktion bleibt nicht offen: Wenn du im success-Handler auf ein await wartest (zum Beispiel auf fetch), wird die Transaktion automatisch beendet. Der nächste continue()-Aufruf wirft dann einen TransactionInactiveError.
  • continue() darf pro success-Event nur einmal aufgerufen werden, sonst wird ein InvalidStateError geworfen.
  • update() und delete() funktionieren nur in einer readwrite-Transaktion. In readonly werfen sie einen ReadOnlyError.
  • Ein ungültiger Schlüssel als query führt zu einem DataError. Ist der Store gelöscht, kommt ein InvalidStateError.
  • Für einfache Massenabfragen sind getAll() oder getAllKeys() 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.