現在可以讀取及寫入 NFC 標記。
什麼是 Web NFC?
NFC 是近距離無線通訊技術,運作頻率為 13.56 MHz,可讓裝置在 10 公分以內的距離通訊,傳輸速率最高可達 424 kbit/s。
Web NFC 可讓網站在靠近使用者裝置時 (通常為 5 到 10 公分,2 到 4 英吋),讀取及寫入 NFC 標籤。目前範圍僅限於 NFC 資料交換格式 (NDEF),這是一種輕量型二進位訊息格式,適用於不同標記格式。

建議用途
Web NFC 僅限於 NDEF,因為讀取和寫入 NDEF 資料的安全性屬性更容易量化。不支援低階 I/O 作業 (例如 ISO-DEP、NFC-A/B、NFC-F)、對等通訊模式和主機型卡片模擬 (HCE)。
可能使用 Web NFC 的網站範例包括:
- 博物館和美術館可讓使用者將裝置輕觸展覽附近的 NFC 卡片,顯示展覽的額外資訊。
- 商品目錄管理網站可以讀取或寫入容器 NFC 標記的資料,以更新內容資訊。
- 會議主辦單位可在活動期間掃描 NFC 徽章,並確保徽章已鎖定,避免徽章上的資訊遭到變更。
- 網站可使用這項功能分享裝置或服務佈建情境所需的初始密碼,以及在運作模式中部署設定資料。

目前狀態
步驟 | 狀態 |
---|---|
1. 建立說明 | 完成 |
2. 草擬規格初稿 | 完成 |
3. 收集意見回饋並反覆修正設計 | 完成 |
4. 來源試用 | 完成 |
5. 啟動 | 完成 |
使用 Web NFC
特徵偵測
硬體的功能偵測方式與您習慣的方式可能不同。
NDEFReader
表示瀏覽器支援 Web NFC,但無法判斷是否具備必要硬體。特別是如果缺少硬體,特定呼叫傳回的 Promise 會遭到拒絕。我會在說明 NDEFReader
時提供詳細資料。
if ('NDEFReader' in window) { /* Scan and write NFC tags */ }
術語
NFC 標記是被動式 NFC 裝置,也就是說,當主動式 NFC 裝置 (例如手機) 靠近時,會透過磁感應供電。NFC 標籤有許多形式和樣式,例如貼紙、信用卡、手環等。

NDEFReader
物件是 Web NFC 的進入點,可公開功能,以便準備讀取和/或寫入動作,並在 NDEF 標記靠近時完成動作。NDEFReader
中的 NDEF
代表 NFC 資料交換格式,這是 NFC 論壇標準化的輕量型二進位訊息格式。
NDEFReader
物件用於處理來自 NFC 標記的 NDEF 訊息,以及將 NDEF 訊息寫入範圍內的 NFC 標記。
支援 NDEF 的 NFC 標記就像便利貼,任何人都能讀取,除非是唯讀狀態,否則任何人都能寫入。其中包含單一 NDEF 訊息,封裝了一或多個 NDEF 記錄。每個 NDEF 記錄都是二進位結構,內含資料酬載和相關聯的類型資訊。Web NFC 支援下列 NFC 論壇標準化記錄類型:空白、文字、網址、智慧海報、MIME 類型、絕對網址、外部類型、不明和本機類型。

掃描 NFC 標記
如要掃描 NFC 標記,請先例項化新的 NDEFReader
物件。呼叫 scan()
會傳回 promise。如果先前未授予存取權,系統可能會提示使用者。如果符合下列所有條件,承諾就會解決:
- 只有在使用者做出手勢 (例如觸控手勢) 或點按滑鼠時,才會呼叫這個函式。
- 使用者已允許網站與 NFC 裝置互動。
- 使用者的手機支援 NFC。
- 使用者已在手機上啟用 NFC。
Promise 解決後,只要透過事件監聽器訂閱 reading
事件,即可接收 NDEF 訊息。此外,您也應訂閱 readingerror
事件,以便在附近出現不相容的 NFC 標記時收到通知。
const ndef = new NDEFReader();
ndef.scan().then(() => {
console.log("Scan started successfully.");
ndef.onreadingerror = () => {
console.log("Cannot read data from the NFC tag. Try another one?");
};
ndef.onreading = event => {
console.log("NDEF message read.");
};
}).catch(error => {
console.log(`Error! Scan failed to start: ${error}.`);
});
當 NFC 標籤靠近時,系統會觸發 NDEFReadingEvent
事件。當中包含兩個專屬屬性:
serialNumber
代表裝置的序號 (例如 00-11-22-33-44-55-66),如果沒有序號,則為空字串。message
代表儲存在 NFC 標記中的 NDEF 訊息。
如要讀取 NDEF 訊息的內容,請逐一檢查 message.records
,並根據 recordType
適當處理其 data
成員。data
成員會公開為 DataView
,因為這樣可以處理以 UTF-16 編碼的資料。
ndef.onreading = event => {
const message = event.message;
for (const record of message.records) {
console.log("Record type: " + record.recordType);
console.log("MIME type: " + record.mediaType);
console.log("Record id: " + record.id);
switch (record.recordType) {
case "text":
// TODO: Read text record with record data, lang, and encoding.
break;
case "url":
// TODO: Read URL record with record data.
break;
default:
// TODO: Handle other records with record data.
}
}
};
寫入 NFC 標記
如要寫入 NFC 標記,請先例項化新的 NDEFReader
物件。呼叫 write()
會傳回 promise。如果先前未授予存取權,系統可能會提示使用者。此時,NDEF 訊息會「準備就緒」,且如果符合下列所有條件,承諾就會解決:
- 只有在使用者做出手勢 (例如觸控手勢) 或點按滑鼠時,才會呼叫這個函式。
- 使用者已允許網站與 NFC 裝置互動。
- 使用者的手機支援 NFC。
- 使用者已在手機上啟用 NFC。
- 使用者已輕觸 NFC 標籤,且 NDEF 訊息已成功寫入。
如要將文字寫入 NFC 標記,請將字串傳遞至 write()
方法。
const ndef = new NDEFReader();
ndef.write(
"Hello World"
).then(() => {
console.log("Message written.");
}).catch(error => {
console.log(`Write failed :-( try again: ${error}.`);
});
如要將網址記錄寫入 NFC 標記,請將代表 NDEF 訊息的字典傳遞至 write()
。在下列範例中,NDEF 訊息是具有 records
鍵的字典。其值為記錄陣列,在本例中,是定義為物件的網址記錄,其中 recordType
鍵設為 "url"
,data
鍵設為網址字串。
const ndef = new NDEFReader();
ndef.write({
records: [{ recordType: "url", data: "https://w3c.github.io/web-nfc/" }]
}).then(() => {
console.log("Message written.");
}).catch(error => {
console.log(`Write failed :-( try again: ${error}.`);
});
您也可以將多筆記錄寫入 NFC 標記。
const ndef = new NDEFReader();
ndef.write({ records: [
{ recordType: "url", data: "https://w3c.github.io/web-nfc/" },
{ recordType: "url", data: "https://web.dev/nfc/" }
]}).then(() => {
console.log("Message written.");
}).catch(error => {
console.log(`Write failed :-( try again: ${error}.`);
});
如果 NFC 標記包含不應覆寫的 NDEF 訊息,請在傳遞至 write()
方法的選項中,將 overwrite
屬性設為 false
。在這種情況下,如果 NFC 標記中已儲存 NDEF 訊息,傳回的 Promise 會遭到拒絕。
const ndef = new NDEFReader();
ndef.write("Writing data on an empty NFC tag is fun!", { overwrite: false })
.then(() => {
console.log("Message written.");
}).catch(error => {
console.log(`Write failed :-( try again: ${error}.`);
});
將 NFC 標記設為唯讀
為避免惡意使用者覆寫 NFC 標記的內容,您可以將 NFC 標記設為永久唯讀。這項操作無法復原。NFC 標記設為唯讀後,就無法再寫入資料。
如要將 NFC 標記設為唯讀,請先例項化新的 NDEFReader
物件。呼叫 makeReadOnly()
會傳回 promise。如果先前未授予存取權,系統可能會提示使用者。如果符合下列所有條件,這個 Promise 就會解決:
- 只有在使用者做出手勢 (例如觸控手勢) 或點按滑鼠時,才會呼叫這個函式。
- 使用者已允許網站與 NFC 裝置互動。
- 使用者的手機支援 NFC。
- 使用者已在手機上啟用 NFC。
- 使用者已輕觸 NFC 標記,且 NFC 標記已成功設為唯讀。
const ndef = new NDEFReader();
ndef.makeReadOnly()
.then(() => {
console.log("NFC tag has been made permanently read-only.");
}).catch(error => {
console.log(`Operation failed: ${error}`);
});
以下說明如何在寫入 NFC 標記後,將其設為永久唯讀。
const ndef = new NDEFReader();
try {
await ndef.write("Hello world");
console.log("Message written.");
await ndef.makeReadOnly();
console.log("NFC tag has been made permanently read-only after writing to it.");
} catch (error) {
console.log(`Operation failed: ${error}`);
}
Chrome 100 以上版本已在 Android 裝置上推出 makeReadOnly()
,請按照下列步驟確認是否支援這項功能:
if ("NDEFReader" in window && "makeReadOnly" in NDEFReader.prototype) {
// makeReadOnly() is supported.
}
安全性和權限
Chrome 團隊設計及實作 Web NFC 時,遵循了控管強大網頁平台功能存取權中定義的核心原則,包括使用者控制權、資訊公開和人體工學。
由於 NFC 會擴大惡意網站可能取得的資訊範圍,因此系統會限制 NFC 的可用性,盡可能讓使用者瞭解並控管 NFC 的使用情形。

Web NFC 僅適用於頂層框架和安全瀏覽環境 (僅限 HTTPS)。來源必須先在處理使用者手勢 (例如點選按鈕) 時要求 "nfc"
permission。如果先前未授予存取權,NDEFReader
、scan()
、write()
和 makeReadOnly()
方法會觸發使用者提示。
document.querySelector("#scanButton").onclick = async () => {
const ndef = new NDEFReader();
// Prompt user to allow website to interact with NFC devices.
await ndef.scan();
ndef.onreading = event => {
// TODO: Handle incoming NDEF messages.
};
};
使用者啟動權限提示,並在現實世界中將裝置移至目標 NFC 標籤上方,這兩者結合起來,就與其他檔案和裝置存取 API 中的選擇器模式相符。
如要執行掃描或寫入作業,使用者以裝置觸碰 NFC 標籤時,網頁必須顯示在畫面上。瀏覽器會透過觸覺回饋來表示輕觸。如果螢幕關閉或裝置已鎖定,系統會封鎖 NFC 無線電存取權。對於非可見網頁,系統會暫停接收及推送 NFC 內容,並在網頁再次顯示時恢復。
有了 Page Visibility API,就能追蹤文件瀏覽權限的變更時間。
document.onvisibilitychange = event => {
if (document.hidden) {
// All NFC operations are automatically suspended when document is hidden.
} else {
// All NFC operations are resumed, if needed.
}
};
食譜集
以下提供程式碼範例,協助您快速上手。
檢查權限
Permissions API 可檢查是否已授予 "nfc"
權限。這個範例說明如何掃描 NFC 標籤 (如果先前已授予存取權,則不需要使用者互動),或顯示按鈕 (如果先前未授予存取權)。請注意,寫入 NFC 標記的機制相同,因為這項作業在幕後使用相同的權限。
const ndef = new NDEFReader();
async function startScanning() {
await ndef.scan();
ndef.onreading = event => {
/* handle NDEF messages */
};
}
const nfcPermissionStatus = await navigator.permissions.query({ name: "nfc" });
if (nfcPermissionStatus.state === "granted") {
// NFC access was previously granted, so we can start NFC scanning now.
startScanning();
} else {
// Show a "scan" button.
document.querySelector("#scanButton").style.display = "block";
document.querySelector("#scanButton").onclick = event => {
// Prompt user to allow UA to send and receive info when they tap NFC devices.
startScanning();
};
}
中止 NFC 作業
使用 AbortController
原始型別可輕鬆中止 NFC 作業。以下範例說明如何透過 NDEFReader scan()
、makeReadOnly()
、write()
方法的選項傳遞 AbortController
的 signal
,並同時中止兩項 NFC 作業。
const abortController = new AbortController();
abortController.signal.onabort = event => {
// All NFC operations have been aborted.
};
const ndef = new NDEFReader();
await ndef.scan({ signal: abortController.signal });
await ndef.write("Hello world", { signal: abortController.signal });
await ndef.makeReadOnly({ signal: abortController.signal });
document.querySelector("#abortButton").onclick = event => {
abortController.abort();
};
寫入後讀取
使用 write()
,然後搭配 AbortController
基本體使用 scan()
,即可在將訊息寫入 NFC 標記後讀取該標記。以下範例說明如何將訊息寫入 NFC 標記,並讀取 NFC 標記中的新訊息。掃描作業會在三秒後停止。
// Waiting for user to tap NFC tag to write to it...
const ndef = new NDEFReader();
await ndef.write("Hello world");
// Success! Message has been written.
// Now scanning for 3 seconds...
const abortController = new AbortController();
await ndef.scan({ signal: abortController.signal });
const message = await new Promise((resolve) => {
ndef.onreading = (event) => resolve(event.message);
});
// Success! Message has been read.
await new Promise((r) => setTimeout(r, 3000));
abortController.abort();
// Scanning is now stopped.
讀取及寫入文字記錄
文字記錄 data
可使用以記錄 encoding
屬性例項化的 TextDecoder
解碼。請注意,文字記錄的語言可透過 lang
屬性取得。
function readTextRecord(record) {
console.assert(record.recordType === "text");
const textDecoder = new TextDecoder(record.encoding);
console.log(`Text: ${textDecoder.decode(record.data)} (${record.lang})`);
}
如要寫入簡單的文字記錄,請將字串傳遞至 NDEFReader write()
方法。
const ndef = new NDEFReader();
await ndef.write("Hello World");
文字記錄預設為 UTF-8,並採用目前文件的語言,但可以使用完整語法建立自訂 NDEF 記錄,指定這兩項屬性 (encoding
和 lang
)。
function a2utf16(string) {
let result = new Uint16Array(string.length);
for (let i = 0; i < string.length; i++) {
result[i] = string.codePointAt(i);
}
return result;
}
const textRecord = {
recordType: "text",
lang: "fr",
encoding: "utf-16",
data: a2utf16("Bonjour, François !")
};
const ndef = new NDEFReader();
await ndef.write({ records: [textRecord] });
讀取及寫入網址記錄
使用 TextDecoder
解碼記錄的 data
。
function readUrlRecord(record) {
console.assert(record.recordType === "url");
const textDecoder = new TextDecoder();
console.log(`URL: ${textDecoder.decode(record.data)}`);
}
如要寫入網址記錄,請將 NDEF 訊息字典傳遞至 NDEFReader write()
方法。NDEF 訊息中包含的網址記錄定義為物件,其中 recordType
鍵設為 "url"
,而 data
鍵則設為網址字串。
const urlRecord = {
recordType: "url",
data:"https://w3c.github.io/web-nfc/"
};
const ndef = new NDEFReader();
await ndef.write({ records: [urlRecord] });
讀取及寫入 MIME 類型記錄
MIME 類型記錄的 mediaType
屬性代表 NDEF 記錄酬載的 MIME 類型,因此 data
可以正確解碼。舉例來說,您可以使用 JSON.parse
解碼 JSON 文字,並使用 Image 元素解碼圖片資料。
function readMimeRecord(record) {
console.assert(record.recordType === "mime");
if (record.mediaType === "application/json") {
const textDecoder = new TextDecoder();
console.log(`JSON: ${JSON.parse(decoder.decode(record.data))}`);
}
else if (record.mediaType.startsWith('image/')) {
const blob = new Blob([record.data], { type: record.mediaType });
const img = new Image();
img.src = URL.createObjectURL(blob);
document.body.appendChild(img);
}
else {
// TODO: Handle other MIME types.
}
}
如要寫入 MIME 類型記錄,請將 NDEF 訊息字典傳遞至 NDEFReader write()
方法。NDEF 訊息中包含的 MIME 類型記錄定義為物件,其中 recordType
鍵設為 "mime"
,mediaType
鍵設為內容的實際 MIME 類型,data
鍵設為可為 ArrayBuffer
或提供 ArrayBuffer
檢視畫面的物件 (例如 Uint8Array
、DataView
)。
const encoder = new TextEncoder();
const data = {
firstname: "François",
lastname: "Beaufort"
};
const jsonRecord = {
recordType: "mime",
mediaType: "application/json",
data: encoder.encode(JSON.stringify(data))
};
const imageRecord = {
recordType: "mime",
mediaType: "image/png",
data: await (await fetch("icon1.png")).arrayBuffer()
};
const ndef = new NDEFReader();
await ndef.write({ records: [jsonRecord, imageRecord] });
讀取及寫入絕對網址記錄
絕對網址記錄 data
可透過簡單的 TextDecoder
解碼。
function readAbsoluteUrlRecord(record) {
console.assert(record.recordType === "absolute-url");
const textDecoder = new TextDecoder();
console.log(`Absolute URL: ${textDecoder.decode(record.data)}`);
}
如要寫入絕對網址記錄,請將 NDEF 訊息字典傳遞至 NDEFReader write()
方法。NDEF 訊息中包含的絕對網址記錄定義為物件,其中 recordType
鍵設為 "absolute-url"
,data
鍵則設為網址字串。
const absoluteUrlRecord = {
recordType: "absolute-url",
data:"https://w3c.github.io/web-nfc/"
};
const ndef = new NDEFReader();
await ndef.write({ records: [absoluteUrlRecord] });
讀取及寫入智慧海報記錄
智慧海報記錄 (用於雜誌廣告、傳單、看板等) 會將某些網頁內容描述為 NDEF 記錄,其中包含 NDEF 訊息做為酬載。呼叫 record.toRecords()
,將 data
轉換為智慧型海報記錄中包含的記錄清單。其中應包含網址記錄、標題的文字記錄、圖片的 MIME 類型記錄,以及一些自訂本機類型記錄,例如分別對應智慧海報記錄類型、動作和大小的 ":t"
、":act"
和 ":s"
。
本機類型記錄僅在所含 NDEF 記錄的本機環境中是唯一的。如果型別的意義在所含記錄的本機環境以外並不重要,且儲存空間用量受到嚴格限制,請使用這些型別。Web NFC 中的本機類型記錄名稱一律以 :
開頭 (例如 ":t"
、":s"
、":act"
)。這是為了區分文字記錄和本機類型文字記錄。
function readSmartPosterRecord(smartPosterRecord) {
console.assert(record.recordType === "smart-poster");
let action, text, url;
for (const record of smartPosterRecord.toRecords()) {
if (record.recordType == "text") {
const decoder = new TextDecoder(record.encoding);
text = decoder.decode(record.data);
} else if (record.recordType == "url") {
const decoder = new TextDecoder();
url = decoder.decode(record.data);
} else if (record.recordType == ":act") {
action = record.data.getUint8(0);
} else {
// TODO: Handle other type of records such as `:t`, `:s`.
}
}
switch (action) {
case 0:
// Do the action
break;
case 1:
// Save for later
break;
case 2:
// Open for editing
break;
}
}
如要寫入智慧海報記錄,請將 NDEF 訊息傳遞至 NDEFReader write()
方法。NDEF 訊息中包含的智慧海報記錄定義為物件,其中 recordType
鍵設為 "smart-poster"
,而 data
鍵則設為物件,代表智慧海報記錄中包含的 NDEF 訊息 (再次)。
const encoder = new TextEncoder();
const smartPosterRecord = {
recordType: "smart-poster",
data: {
records: [
{
recordType: "url", // URL record for smart poster content
data: "https://my.org/content/19911"
},
{
recordType: "text", // title record for smart poster content
data: "Funny dance"
},
{
recordType: ":t", // type record, a local type to smart poster
data: encoder.encode("image/gif") // MIME type of smart poster content
},
{
recordType: ":s", // size record, a local type to smart poster
data: new Uint32Array([4096]) // byte size of smart poster content
},
{
recordType: ":act", // action record, a local type to smart poster
// do the action, in this case open in the browser
data: new Uint8Array([0])
},
{
recordType: "mime", // icon record, a MIME type record
mediaType: "image/png",
data: await (await fetch("icon1.png")).arrayBuffer()
},
{
recordType: "mime", // another icon record
mediaType: "image/jpg",
data: await (await fetch("icon2.jpg")).arrayBuffer()
}
]
}
};
const ndef = new NDEFReader();
await ndef.write({ records: [smartPosterRecord] });
讀取及寫入外部型別記錄
如要建立應用程式定義的記錄,請使用外部類型記錄。這些記錄可能包含可透過 toRecords()
存取的 NDEF 訊息做為酬載。名稱包含發行機構的網域名稱、半形冒號,以及至少一個字元的型別名稱,例如 "example.com:foo"
。
function readExternalTypeRecord(externalTypeRecord) {
for (const record of externalTypeRecord.toRecords()) {
if (record.recordType == "text") {
const decoder = new TextDecoder(record.encoding);
console.log(`Text: ${textDecoder.decode(record.data)} (${record.lang})`);
} else if (record.recordType == "url") {
const decoder = new TextDecoder();
console.log(`URL: ${decoder.decode(record.data)}`);
} else {
// TODO: Handle other type of records.
}
}
}
如要寫入外部型別記錄,請將 NDEF 訊息字典傳遞至 NDEFReader write()
方法。NDEF 訊息中包含的外部型別記錄定義為物件,其中 recordType
鍵設為外部型別的名稱,而 data
鍵則設為代表外部型別記錄中 NDEF 訊息的物件。請注意,data
鍵也可以是 ArrayBuffer
,或提供 ArrayBuffer
的檢視畫面 (例如 Uint8Array
、DataView
)。
const externalTypeRecord = {
recordType: "example.game:a",
data: {
records: [
{
recordType: "url",
data: "https://example.game/42"
},
{
recordType: "text",
data: "Game context given here"
},
{
recordType: "mime",
mediaType: "image/png",
data: await (await fetch("image.png")).arrayBuffer()
}
]
}
};
const ndef = new NDEFReader();
ndef.write({ records: [externalTypeRecord] });
讀取及寫入空白記錄
空白記錄沒有酬載。
如要寫入空白記錄,請將 NDEF 訊息字典傳遞至 NDEFReader
write()
方法。NDEF 訊息中包含的空白記錄定義為 recordType
鍵設為 "empty"
的物件。
const emptyRecord = {
recordType: "empty"
};
const ndef = new NDEFReader();
await ndef.write({ records: [emptyRecord] });
瀏覽器支援
Chrome 89 版的 Android 裝置支援 Web NFC。
開發提示
以下是我開始使用 Web NFC 時,希望自己能瞭解的事項:
- Android 會在 Web NFC 運作前,在 OS 層級處理 NFC 標記。
- 您可以在 material.io 找到 NFC 圖示。
- 使用 NDEF 記錄
id
,以便在需要時輕鬆識別記錄。 - 支援 NDEF 的未格式化 NFC 標記包含單一空白類型記錄。
- 如下所示,編寫 Android 應用程式記錄很簡單。
const encoder = new TextEncoder();
const aarRecord = {
recordType: "android.com:pkg",
data: encoder.encode("com.example.myapp")
};
const ndef = new NDEFReader();
await ndef.write({ records: [aarRecord] });
示範
請試用官方範例,並查看一些有趣的 Web NFC 示範:
意見回饋
Web NFC 社群群組和 Chrome 團隊很想聽聽您對 Web NFC 的想法和使用體驗。
介紹 API 設計
API 是否有任何異常狀況?還是缺少實作構想所需的方法或屬性?
在 Web NFC GitHub 存放區中提出規格問題,或在現有問題中新增想法。
回報導入問題
您是否發現 Chrome 實作方式有錯誤?還是實作方式與規格不同?
前往 https://new.crbug.com 回報錯誤。請務必盡可能提供詳細資料,並提供重現錯誤的簡單操作說明,且將「Components」設為 Blink>NFC
。
顯示支援
您是否打算使用 Web NFC?您的公開支持有助於 Chrome 團隊優先處理功能,並向其他瀏覽器供應商展現支援這些功能的重要性。
使用主題標記 #WebNFC
傳送推文給 @ChromiumDev,告訴我們您在何處使用這項功能,以及使用方式。
實用連結
- 規格
- Web NFC 示範
- 追蹤錯誤
- ChromeStatus.com 項目
- Blink 元件:
Blink>NFC
特別銘謝
非常感謝 Intel 團隊實作 Web NFC。Google Chrome 仰賴提交者社群的共同努力,推動 Chromium 專案。並非所有 Chromium 提交者都是 Google 員工,這些貢獻者值得特別表揚!