透過 JavaScript 傳送表單

當用戶提交 HTML 表單時,例如透過點選 提交按鈕,瀏覽器會向指定的 HTTP 請求傳送表單中的資料。但與這種宣告式的方法不同,Web 應用有時會使用 JavaScript API,如 fetch(),以程式設計方式將資料傳送到期望表單提交的端點。本文將解釋為什麼這是一個重要的用例以及如何實現它。

為什麼使用 JavaScript 提交表單資料?

正如我們在關於 傳送表單資料 的文章中所述,標準的 HTML 表單提交會載入資料傳送到的 URL,這意味著瀏覽器視窗會進行整頁重新整理導航。

然而,許多 Web 應用,特別是 漸進式 Web 應用單頁應用,使用 JavaScript API 從伺服器請求資料並更新頁面上相關部分,從而避免了整頁重新整理的開銷。

因此,當這些 Web 應用想要提交表單資料時,它們僅使用 HTML 表單來收集使用者輸入,而不是用於資料提交。當用戶嘗試傳送資料時,應用程式會接管控制權,並使用 JavaScript API(如 fetch())傳送資料。

JavaScript 表單提交的問題

如果 Web 應用傳送表單資料的伺服器端點在 Web 應用開發者的控制之下,那麼他們可以按任意方式傳送表單資料:例如,作為 JSON 物件。

但是,如果伺服器端點期望表單提交,Web 應用必須以特定方式編碼資料。例如,如果資料只是文字,它將由 URL 編碼的鍵/值對列表組成,並以 application/x-www-form-urlencodedContent-Type 傳送。如果表單包含二進位制資料,則必須使用 multipart/form-data 內容型別傳送。

FormData 介面負責以這種方式編碼資料的過程,在本文的其餘部分,我們將快速介紹 FormData。有關更多詳細資訊,請參閱我們關於 使用 FormData 物件 的指南。

手動構建 FormData 物件

您可以透過為要新增的每個欄位呼叫物件的 append() 方法來填充 FormData 物件,傳入欄位的名稱和值。值可以是字串(用於文字欄位)或 Blob(用於二進位制欄位,包括 File 物件)。

在下面的示例中,當用戶點選按鈕時,我們將資料作為表單提交發送

js
async function sendData(data) {
  // Construct a FormData instance
  const formData = new FormData();

  // Add a text field
  formData.append("name", "Pomegranate");

  // Add a file
  const selection = await window.showOpenFilePicker();
  if (selection.length > 0) {
    const file = await selection[0].getFile();
    formData.append("file", file);
  }

  try {
    const response = await fetch("https://example.org/post", {
      method: "POST",
      // Set the FormData instance as the request body
      body: formData,
    });
    console.log(await response.json());
  } catch (e) {
    console.error(e);
  }
}

const send = document.querySelector("#send");
send.addEventListener("click", sendData);
  1. 我們首先構造一個新、空的 FormData 物件。

  2. 接下來,我們呼叫兩次 append(),將兩個專案新增到 FormData 物件中:一個文字欄位和一個檔案。

  3. 最後,我們使用 fetch() API 發起一個 POST 請求,將 FormData 物件設定為請求體。

請注意,我們無需設定 Content-Type 標頭:當我們向 fetch() 傳遞 FormData 物件時,會自動設定正確的標頭。

關聯 FormData 物件和 <form>

如果您的資料實際上來自一個 <form>,您可以透過將表單傳遞給 FormData 建構函式來填充 FormData 例項。

假設我們的 HTML 聲明瞭一個 <form> 元素

html
<form id="userinfo">
  <p>
    <label for="username">Enter your name:</label>
    <input type="text" id="username" name="username" value="Dominic" />
  </p>
  <p>
    <label for="avatar">Select an avatar</label>
    <input type="file" id="avatar" name="avatar" required />
  </p>
  <input type="submit" value="Submit" />
</form>

該表單包含一個文字輸入框、一個檔案輸入框和一個提交按鈕。

JavaScript 程式碼如下

js
const form = document.querySelector("#userinfo");

async function sendData() {
  // Associate the FormData object with the form element
  const formData = new FormData(form);

  try {
    const response = await fetch("https://example.org/post", {
      method: "POST",
      // Set the FormData instance as the request body
      body: formData,
    });
    console.log(await response.json());
  } catch (e) {
    console.error(e);
  }
}

// Take over form submission
form.addEventListener("submit", (event) => {
  event.preventDefault();
  sendData();
});

我們為表單元素添加了一個提交事件處理程式。它首先呼叫 preventDefault() 來阻止瀏覽器內建的表單提交,以便我們接管。然後我們呼叫 sendData(),它會檢索表單元素並將其傳遞給 FormData 建構函式。

之後,我們使用 fetch()FormData 例項作為 HTTP POST 請求傳送。