Start · Sprachen · JavaScript · Referenz · NDEFReader.scan()

NDEFReader.scan()

Methode

Aktiviert das NFC-Lesegerät und liefert ein Promise, das erfüllt wird, sobald das Lesen von NDEF-Tags geplant ist, und bei Hardware- oder Berechtigungsfehlern abgelehnt wird.

seit JavaScript Experimentell: Chrome für Android 89+ (Web NFC), k Kategorie: webapi

Signatur

NDEFReader.prototype.scan(options)

Beschreibung

scan() ist der Einstiegspunkt der Web-NFC-API zum Lesen. Die Methode aktiviert den NFC-Leser des Geräts und bittet dabei, falls noch nicht geschehen, um die Berechtigung "nfc". Das zurückgegebene Promise wird nicht erfüllt, wenn ein Tag gelesen wurde, sondern schon dann, wenn der Lesevorgang erfolgreich eingerichtet ist. Die eigentlichen Daten kommen danach über das Event reading an, Lesefehler über das Event readingerror.

Ein gelesenes Tag wird als NDEFReadingEvent geliefert. Es enthält die serialNumber des Tags und eine message vom Typ NDEFMessage mit einem Array von NDEFRecord-Objekten (recordType, mediaType, data als DataView usw.).

Über ein AbortSignal im Optionsobjekt beendest du den Scan gezielt, etwa nach dem ersten gelesenen Tag oder nach einer Zeitüberschreitung. Das ist wichtig, denn ohne Abbruch bleibt der Leser aktiv, solange die Seite sichtbar ist.

Typische Anwendungen sind Inventur- und Lager-Apps, Check-in-Systeme, Museumsführer und das Auslesen von Smart-Postern, also überall dort, wo ein Android-Smartphone kurz an ein NFC-Tag gehalten wird.

Parameter

Name Typ Default Beschreibung
$options Object {} Optionales Konfigurationsobjekt für den Scan.
$options.signal AbortSignal Signal eines AbortController. Ein Aufruf von abort() beendet den Scan. Ist das Signal schon beim Aufruf abgebrochen, wird das Promise sofort mit einer AbortError-DOMException abgelehnt.

Rückgabewert

Typ
Promise<undefined>
Beschreibung
Wird mit undefined erfüllt, sobald der Leser aktiv ist und Tags empfangen kann. Abgelehnt wird es unter anderem mit NotAllowedError (Berechtigung verweigert oder kein sicherer Kontext), NotSupportedError (keine kompatible NFC-Hardware), NotReadableError (NFC ist deaktiviert oder der Leser belegt), InvalidStateError (es läuft bereits ein Scan) und AbortError (Signal abgebrochen).

Beispiele

Einfacher Scan mit reading- und readingerror-Event

const scanButton = document.querySelector('#scan');

scanButton.addEventListener('click', async () => {
  if (!('NDEFReader' in window)) {
    console.log('Web NFC wird nicht unterstützt.');
    return;
  }

  const reader = new NDEFReader();

  try {
    await reader.scan(); // muss durch eine Nutzeraktion ausgelöst werden
    console.log('Scan gestartet – Tag an das Gerät halten.');

    reader.addEventListener('readingerror', () => {
      console.log('Tag erkannt, aber nicht lesbar (kein NDEF?).');
    });

    reader.addEventListener('reading', ({ serialNumber, message }) => {
      console.log(`Seriennummer: ${serialNumber}`);
      console.log(`Anzahl Records: ${message.records.length}`);
    });
  } catch (error) {
    console.log(`Fehler: ${error.name} – ${error.message}`);
  }
});
Scan gestartet – Tag an das Gerät halten. Seriennummer: 04:a2:3b:1a:5c:6d:80 Anzahl Records: 1

Text- und URL-Records auswerten

const reader = new NDEFReader();

const decodeRecord = (record) => {
  const decoder = new TextDecoder(record.encoding ?? 'utf-8');
  switch (record.recordType) {
    case 'text':
      return `Text (${record.lang}): ${decoder.decode(record.data)}`;
    case 'url':
    case 'absolute-url':
      return `URL: ${decoder.decode(record.data)}`;
    case 'mime':
      return `MIME ${record.mediaType}: ${record.data.byteLength} Bytes`;
    default:
      return `Unbekannter Typ: ${record.recordType}`;
  }
};

document.querySelector('#scan').onclick = async () => {
  await reader.scan();
  reader.onreading = (event) => {
    event.message.records.map(decodeRecord).forEach((line) => console.log(line));
  };
};
Text (de): Regal 12, Fach B URL: https://example.com/artikel/4711

Nur ein Tag lesen, mit Zeitlimit per AbortController

const readSingleTag = (timeoutMs = 10000) => {
  const controller = new AbortController();
  const reader = new NDEFReader();

  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      controller.abort();
      reject(new Error('Zeitüberschreitung: kein Tag gelesen.'));
    }, timeoutMs);

    reader.onreading = (event) => {
      clearTimeout(timer);
      controller.abort(); // Leser nach dem ersten Tag abschalten
      resolve(event);
    };

    reader.scan({ signal: controller.signal }).catch((error) => {
      clearTimeout(timer);
      reject(error);
    });
  });
};

document.querySelector('#checkin').addEventListener('click', async () => {
  try {
    const { serialNumber } = await readSingleTag(5000);
    console.log(`Check-in erfolgreich für Tag ${serialNumber}`);
  } catch (error) {
    console.log(error.message);
  }
});
Check-in erfolgreich für Tag 04:a2:3b:1a:5c:6d:80

Berechtigungsstatus vorab prüfen

const startIfGranted = async () => {
  const { state } = await navigator.permissions.query({ name: 'nfc' });
  console.log(`NFC-Berechtigung: ${state}`);

  if (state === 'granted') {
    // Bereits erteilt: Scan darf auch ohne Nutzergeste starten
    const reader = new NDEFReader();
    await reader.scan();
    reader.onreading = ({ serialNumber }) => console.log(serialNumber);
  } else {
    console.log('Bitte auf „Scannen“ tippen, um NFC zu erlauben.');
  }
};

startIfGranted();
NFC-Berechtigung: prompt Bitte auf „Scannen“ tippen, um NFC zu erlauben.

// Wichtig · Fallstricke

  • Browser-Unterstützung: Web NFC ist experimentell und bislang nur in Chrome für Android (ab Version 89) sowie darauf aufbauenden Browsern verfügbar. Desktop-Browser, Firefox und Safari/iOS unterstützen die API nicht. Prüfe die Verfügbarkeit daher immer mit 'NDEFReader' in window.
  • Sicherer Kontext: Die API funktioniert nur über HTTPS (oder localhost) und nur im obersten Dokument, nicht in Cross-Origin-iframes.
  • Nutzergeste: Ist die Berechtigung noch nicht erteilt, muss scan() durch eine Nutzeraktion wie einen Klick ausgelöst werden. Sonst wird das Promise mit NotAllowedError abgelehnt.
  • Promise ≠ Leseergebnis: Das Promise wird erfüllt, sobald der Scan läuft. Die Tag-Daten kommen ausschließlich über das Event reading. Registriere die Event-Handler am besten vor oder direkt nach await scan().
  • Sichtbarkeit: NFC-Tags werden nur geliefert, solange die Seite sichtbar ist und das Display eingeschaltet ist. Wird der Tab in den Hintergrund verschoben, pausiert der Scan.
  • Mehrfacher Aufruf: Ruft man scan() auf demselben Reader erneut auf, während ein Scan aktiv ist, wird das Promise mit InvalidStateError abgelehnt. Beende den laufenden Scan vorher per AbortController.
  • Sicherheit: Inhalte von NFC-Tags sind nicht vertrauenswürdige Eingaben. Gib gelesene Texte oder URLs niemals ungeprüft per innerHTML aus und öffne URLs nicht automatisch. Sonst drohen XSS und Phishing. Nutze textContent und prüfe URLs gegen eine Positivliste.
  • Nicht-NDEF-Tags oder beschädigte Tags lösen readingerror statt reading aus.