表單操作

2018-07-10 15:32 更新

表單操作

表單為頁(yè)面的主要組成部分,其中包含許多的表單控件。用戶通過(guò)控件提供數(shù)據(jù)并提交給服務(wù)器,服務(wù)器則做出相應(yīng)的處理。而編寫一個(gè)正常工作的表單需要三個(gè)部分:

  1. 構(gòu)建表單
  2. 服務(wù)器處理(提供接受數(shù)據(jù)接口)
  3. 配置表單

構(gòu)建表單

<form>
  <p><label>姓名:<input></label></p>
  <p><label>電話:<input type="tel"></label></p>
  <p><label>郵箱:<input type="email"></label></p>
  <fieldset>
    <legend> 披薩大小 </legend>
    <label><input type="radio" name="size"> 小 </label>
    <label><input type="radio" name="size"> 中 </label>
    <label><input type="radio" name="size"> 大 </label>
  </fieldset>
  <fieldset>
    <legend> 披薩配料 </legend>
    <label><input type="checkbox"> 熏肉 </input></label>
    <label><input type="checkbox"> 奶酪 </input></label>
    <label><input type="checkbox"> 洋蔥 </input></label>
    <label><input type="checkbox"> 蘑菇 </input></label>
  </fieldset>
  <p><label>配送時(shí)間:<input type="time" min="11:00" max="2100" step="900"></label></p>
  <p><button>提交訂單</button></p>
</form>

服務(wù)器處理

提供接口地址(例如,https://pizza.example.com/order,數(shù)據(jù)格式(application/x-www-form-urlencoded),還是接受的參數(shù)信息(custname、custtel、custemail、size、topping、delivery)。

數(shù)據(jù)命名需在表單控件中注明。

配置表單

<form action="https://pizza.example.com/order" method="post" enctype="application/x-www-form-urlencoded">
  <p><label>姓名:<input name="custname"></label></p>
  <p><label>電話:<input type="tel" name="custtel"></label></p>
  <p><label>郵箱:<input type="email" name="custemail"></label></p>
  <fieldset>
    <legend> 披薩大小 </legend>
    <label><input type="radio" name="size" value="small"> 小 </label>
    <label><input type="radio" name="size" value="medium"> 中 </label>
    <label><input type="radio" name="size" value="large"> 大 </label>
  </fieldset>
  <fieldset>
    <legend> 披薩配料 </legend>
    <label><input type="checkbox"> 熏肉 </input></label>
    <label><input type="checkbox"> 奶酪 </input></label>
    <label><input type="checkbox"> 洋蔥 </input></label>
    <label><input type="checkbox"> 蘑菇 </input></label>
  </fieldset>
  <p><label>配送時(shí)間:<input type="time" min="11:00" max="2100" step="900"></label></p>
  <p><button>提交訂單</button></p>
</form>

用戶所有提交的信息需在提交服務(wù)器前對(duì)其進(jìn)行驗(yàn)證從而提高用戶體驗(yàn)。

NOTE:表單驗(yàn)證 使用 require 來(lái)強(qiáng)制用戶填寫相應(yīng)的信息。

內(nèi)容

元素

form 元素

form 元素為構(gòu)建表單中最重要的元素。

<form novalidate name="pizza" target="abc" method="post" autocomplete="off" accept-charset="utf-8" action="http://pizza.example.com/order" enctype="application/x-www-form-urlencoded">

其對(duì)應(yīng)的信息則可以視為

字段
noValidatetrue
targetabc
methodpost
acceptCharsetutf-8
actionhttp://pizza.example.com/order
enctypeapplication/x-www-form-urlencoded
namepizza
autocompleteoff

NOTE:前六項(xiàng)為表單提交相關(guān)的信息。

屬性
  • name 屬性:可以用于獲取表單節(jié)點(diǎn)元素。
var pizzaForm = document.forms.pizza;
  • autocomplete 屬性:有兩個(gè)值 on 與 off,在設(shè)置為 on 時(shí),可以自動(dòng)對(duì)輸入框進(jìn)行補(bǔ)全(之前提交過(guò)的輸入值,下圖左)。

NOTE:在已經(jīng)設(shè)置 autocomplete="off" 時(shí)依然出現(xiàn)提示框,大多數(shù)情況為瀏覽器設(shè)置的自動(dòng)補(bǔ)全(可以強(qiáng)制關(guān)閉,需要時(shí)請(qǐng)搜索對(duì)應(yīng)的解決方案)。

  • elements 屬性:為一個(gè)動(dòng)態(tài)節(jié)點(diǎn)集合(更具 DOM 的變化進(jìn)行變化),其用于歸結(jié)該表單的子孫表單控件(除圖標(biāo)按鈕外 <input type="image>"):

    • button
    • fieldset
    • input
    • keygen
    • object
    • output
    • select
    • textarea

此外還有歸屬于該表單的空間(依舊圖片按鍵除外)代碼如下所示。

<form id="a">
</form>
<label><input name="null" form="a"></label>
  • length 屬性:等價(jià)于 elements.length 來(lái)用于描述表單內(nèi)節(jié)點(diǎn)集合的個(gè)數(shù)。
選取表單空間元素
<form name="test">
  <input name="a">
  <input name="b">
</form>

選取 name="a" 的控件可以使用下面的方法:

testForm.elements[0];
testForm.elements['a'];

// 操作 Form 表單的屬性
testForm[0];
testForm['a'];
  • form[name] 通過(guò)名稱作為索引時(shí)有如下特點(diǎn):

    • 返回 id 或者 name 為指定名稱的表單空間(圖標(biāo)按鍵除外)
    • 如果結(jié)果為空,則返回id 為指定名稱的 img 元素(入下面代碼所示)
    • 如果有多個(gè)同名元素,則返回的元素為動(dòng)態(tài)節(jié)點(diǎn)集合
    • 一旦用指定名稱取過(guò)改元素,之后則不論該元素的 id 或者 name 如何變化,只有節(jié)點(diǎn)存在則均可使用原名稱來(lái)繼續(xù)獲取改節(jié)點(diǎn)。

無(wú)指定名稱索引范例

<form name="test">
  <img id="a" src="sample.png">
</form>
testForm['a']; // 取得的便是 id 為 a 的圖片元素

更新名稱,依然可以獲取節(jié)點(diǎn)范例

<form name="test">
  <input name="a">
</form>
// 第一步
testForm['a'];
// 或者
testForm.elements['a'];

// 第二步
testForm['a'].name = 'b';

form 接口

form 元素也提供了一些接口便于對(duì)其進(jìn)行操作 reset() submit() checkValidity()。

可以重置(reset)的元素有下面的幾種:

  • input
  • keygen
  • output
  • select
  • textarea

當(dāng)觸發(fā)表單 reset 事件時(shí)可使用阻止該事件的默認(rèn)行為來(lái)取消重置。而且元素重置時(shí)將不會(huì)再次觸發(fā)元素上的 change 與 input 事件。

label 元素
<label for="textId" form="formId">
字段
htmlFortextId
controlHTMLElement#textId
formHTMLFormElement#formId
  • htmlFor 屬性:用于關(guān)聯(lián)表單控件的激活行為(可使點(diǎn)擊 label 與點(diǎn)擊表單控件的行為一致),可關(guān)聯(lián)的元素有下列(hidden 除外):

    • button
    • input
    • keygen
    • meter
    • output
    • progress
    • select
    • textarea

自定義文件提交控件樣式

  • control 屬性:如果指定了 for 屬性則指定該for 屬性對(duì)于 id 的可關(guān)聯(lián)元素。如果沒(méi)有指定 for 屬性則為第一個(gè)可關(guān)聯(lián)的子孫元素。

可關(guān)聯(lián)的元素 (只讀屬性不可在程序中直接賦值修改)

  • button
  • fieldset
  • input
  • keygen
  • label
  • object
  • output
  • select
  • textarea

- form 屬性:修改關(guān)聯(lián)元素所歸屬的表單則可以修改元素的 form 屬性為帶關(guān)聯(lián)表單Id(元素中對(duì)于的for屬性也應(yīng)該做對(duì)應(yīng)的修改)。//這里有一點(diǎn)小問(wèn)題,更改form屬性之后label并不能自動(dòng)綁定到新表單對(duì)應(yīng)的元素上

label.setAttribute('form', 'newFormId');
input 元素
<input type="text">
  • type 屬性:可用于控制控件的外觀以及數(shù)據(jù)類型(默認(rèn)為 text),在不同的瀏覽器不同數(shù)據(jù)類型有不同的展示效果。

本地圖片預(yù)覽示例

所需技術(shù)點(diǎn)(HTMLInputElement屬性)

  • onchange
  • accept
  • multiple
  • files
<input type="file" accept="image/*" multiple>
file.addEventListener(
  'change', function(event){
      var files = Array.prototype.slice.call(
        event.target.files, 0
      );
      files.forEach(function(item){
        files2dataurl(item,function(url){
          var image = new Image();
          parent.appendChild(image);
          image.src = url;
        });
      });
  }
);
function file2dataurl(file, callback) {
    if (!window.FileReader) {
        throw 'Browser not support File API !';
    }
    var reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = function(event) {
        callback(event.target.result);
    };
}

NOTE:accept 所支持的格式有 audio/* video/* image/* 以及不帶;的 MINE Type 類型和 . 開頭的文件名后綴的文件。多個(gè)文件類型可以使用,分隔。

select 元素

指定選項(xiàng)列表中選擇需要的選項(xiàng)。

主要的三個(gè)子標(biāo)簽 select、optgroup(用于選項(xiàng)分組)、option。

  • select 具有的屬性和方法如下:

    • name
    • value
    • multiple
    • options(動(dòng)態(tài)節(jié)點(diǎn)集合)
    • selectedOptions(動(dòng)態(tài)節(jié)點(diǎn)集合)
    • selectedIndex
    • add(element[, before])(無(wú)指定參照物則添加至最末端)
    • remove([index])
  • optgroup 所具有的屬性和方法:

    • disabled (分組選項(xiàng)不可選)
    • label(分組說(shuō)明)
  • option 所具有的屬性和方法:

    • disabled
    • label(描述信息)
    • value(提交表單時(shí)的數(shù)據(jù)信息)
    • text(用戶看到的文字)
    • index
    • selected
    • defaultSelected
選項(xiàng)操作

創(chuàng)建選項(xiàng)

document.createElement('option')
// 或者
new Option([text[, value[, defaultSelected[, selected]]]])

添加選項(xiàng)

var option = new Option('sample');
opt.insertAdjacentElement(option, '參照元素');
// 或者
select.add(option, '參照元素')

刪除選項(xiàng)

opt.parentNode.removeChild(option);
// 或者使用它的索引將其刪除
select.remove(2);
級(jí)聯(lián)下列選擇器

所需知識(shí)點(diǎn):

  • onchange
  • remove
  • add
<form name="course">
  <select name="chapter">
    <option>Select0</option>
  </select>
  <select name="section">
    <option>Select1</option>
  </select>
</form>
var chapters = {
  {text: 1, value: 1},
  {text: 2, value: 2}
};
var sections = {
  1: [{
    text:1.1, value: 1.1
  }, {
    text:1.2, value: 1.2
  }],
  2:[{
    text:2.1, value:2.1;
  }]
};

function fillSelect(select, list) {
  for(var i = select.length; i > 0; i--) {
    select.remove(i);
  }
  list.forEach(function(data){
    var option = new Option(data.text, data.value);
    select.add(option);
  })
}

fileSelect(chapterSelect, chapters);
chapterSelect.addEventListener(
  'change', function(event) {
    var value = event.target.value,
        list = sections[value] || [];
    fillSelect(sectionSelect, list);
  }
);
textarea 元素

textarea 具有的屬性和方法如下:

  • name
  • value (用戶輸入信息)
  • select() (全選當(dāng)前輸入的內(nèi)容)
  • selectionStart (選中的內(nèi)容的起始位置,無(wú)選中時(shí)返回當(dāng)前光標(biāo)所在位置)
  • selectionEnd (選中內(nèi)容結(jié)束位置,無(wú)選中時(shí)返回光標(biāo)位置)
  • selectionDirection (選取方向 forward backward
  • setSelectionRange(start, end[, direction]) (使用程序選中內(nèi)容)
  • setRangeText(replacement[, start, end, [mode]]) (設(shè)置內(nèi)容范圍)
selection

表示選擇區(qū)域,對(duì)于 input 元素同樣有效。

selectionDirection 主要是用于在使用 SHIFT 鍵與方向鍵組合選取時(shí)的選取方向。設(shè)置為 forward 時(shí)選取移動(dòng)的方向?yàn)?nbsp;selectionEnd 設(shè)置為 backward 時(shí)移動(dòng)方向?yàn)?nbsp;selectionStart

@輸入提示示例

所需知識(shí)點(diǎn):

  • oninput
  • selectionStart
  • setRangeText
textarea.addEventListener(
  'input', function(event) {
    var target = event.target,
        cursor = target.selectionStart;
    if(target.value.charAt(cursor-1) === '@') {
      doShowAtList(functi=on(name){
        var end = cursor + name.length;
        target.setRangeText(
          name, cursor, end, 'end'
        );
      });
    }
  }
);
其他元素
  • fieldset
  • button
  • keygen
  • output
  • progress
  • meter

驗(yàn)證

可以被驗(yàn)證的元素如下所示:】

  • button
  • input
  • select
  • textarea

以下情況不可以做驗(yàn)證

  • input 元素在類型是 hidden, reset, button 時(shí)
  • button 元素在類型為 reset, button 時(shí)
  • input 與 textarea 當(dāng)屬性為 readonly 時(shí)
  • 當(dāng)元素為 datalist 的子孫節(jié)點(diǎn)時(shí)
  • 當(dāng)元素被禁用時(shí) disabled 的狀態(tài)
屬性

驗(yàn)證涉及到以下的以下屬性,在每一個(gè)可以驗(yàn)證的元素上均可以調(diào)用對(duì)于的屬性或通過(guò)接口進(jìn)行操作:

  • willValidate (表明此元素在表單提交時(shí)是否會(huì)被驗(yàn)證)
  • checkValidity() (用于驗(yàn)證元素,返回 true 當(dāng)驗(yàn)證通過(guò),或者觸發(fā) invalid 事件)
  • validity (存儲(chǔ)驗(yàn)證結(jié)果)
  • validationMessage (顯示驗(yàn)證異常信息)
  • setCustomValidity(message) (自定義驗(yàn)證錯(cuò)誤信息)

自定義異常范例

涉及到的知識(shí)點(diǎn):

  • oninvalid
  • setCustomValidity
<form action="./api" method="post">
  <label>Name: <input name="username" required></label>
  <button>submit</button>
</form>
input.addEventListener(
  'invalid', function(event){
    var target = event.target;
    if (target.validity.valueMissing) {
      target.setCustomValidity('Name is missing');
    }
  }
)

禁止驗(yàn)證范例

使用 form 中 novalidate 屬性來(lái)禁止表單提交的驗(yàn)證。

<form action="./api" method="post" novalidate>
  <label>Mobile: <input name="mobile" type="number"></label>
  <button>submit</button>
</form>

提交

隱式提交

在操作過(guò)程中通過(guò)控件的操作來(lái)提交表單(敲擊回車來(lái)提交表單),其需要滿足以下的條件:

  • 表單有非禁用的提交按鍵
  • 沒(méi)有提交按鍵時(shí),不超過(guò)一個(gè)類型為 text search url email password date time number的 input 元素
提交過(guò)程細(xì)節(jié)

提交過(guò)程分為兩個(gè)階段,第一個(gè)階段是更具表單 enctype 指定的值構(gòu)建要提交的數(shù)據(jù),第二個(gè)階段是使用指定的方法(method)發(fā)送數(shù)據(jù)到 action 指定的目標(biāo)。

構(gòu)建提交數(shù)據(jù),從可提交元素中提取數(shù)據(jù)組成指定數(shù)據(jù)結(jié)構(gòu)過(guò)程(可提交元素有 button input keygenobject select textarea

編碼方式(enctype)所支持的形式:

  • application/x-www-form-urlencoded (默認(rèn),數(shù)據(jù)格式為 & 分隔的鍵值對(duì))
  • multipart/form-data (IFC 2388 字節(jié)流形式,例如文件上傳所使用的數(shù)據(jù)編碼形式)
  • text/plain (回車換行符分隔的鍵值對(duì))

特殊案例一

當(dāng)一個(gè)表單元素 name="isindex" 并且 type="text" 而且滿足如下要求時(shí):

  • 編碼格式為 application/x-www-form-urlencoded
  • 作為表單的第一個(gè)元素

則提交時(shí)只發(fā)送 value 值,不包含 name。

<form action="./api" method="post">
  <input name="isindex">
  <input name="a">
  <button>submit</button>
</form>

特殊案例二

當(dāng) name="_charset_" 并且類型為 hidden 時(shí),而且滿足如下要求時(shí):

  • 沒(méi)有設(shè)置 value 值

則提交時(shí) value 自動(dòng)使用當(dāng)前提交的字符集填充。

submit 接口

form.submit() 可以通過(guò)調(diào)用接口submit()直接提交表單,在提交表單時(shí)均會(huì)觸發(fā)一個(gè) onsubmit 表單提交事件,在這個(gè)事件中 women 可以做下面的事件:

  • 提交前的數(shù)據(jù)驗(yàn)證
  • 阻止事件的默認(rèn)行為來(lái)取消表單的提交(例如當(dāng)驗(yàn)證失敗時(shí))
form.addEventListener(
  'submit', function(event) {
    var notValid = false;
    var elements = event.target.elements;

    // 自定義驗(yàn)證

    if (notValid) {
      // 取消提交
      event.preventDefault();
    }
  }
)

無(wú)刷新表單提交范例

常用的方式是通過(guò) AJAX 進(jìn)行實(shí)現(xiàn),這里我們使用 iframe 來(lái)做中介代理實(shí)現(xiàn)。

所需知識(shí)點(diǎn):

  • form
  • target
  • iframe
<iframe name="targetFrame" class="f-hidden" style="display:none" id="result">

<form action="./api" method="post" target="targetFrame">
  <input name="isindex">
  <input name="a">
  <button>submit</button>
</form>
var frame = document.getElementById('result');
frame.addEventListener(
  'load', function(event) {
    try {
      var result = JSON.parse(
        frame.contentWindow.document.body.textContent
      );

      // 還原登陸按鈕狀態(tài)
      disabledSubmit(false);

      // 識(shí)別登陸結(jié)果
      if (result.code === 200) {
        showMessage('j-suc', 'success');
        form.reset();
      }
    } catch(ex) {
      // 忽略操作
    }
  }
)

表單應(yīng)用

首先需要知道服務(wù)器端登陸接口的相關(guān)信息,如下所示:

描述數(shù)據(jù)信息
請(qǐng)求地址/api/login
請(qǐng)求參數(shù)telephone 手機(jī)號(hào)碼; password 密碼 MD5 加密
返回結(jié)果code 請(qǐng)求狀態(tài); result 請(qǐng)求數(shù)據(jù)結(jié)果

var form = document.forms.loginForm;

var message = document.getElementById('message');

// 通用邏輯封裝
function showMessage(class, message) {
  if(!class) {
    message.innerHTML = "";
    message.classList.remove('j-suc');
    message.classList.remove('j-err');
  } else {
    message.innerHTML = message;
    message.classList.add(class);
  }
}

function invalidInput (node, message) {
  showMessage('j-err', message);
  node.classList.add('j-err');
  node.focus();
}

function clearInvalid(node){
  showMessage();
  node.classList.remove('j-err');
}

function disabledSubmit(disabled) {
  form.loginBtn.disabled = !!disabled;
  var method = !disabled ? 'remove' : 'add';
  form.loginBtn.classList[method]('j-disabled');
}

// 驗(yàn)證手機(jī)號(hào)碼(系統(tǒng)自帶方法)
form.telephone.addEventListener(
  'invalid', function(event) {
    event.preventDefault();
    invalidInput(form.telephone, 'invalid mobile number');
  }
);

// 驗(yàn)證密碼
form.addEventListener(
  'submit', function(event) {
    var input = form.password;
    var password = input.value;
    errorMessage = '';
    if (password.length < 6) {
      errorMessage = 'password less than 6 char';
    } else if (!/\d./test(password) || !/[a-z]/i.test(password)) {
      errorMessage = 'password must contains number and letter'
    }

    if (!!errorMessage) {
      event.preventDefault();
      invalidInput(input, errorMessage);
      return;
    }
    // 提交表單代碼
    // ...
  }
);

// 提交表單
form.addEventListener(
  'submit', function(event){
    input.value = md5(password);
    disabledSubmit(true);
  }
);

// 狀態(tài)恢復(fù)
form.addEventListener(
  'focus', function(event) {
    // 錯(cuò)誤還原
    clearInvalid(event.target);
    // 還原登陸按鈕狀態(tài)
    disabledSubmit(false);
  }
)


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)