Signatur
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
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}`);
}
});
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));
};
};
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);
}
});
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();
// 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 mitNotAllowedErrorabgelehnt. - 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 nachawait 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 mitInvalidStateErrorabgelehnt. Beende den laufenden Scan vorher perAbortController. - Sicherheit: Inhalte von NFC-Tags sind nicht vertrauenswürdige Eingaben. Gib gelesene Texte oder URLs niemals ungeprüft per
innerHTMLaus und öffne URLs nicht automatisch. Sonst drohen XSS und Phishing. NutzetextContentund prüfe URLs gegen eine Positivliste. - Nicht-NDEF-Tags oder beschädigte Tags lösen
readingerrorstattreadingaus.