在 HTML 中,有两种方式表现文本框:

  1. 使用 <input> 元素表现单行文本框。
  2. 使用 <textarea> 元素表现多行文本框。

必须将 <input> 元素的 type 属性设置为 “text”,才能表现为文本框。它的 size 属性指定这个文本框能够显示的字符数;value 属性用于设置文本框的初始值;maxlength 属性指定文本框可以接受的最大字符数:

<input type="text" size="25" maxlength="50" value="initial value">

<textarea> 元素的 rows 属性指定文本框的字符行数,cols 属性指定文本框的字符列数。它的初始值必须放在 <textarea></textarea> 之间:

<textarea rows="25" cols="5">初始值</textarea>

注意,不能给 <textarea> 元素设置最大字符数!

这两种文本框都会将用户输入的内容保存在 value 属性中,可以通过这个属性读取和写入文本框的值:

var textbox = document.forms[0].elements["textbox1"];
console.log(textbox.value);

textbox.value = "新值";

不建议使用标准的 DOM 方法,因为使用标准的 DOM 方法对 value 属性所做的更改,不一定会反映在 DOM 中!

1.1 选择文本

这两种文本都支持 select() 方法,这可以选择文本框中的所有文本。调用 select() 方法时,除了 Opera 之外,其他的浏览器都会将焦点设置到文本框中。

一般情况下,在文本框获得焦点时选择所有的文本内容,这会提高用户体验,特别是文本框包含默认值时,这样用户就不需要一个一个地删除文本咯:

<script type="text/javascript">
    var textbox = document.getElementById("deniro");
    EventUtil.addHandler(textbox, "focus", function (event) {
        event = EventUtil.getEvent(event);
        var target = EventUtil.getTarget(event);
        target.select();
    })
</script>

1.1.1 select 事件

选择了文本框中的文本时,会触发 select 事件。至于什么时候触发,会因浏览器而异。IE9+、Opera、Firefox、Chrome 和 Safari 中,只要用户选择了文本并释放鼠标,才会触发 select 事件。IE8 及早期版本,只要用户选择了一个字符,就会触发select 事件:

<script type="text/javascript">
    var textbox = document.forms[0].elements["textbox1"];
    EventUtil.addHandler(textbox, "select", function (event) {
        console.log("Text selected" + textbox.value);
    });
</script>

1.1.2 取得选择的文本

HTML5 添加了两个属性:selectionStart 和 selectionEnd。它们保存的是基于 0 的数值,表示所选择文本的范围(文本选区开头与结尾的偏移量)。 IE8 及更早版本不支持这两个属性,它用的是 document.selection 对象:

<script type="text/javascript">
    function getSelectedText(textbox) {
        if (typeof textbox.selectionStart == "number") {
            return textbox.value.substring(textbox.selectionStart, textbox.selectionEnd);
        } else if (document.selection) {
            return document.selection.createRange().text;
        }
    }

    var textbox = document.forms[0].elements["textbox1"];
    EventUtil.addHandler(textbox, "select", function (event) {
        console.log(getSelectedText(textbox));
    });

</script>

1.1.3 取得选择的部分文本

HTML5 使用 setSelectionRange() 方法,它接收两个参数:要选择的第一个字符的索引以及最后要选择的最后一个字符之后的字符索引,而 IE8 及更早版本是使用范围来选择部分文本的,所以跨浏览器的方案是:

<script type="text/javascript">
    /**
     *
     * @param textbox 要操作的文本框
     * @param startIndex 要选择文本中的第一个字符的索引
     * @param stopIndex 要选择文本中的最后一个字符之后的索引
     */
    function selectText(textbox, startIndex, stopIndex) {
        if (textbox.setSelectionRange) {
            textbox.setSelectionRange(startIndex, stopIndex);
        } else if (textbox.createTextRange) {
            var range = textbox.createTextRange();
            range.collapse(true);
            range.moveStart("character", startIndex);
            range.moveEnd("character", stopIndex - startIndex);
            range.select();
        }

        textbox.focus();
    }

    var textbox = document.forms[0].elements["textbox1"];
    textbox.value = "Hello world!";

    //选择所有文本
    selectText(textbox, 0, textbox.value.length);

    //选择前 3 个字符
    selectText(textbox, 0, 3);//"Hel"

    //选择第 4 到第 6 个字符
    selectText(textbox, 4, 7);//"o w"
</script>

这种技术一般用于根据输入提供建议的自动完成文本框中,是不是很厉害呀O(∩_∩)O~

2 过滤输入

综合使用事件和 DOM,就可以将普通的文本框转换为能够理解用户输入的数据的智能型文本框!

2.1 屏蔽字符

有时候,需要确保用户输入的文本中包含某些字符或者不包含某些字符。可以通过 keypress 事件(需要阻止默认行为)来屏蔽这些字符:

<script type="text/javascript">

    var textbox = document.forms[0].elements["textbox1"];

    EventUtil.addHandler(textbox, "keypress", function (event) {
        event = EventUtil.getEvent(event);
//        var target = EventUtil.getTarget(event);
        var charCode = EventUtil.getCharCode(event);

        if (!/\d/.test(String.fromCharCode(charCode)) && charCode > 9 && !event.ctrlKey) {
            EventUtil.preventDefault(event);
        }
    })
</script>

上面的代码屏蔽了非数字字符(但无法阻止输入法写入的字符)。

2.2 操作剪贴板

HTML5 定义了 6 个剪贴板事件:

事件

说明

beforecopy

发生复制操作前触发。

copy

发生复制操作时触发。

beforecut

发生剪切操作前触发。

cut

发生剪切操作时触发。

beforepaste

发生粘贴操作前触发。

paste

发生粘贴操作时触发。


这些事件和对象会因浏览器而异,所以我们在 EventUtil 中添加了以下方法:

/**
 * 获取剪贴板数据
 * @param event
 * @returns {string}
 */
getClipboardText: function (event) {
    var clipboardData = (event.clipboardData || window.clipboardData);
    return clipboardData.getData("text");
},

/**
 * 设置剪贴板数据
 * @param event
 * @param value 数据
 * @returns {boolean}
 */
setClipboardText: function (event, value) {
    if (event.clipboardData) {
        return event.clipboardData.setData("text/plain", value);
    } else if (window.clipboardData) {//IE
        return window.clipboardData.setData("text", value);
    }
}

为了确保文本框中的内容符合某个格式要求,从剪切板中粘贴过来的数据也必须符合这种格式要求,我们可以在 paste 事件中来实现:

<script type="text/javascript">
    var textbox = document.getElementById("textbox1");

    EventUtil.addHandler(textbox, "paste", function (event) {
        event = EventUtil.getEvent(event);
        var text = EventUtil.getClipboardText(event);

        if (!/^\d*$/.test(text)) {
            EventUtil.preventDefault(event);
        }
    });
</script>

Firefox、Safari 和 Chrome 只允许在 onpaste 事件中使用 getData() 方法。

因为并非所有浏览器都支持访问剪切板,所以也可以屏蔽这些剪切板操作。只要在支持 copy、cut 和 paste 事件的浏览器中阻止这些事件的默认行为即可!

3 自动切换焦点

用户填写完当前字段后,自动将焦点切换到下一个字段,这样可以增强表单的易用性。一般情况下,必须知道用户已经输入了既定长度的数据(比如手机号码)时,才能切换焦点。我们可以把手机号码分为三个部分,形如:

<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="4">
</form>

<script type="text/javascript">
    (function () {

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

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

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

        var textbox1 = document.getElementById("txtTel1");
        var textbox2 = document.getElementById("txtTel2");
        var textbox3 = document.getElementById("txtTel3");

        EventUtil.addHandler(textbox1, "keyup", tabForward);
        EventUtil.addHandler(textbox2, "keyup", tabForward);
        EventUtil.addHandler(textbox3, "keyup", tabForward);
    })();
</script>

注意:这段代码只考虑给出的 <input> 标记,实际应用时还要考虑隐藏字段。

4 HTML5 验证 API

支 持HTML5 验证 API 的浏览器有 Firefox 4+、Safari 5+、Chrome 和 Opera 10+。

4.1 必填字段

在表单字段中指定 required 属性:

<input type="text" name="username"" required>

这个属性适用于 <input><textarea><select> 字段。在 JavaScript 中,通过对应元素的 required 属性,可以检查某个字段是否为必填字段:

var isUsernameRequired = document.forms[0].elements["username"].required;

可以这样检测浏览器是否支持 required 属性:

var isRequiredSupported = "required" in document.createElement("input");

对于空的必填字段,不同的浏览器有不同的处理方式。所以在标准还未统一之前,最好不要使用!

4.2 其他输入类型

HTML5 为 <input> 元素的 type 属性新增了一些类型(email 和 url),这些类型还提供默认的验证功能。但在不同的浏览器中也有不同的处理方式。

不支持它们的浏览器会自动将未知的值设置为 text,可以这样检测:

var isEmailSupported = (input.type == "email");

4.3 数值范围

HTML5 还定义了一些跟数字有关的类型,比如 number、range、datetime 等等。浏览器对这些类型的支持情况不好,所以最好先别用!

4.4 输入模式

文本字段新增了 pattern 属性,它是一个正则表达式,用于验证文本字段的内容是否符合要求:

<input type="text" pattern="\d+" name="count">

注意,模式的开头和结尾不需要加 ^$,默认在内部已经自动加上咯O(∩_∩)O~

可以在 JavaScript 中通过 pattern 属性进行访问。

这样检测浏览器是否支持 pattern 属性:

var isPatternSupported = "pattern" in document.createElement("input");

4.5 检测有效性

使用 checkValidity() 来检测表单中的某个字段是否有效。表单中的每一个字段都有这个方法:

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

也可以检测整个表单是否有效(即表单中的所有字段都有效才算有效):

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

input 还有一个 validity 对象,它包含这些属性:

属性

说明

customError

如果设置了 setCustomValidity(),则返回 true。

patternMismatch

值与指定的 pattern 不匹配,则返回 true。

rangeOverflow

值比 max 的值大,则返回 true。

rangeUnderflow

值比 min 的值小,则返回 true。

stepMisMatch

min 和 max 之间的步长不合理,返回 true。

tooLong

长度超过 maxlength 属性指定的长度,返回 true。Firefox4 会自动约束字符数量,所以这个值可能永远都是 false。

typeMismatch

如果不是 type 类型(比如 mail)所默认的格式,返回 true。

valid

如果其他所有属性都是 false,那么返回 true。

valueMissing

如果设置了 required 的字段没有值,则返回 true。


因此可以这样检测:

if (input.validity && !input.validity.valid){
    if (input.validity.valueMissing){
        console.log("Please specify a value.");
    }else if(input.validity.typeMismatch){
         console.log("Please enter an email address.");
    }else{
         console.log("Value is invalid.");
    }
}

4.6 禁用验证

使用 novalidate 属性,可以让表单不参与验证:

<form method="post" action="xxx" novalidate>
...
</form>

在 JavaScript 中也可以使用 novalidate 属性进行获取或设置操作。

如果一个表单有多个提交按钮,那么可以指定某个提交按钮不进行表单验证(formnovalidate 属性):

<form method="post" action="xxx">
<input type="submit" value="标准提交">
<input type="submit" value="不验证提交" formnovalidate>
</form>

使用 JavaScript 也可以设置这个属性:

document.forms[0].elements["btnNoValidate"].fromNoValidate = true;

注意这里的大小写与 html 中的不同!