升级打怪-精读javascript高级程序设计(第十四章)

14 表单脚本

14.1 表单的基础知识

表单是由form元素来表示的,而js中,表单对应的则是HTMLFormElement类型(继承HTMLElement),私有属性和方法:

  • acceptCharset:服务器能够处理的字符集
  • action:接收的URL;
  • elements:表单中所有控件的集合
  • enctype:请求的编码类型
  • length:表单控件中的数量
  • method:要发送的HTTP请求类型
  • name:表单的名称
  • reset():将所有表单域重置为默认值
  • submit():提交表单
  • tartget():用于发送请求和接收响应的窗口名称;

14.1.1 提交表单

用户单击提交按钮或图像按钮时,就会提交表单.

//通用提交按钮
<input type="submit" value="Submit Form">input>

//自定义提交按钮
<button type="submit">Submit Formbutton>

//图像按钮
<input type="image" src="graphic.gif">input>
var from=documen.getElementById("myForm");
EventUtil.addHandle(form,"submit",function(event){
	//取得事件对象
	event=EventUtil.getEvent(event);
	
	//阻止默认事件
	EventUtil.preventDefault(event);
})

from.submit();

重复提交表单解决方案:

  • 第一次提交表单后就禁止提交按钮
  • 利用onsubmit事件处理程序取消后续的表单提交操作

14.1.2 重置表单

用户单击重置按钮时,表单会被重置.使用type特性值为"reset"的input或button都可以创建重置按钮

//通用重置按钮
<input type="reset" value="Reset Form"/>

//自定义重置按钮
<button type="reset">Reset Formbutton>
var from=documen.getElementById("myForm");
EventUtil.addHandle(form,"reset",function(event){
	//取得事件对象
	event=EventUtil.getEvent(event);
	
	//阻止默认事件
	EventUtil.preventDefault(event);
})

from.reset();

14.1.3 表单字段

每一个表单都有elements属性,该属性是表单中所有表单元素的集合.

var form = document.getElementById("form");
//取得表单中第一个字段
form.elements[0];
//取得表单中名称是"name"的字段
form.elements["name"];
//取得表单中包含字段的数量
form.elements.length;

如果使用的一个name,那么返回的是一个NodeList;
1.共有的表单字段属性

表单字段公有的属性如下:

  • disable:布尔值,表示当前字段是否被禁用
  • form:只想当前字段所属表单的指针,只读;
  • name:当前字段的名称
  • readOnly:布尔值,表示当前字段是否只读
  • tabIndex:表示当前字段的切换序号
  • value:当前字段将被提交给服务器的值.

动态修改表单属性:

var form = document.getElementById("form");

  var field = form.elements[0];

  //修改value属性
  field.value = "456";

  //检查form属性的值
  console.log(field.form === form); //true

  //把焦点设置到当前字段
  field.focus();

  //禁用当前字段
  field.disabled = true;

  //修改type属性(不推荐,但对input来说是可行的)
  field.type = "checkbox";

能够动态修改表单字段属性,意味着我们可以在任何时候,以任何方式来动态操作表单.
2.共有的表单字段方法

  • 每个表单字段都有两个方法:focus()和blur();
  • html5添加了一个autofocus属性
    3.共有的表单字段事件
    除了支持鼠标,键盘,更改和html事件之外,所有表单字段都支持下列3个事件:
  • blur:当前字段失去焦点
  • change:对于input和textarea元素,在他们失去焦点且value值改变时触发;对于select元素,在其选项改变时触发
  • focus:当前字段获取焦点时触发

14.2 文本框脚本

input:单行文本,通过size特性,可以指定文本框能够显示的字符数.通过value特性,可以设置文本框的初始值,而maxlength特性则用于指定文本框可以接受的最大字符数
textarea:多行文本,指定文本框大小可以使用rows和clos特性.不能指定最大字符

<input type="text" size="20" value="456" maxlength="10" />
 <textarea rows="20" clos="20">textarea>

14.2.1 选择文本

上述两个文本框都支持select()方法,这个方法用于选择文本框中的所有文本.

1.选择(select)事件
IE9+ 、Safair 、Opera、Chorme、Firefox 当用户选择文件时,鼠标松开时触发
IE8以及其下 当用户选择文本时,就会触发

window.onload = function() {
        if (document.addEventListener) {
            document.forms[0].elements[0].addEventListener("select", function () {
                console.log("select" + this.value);
            }, false)
        } else {
            document.forms[0].elements[0].attachEvent("onselect", function (event) {
                console.log("ie select" + event.srcElement.value);
            });
        }
    }

2.取得选择的文本
H5 添加的两个属性,

selectionStart、selectionEnd
    IE9+ 、Safair 、Opera、Chorme、Firefox 支持这两个属性

IE8不支持这两个属性,但是提供 document.selection 对象, 其中保存着用户在整个文档范围内选择的文本信息;也就是说,无法确定用户选择的是页面中那个部位的文本。不过,在于select事件一起使用时候,可以假设是用虎选择了文本框中的文本,因此触发该事件。要取得选择的文本,首先必须创建一个范围,然后再将文本从其中取出来,如下。

function getSelectedText(textbox){
        if(typeof textbox.selectionStart == "number"){
            return  textbox.value.substr(textbox.selectionStart,textbox.selectionEnd);
        }else if(document.selection){
            return document.selection.createRange().text;
        }
    }

3.选择部分内容
setSelectionRange(start,end) 接收两个参数,并且不包括 end指定的内容
  IE9+ 、Safair 、Opera、Chorme、Firefox

document.forms[0].elements[0].setSelectionRange(0,3); //123

document.forms[0].elements[0].setSelectionRange(0,2);//12
document.forms[0].elements[0].setSelectionRange(0,1);//1

E8以及其低版本中不支持 setSelctionRange方法,但是要想选择部分内容步骤如下:
 1.createTextRange 创建一个范围,并将其放在恰当的位置
 2.再通过 moveStart() 和 moveEnd()这两个范围方法将范围移动到位。
3.调用moveStart、moveEnd之前必须使用 collapse()将范围折叠到文本框的开始位置。(此时在moveStart()将范围的起始点和终点移到了相同的位置 )
 4.接着再给moveEnd()传入要选择的字符总数即可。 最后一步就是使用范围的select()选择文本

跨浏览器的方式:

function selectText(textbox,start,end){
        if(textbox.setSelectionRange){
            textbox.setSelectionRange(start,end)
        }else{
            var range = textbox.createTextRange();
            range.collapse(true);
            range.moveStart("character",start); // character 字符  || word 单词 || sentence 段落
            range.moveStart("character",end-start)
            range.select();
        }
    }

14.2.2 过滤输入

1.屏蔽字符
使用keypress事件,可以提前每一个文本输入做验证

element.addEventListener("keypress", function (e) {
	//验证操作
	
})

2.操作剪贴板

HTML5增加了剪切板事件:

  • beforecopy:发生复制操作时触发
  • copy:发生剪切操作前触发
  • beforecut:在发生剪切操作前触发
  • cut:在发生剪切操作时触发
  • beforepaste:在发生粘贴操作前触发
  • paste:在发生粘贴操作前触发

获取或设置剪贴板数据

操作的数据放在clipboardData对象中的,在ie中这个对象通过window.clipboardData来访问,在其它浏览器中则通过事件处理函数的参数来访问。
clipboardData对象有三个方法:getData()、 setData() 和 clearData()。在使用getData或setData时,要指定文档类型,ie中使用text或URL,其它浏览使用mime类型(text/plain)
setData()方法只有在ie中才能设置剪贴板数据,在其它浏览器中设置后剪贴板数据依然无效。

第三方库
使用clipboard.js进行操作,库介绍

14.2.3 自动切换焦点

为增加易用性,通四海加快数据输入,可以在前一个文本框中的字符达到最大数量后,自动将焦点切换到下一个文本框.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>test javascript</title>
    <script type="text/javascript">
      window.onload = function () {
        var EventUtil = {
          addhandler: function (element, type, handler) {
            if (element.addEventListenter) {
              element.addEventListenter(type, handler, false);
            } else if (element.attachEvent) {
              element.attachEvent("on" + type, handler);
            } else {
              element["on" + type] = handler;
            }
          },

          getEvent: function (event) {
            return event ? event : window.event;
          },

          getTarget: function () {
            return event.target || event.srcElement;
          },

          preventDefault: function () {
            if (event.preventDefault) {
              event.preventDefault();
            } else {
              event.returnValue = false;
            }
          },

          stopPropagation: function () {
            if (event.stopPropagation) {
              event.stopPropagation();
            } else {
              event.cancelBuddle = true;
            }
          },

          removehandler: function (element, type, handler) {
            if (element.removeEventListenter) {
              element.addEventListenter(type, handler, false);
            } else if (element.detachEvent) {
              element.detachEvent("on" + type, handler);
            } else {
              element["on" + type] = null;
            }
          },

          getCharCode: function (event) {
            if (typeof event.charCode == "number") {
              return event.charCode;
            } else {
              return event.keyCode;
            }
          },

          getClipboardText: function (event) {
            var clipboardData = event.clipboardData || window.clipboardData;
            return clipboardData.getData("text");
          },

          setClipboardText: function (event, value) {
            if (event.clipboardData) {
              return event.clipboardData.setData("text/plain", value);
            } else if (window.clipboardData) {
              return window.clipboardData.setData("text", value);
            }
          },
        };

        var textarea = document.forms[0].elements["text"];
        var button = document.getElementById("button");

        (function () {
          function tabForward(event) {
            event = EventUtil.getEvent(event);
            var target = EventUtil.getTarget(event);

            if (target.value.length == target.maxLength) {
              var form = target.parentElement;

              for (var i = 0, len = form.elements.length; i < len - 1; i++) {
                if (form.elements[i] == target) {
                  form.elements[i + 1].focus();
                  return;
                }
              }
            }
          }

          var textTel1 = document.getElementById("txtTel1");
          var textTel2 = document.getElementById("txtTel2");
          var textTel3 = document.getElementById("txtTel3");

          EventUtil.addhandler(textTel1, "keyup", tabForward);
          EventUtil.addhandler(textTel2, "keyup", tabForward);
          EventUtil.addhandler(textTel3, "keyup", tabForward);
        })();
      };
    </script>
  </head>
  <body>
    <form>
      <input type="text" name="tel1" id="txtTel1" maxlength="3" />
      <input type="text" name="tel2" id="txtTel2" maxlength="3" />
      <input type="text" name="tel3" id="txtTel3" maxlength="3" />
    </form>
  </body>
</html>

14.2.4 HTML5约束验证API

为了将表单提交到服务器之前验证数据,HTML5新增了一些功能.
1.必填字段
任何标注有required的字段,在提交表单时都不能空着;

<input type="text" name="user" required />

2.其他输入类型

<input type="email" name="email" />
<input type="url" name="homepage" />

3.数值范围
基于数字的值:number,range,datetime,datetime-local,date,month,week,还有time.浏览器对这几个类型的支持情况并不好.

<input type="number" min="0" max="100" name="count" />

4.输入模式

<input type="text" pattern="\d+" />

5.检测有效性
使用checkValidty()方可以检测表单中的某个字段是否有效.

if(document.forms[0].elements[0].checkValidity()){
//字段有效
}else{
	//字段无效
}

检测整个表单是否有效

if(document.forms[0].checkValidity()){
//字段有效
}else{
	//字段无效
}


6.禁用验证
通过设置novalidate属性,可以告诉表单不进行验证;

14.3 选择框脚本

参考资料1
参考资料2

选择框是通过select和option元素所创建的;

14.4 表单序列化

浏览器是怎样将数据发送给服务器的:
1.对表单字段的名称和值进行url编码,使用和号(&)分隔
2.不发送禁用的表单字段
3.只发送勾选的复选框和单选按钮
4.不发送type为reset和button的按钮
5.多选选择框中的每一个选中的值单独一个条目
6.在单击提交按钮提交表单的情况下,也会发送提交按钮
7.select元素的值,就是选中的option元素的value特性的值

 function serializeForm2(form) {
   var parts = [];
   for (var i = 0, i1 = form.elements.length; i < i1; i++) {
    var field = form.elements[i];
    switch (field.type) {
     case 'select-one':
     case 'select-multiple':
      if (field.type.length) {
       for (var j = 0, j1 = field.options.length; j < j1; j++) {
        var option = field.options[j];
        if (option.selected) {
         var optionValue = '';
         if (option.hasAttribute('value') && option.attributes['value'].specified) {
          //specified表明是否有此属性,有的话返回true,若定义了此属性但尚未添加到元素中也返回true。
          optionValue = option.value;
         } else {
          optionValue = optionValue.text;
         }
         parts.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(optionValue));
        }
       }
      }
      break;
     case undefined:
     case 'file':
     case 'submit':
     case 'reset':
     case 'button':
      break;
     case 'radio':
     case 'checkbox':
      if(!field.checked){
       break;
      }else{
       parts.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(field.dataset['index']));
       break;
      }
     default:
      if(field.name.length){
       parts.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value));
      }
    }
   }
   return parts.join('&');
  }
   
  // 调用
  var oForm = document.getElementById('target');
    console.log(serializeForm2(oForm));

14.5 富文本编辑器

14.5.1 实现方式

1.使用iframe实现,主要通过设置文档的designMode设置为"on":


<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documenttitle>
  head>
  <body>
    <iframe
      src="./blank.html"
      frameborder="0"
      name="richedi"
      style="height: 100px; width: 100px"
    >iframe>
  body>
html>
<script>
  frames["richedi"].addEventListener("load", function () {
    frames["richedi"].document.designMode = "on";
  });
script>

2.使用contenteditable属性


<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documenttitle>
    <style>
      .editable {
        height: 100px;
        width: 100px;
        border: 2px solid red;
        overflow-y: scroll;
      }
    style>
  head>
  <body>
    <div class="editable" id="richedit" contenteditable="">div>
  body>
html>

14.5.2 操作富文本

操作富文本

14.5.3 富文本选区

selection 对象拥有下列属性:

  • anchorNode: 选区起点所在节点
  • anchorOffset:选区起点在其所在节点中的偏移量
  • focusNode:选区终点所在节点
  • focusOffest:选区终点在其所在节点中的偏移量
  • isCollapsed:布尔值,表示选区的起点终点是否重合
  • rangeCount:选区包含DOM节点的数量

拥有下列方法:

  • addRange(range):将指定范围内的内容添加到选区
  • collapse(node,offset):将选区折叠到指定节点的指定偏移处
  • collapseToEnd():将选区折叠到选区终点的位置
  • collapseToStart():将选区折叠到起点位置
  • containsNode(node):确定指定节点是否包含在选区之内
  • deleteFromDocument():从文档中删除选区中的文本
  • extend(node,offset):将 focusNode 和 focusOffset 移动到指定位置来拓展选区
  • getRangeAt(index): 返回所应对应选区中的DOM范围
  • removeAllRanges():从选区中移除所有的DOM范围,该方法也会移除选区,因为选区中至少要有一个范围
  • removeRange(range): 从选区中移除指定范围
  • selectAllChildren(node):清除选区并选择指定节点的所有子节点
  • toString():返回选区包含的指定文本
let selection = frames["richedit"].getSelection();

// 取得选择的文本
let selectedText = selection.toString();

// 取得选区范围
let range = selection.getRangeAt(0);

// 突出显示选择的文本
let span = frames["richedit"].document.createElement('span');
span.style.backgroundColor = "yellow";
range.surroundContents(span);

14.5.4 表单与富文本

由于富文本编辑器不是使用表单控件实现的,因此从技术上来说富文本编辑器并不属于表单的一部分

所以我们在提交表单时富文本编辑器内的内容并不会随着一起提交,所以我们需要手工提取并提交富文本编辑器中的内容

let form = document.forms[0];

form.onsubmit = function(event){
    let target = event.target;

    target.elements["comments"].value = document.getElementById("richedit").innerHtml;

}

你可能感兴趣的:(前端,高级程序设计,js,javascript)