HTML欄位移到表單外就沒送出:用form指定所屬表單

重新排版表單後,有些欄位移到側欄或固定操作區,畫面仍看得到,提交資料卻少了一項。先查欄位的所屬表單。普通欄位放在form外,不會只因為靠近某個表單就自動加入;需要跨版型放置時,可以用欄位的form屬性指定表單的唯一id。

這不是把整份網頁都當成一張表單。每個欄位有自己的所屬關係,提交時再按控制項狀態和name整理資料。本文用兩個獨立表單比較FormData,讓你先確認欄位究竟連到哪裡,再回頭調整實際版型。

用form id連接外部欄位

<form id="main">
  <input name="inside" value="A">
</form>

<input id="outside" name="outside" value="B" form="main">
<button type="submit" form="main">送出</button>

outside和送出按鈕雖然在form元素外,仍能透過form="main"連到id為main的表單。這個值是表單id,不是表單name,也不是欄位name。欄位name負責資料鍵名,form屬性則負責所屬表單,兩者不要混淆。

這種寫法可以讓版型把欄位與按鈕分放不同區域,但表單必須存在於同一份文件中,而且id唯一。它不會跨iframe去尋找另一份文件的表單,也不會因為某個遠端頁面上剛好有main就建立關聯。

先查input.form,不要只看位置

const input = document.querySelector('#outside');
console.log(input.form?.id);

input的form屬性可查看瀏覽器已判定的所屬表單。如果結果是undefined或null,要查它是否真的在某個form裡、form屬性有沒有對上存在的id。若連到另一個表單,則看是否被明確的form屬性指定了其他目標。

外部欄位即使顯示在表單正下方,也不代表它會跟著提交;反過來,一個控制項也可能因明確指定form而屬於畫面上較遠的表單。重排模板時,DOM父子關係和form owner都要核對,不能只憑CSS位置判斷。

用兩個表單比較實際資料

<form id="main">
  <input name="inside" value="A">
</form>
<form id="other"></form>
<input id="outside" name="outside" value="B">
<input id="linked" name="linked" value="C" form="main">
const main = document.querySelector('#main');
console.log([...new FormData(main).entries()]);

在Chromium154的這組獨立頁面,main的資料起初只有inside=A和linked=C。outside雖然有name和值,仍未屬於任何表單,所以沒有加入。把outside加上form="main"後,資料才多出outside=B。

接著把linked改成form="other",main就不再包含linked;other的FormData則包含linked=C。這是欄位所屬關係改變,不是資料因為空值被忽略。對多表單頁面而言,一個看似無害的form屬性就可能讓資料提交到另一段流程。

fieldset的form不會傳給子欄位

<fieldset form="main">
  <input name="nested" value="D">
</fieldset>

這段fieldset在main外,給它form="main"不會讓裡面的input自動繼承所屬表單。在本例中nested的input.form仍為null,main的FormData也沒有nested。若你要讓這個外部input參與main提交,應在input本身設定form="main"。

表單不是靠一個任意外層區塊就能把資料「整包掛上去」。像把section或div加form屬性,也不能當成欄位關聯機制。應使用支援form關聯的控制項,逐一確認實際owner,再用提交資料檢查結果。

如果子欄位本來就在main內,則可以透過所在的form取得關聯;這與外部fieldset不繼承是兩回事。範例故意把fieldset放在form外,讓差異清楚。排查時要保留你實際的DOM結構,不要把欄位臨時移回form裡測通,就以為外部版型也已經修好。

有所屬表單,仍要符合提交條件

正確form owner不保證每個欄位都會出現在FormData。例如沒有name的欄位不會以你期待的鍵名提交,disabled控制項通常也不會加入;核取方塊還要看是否勾選。這些是資料收集的規則,應在所屬關係確認後再逐項排查。

required與FormData也有不同用途。new FormData整理可提交欄位,並不會替你完成整張表單的交互驗證。如果你用JavaScript自定義提交,還要確認使用者真正的送出路徑是否檢查有效性,不要只看到資料物件建立成功就判定所有欄位都合格。

後端更不能因為欄位帶有form屬性就相信它來自正確介面。使用者可以自行組織請求,所以權限、必填、允許值與資料型別仍須在後端驗證。form屬性解決的是瀏覽器裡控制項的歸屬,不能代替安全或業務規則。

避免用巢狀form包住版型

HTML不允許把form當成一般容器反覆巢狀。若為方便排版把一張form塞進另一張,瀏覽器解析出的結構可能與原始碼想像不同,控制項歸屬也容易混亂。把佈局區塊改用合適容器,表單本身保持清楚獨立,再用form屬性連接外部控制項。

元件式版型尤其要看最終DOM。父元件可能已有form,子元件又包了一張,開發者只看各自檔案時不易發現。用瀏覽器檢查實際元素樹與input.form,能看出組合後究竟是哪個表單負責欄位。

不要用JavaScript提交時遍歷全頁所有input來補漏。多表單頁面可能混入搜尋、登入與聯絡欄位,全部收集反而會把無關資料送出去。先修正每份表單的關聯,再使用那張表單的資料,範圍會比較清楚。

重排後核對兩份資料各自的內容

外部送出按鈕也要一起核對。按鈕的form指到哪一張表單,點擊時就會走那張表單的提交流程;如果版型複製後只改欄位卻漏改按鈕,畫面可能顯示一份資料,實際送出的卻是另一份。使用明確的type="submit"與form值,讓後來維護的人看得懂它的作用。

若同一份表單有「儲存草稿」與「正式提交」兩個按鈕,還要看按鈕是否使用不同的提交覆寫設定。這不是把欄位掛到表單後就自然解決的問題。先維持你原本的功能約定,再分別點擊兩個入口觀察請求;不要為了版型方便,把原本不同的操作合成一個含糊的送出事件。

對多步驟表單而言,藏起來的欄位仍可能屬於同一張表單。你應先決定每一步哪些資料可以保留、哪些要停用或重設,再核對提交結果。form owner只說明歸屬,不會按畫面是否可見替你決定哪些資料應該保留。把這些規則留在表單邏輯裡,後續改版較不容易誤刪使用者已填的內容。

最後檢查label也跟著新id正確關聯。為了外部欄位新增id時,別用另一張表單已使用的識別字;重複id不僅影響form目標,也會影響標籤和描述定位。用清楚的元件命名,加上實際DOM核對,能減少重排後多個小問題互相掩蓋的情況。

修改前先記下每個表單應包含哪些欄位,修改後分別建立FormData比較鍵名和值。外部欄位加入正確表單,另一張表單也不應因此多收到它。使用合成資料,在測試頁驗證成功與缺欄位情況,再到自己的測試站看實際請求。

如果表單或欄位會動態生成,還要測加入、移除與換id後的狀態。明確form屬性會依可用目標重新判定關聯,不能只在第一次載入測一次。標籤、欄位、提交按鈕與後端資料約定保持一致,重排版型才不會變成一次難追的資料遺失。

參考資料

原文鏈接:https://wntheme.com/html-form-attribute-outside-control/,轉載請註明出處。
0

評論0

顯示驗證碼
沒有帳號?註冊  忘記密碼?