跳至主要內容

表单控件

Yang大约 6 分钟

tabindex

  • 任何具有 tabindex 特性的元素,都会变成可聚焦的
  • 该特性的 value 是当使用 Tab(或类似的东西)在元素之间进行切换时,元素的顺序号
  • 切换顺序为:从 1 开始的具有 tabindex 的元素排在前面(按 tabindex 顺序),然后是不具有 tabindex 的元素(例如常规的 <input>
  • 不具有 tabindex 的元素按文档源顺序(默认顺序)切换
  • tabindex="0" 会使该元素被与那些不具有 tabindex 的元素放在一起。也就是说,当我们切换元素时,具有 tabindex="0" 的元素将排在那些具有 tabindex ≥ 1 的元素的后面
  • tabindex="-1" 只允许以编程的方式聚焦于元素。Tab 键会忽略这样的元素,但是 elem.focus() 有效
  • 可以使用 elem.tabIndex 通过 JavaScript 来添加 tabindex,效果是一样的

获取表单

document.forms.my; // name="my" 的表单
document.forms[0]; // 文档中的第一个表单

element.form; // 反向获取表单

获取表单元素

  • 返回元素节点或元素节点列表
  • form.elements.name 可以简写成 form.name
<form name="my">
  <input name="one" value="1">
  <input name="two" value="2">
  <fieldset name="userFields">
    <legend>info</legend>
    <input name="login" type="text">
  </fieldset>
</form>

<script>
  // 获取表单
  let form = document.forms.my; // <form name="my"> 元素

  // 获取表单中的元素
  let elem = form.elements.one; // <input name="one"> 元素

  console.log(elem.value); // 1
  
  let fieldset = form.elements.userFields;
  console.log(fieldset); // HTMLFieldSetElement
  
  // 可以通过名字从表单和 fieldset 中获取 input
  console.log(fieldset.elements.login == form.elements.login); // true
</script>

表单元素

input

  • input.value:获取值

radio

  • radio.checked:是否选中

checkbox

  • checkbox.checked:是否选中

textarea

  • textarea.value:获取值

select

  • select.options:获取 <option> 子元素集合

  • select.value:当前选择 <option>value

  • select.selectedIndex:当前选择 <option> 的索引

  • option.selected<option> 是否被选择

  • option.index<option> 在所属 <select> 中的索引

  • option.text<option> 的文本内容

  • option.value<option>value

新增 <option> 标签的方式

/**
 * text:标签内的文本内容
 * value:标签 value 属性的值
 * defaultSelected:如果为 true,则会在添加标签属性 selected,可以使用 getAttribute 获取
 * selected:选项是否被选中
 */
option = new Option(text, value, defaultSelected, selected);

表单事件

submit

提交表单时触发,具体条件如下

  • 点击 <input type="submit"><input type="image"> 时触发
  • input 字段中按下 Enter 键时触发
    • 会在 <input type="submit"> (如果有的话)上触发一次 click 事件
<form onsubmit="return false">
 <input type="text" size="30" value="Focus here and press enter">
 <input type="submit" value="Submit" onclick="alert('click')">
</form>
// 主动触发,直接提交表单
form.submit()

focus

聚焦元素时触发,当用户点击某个元素或使用键盘上的 Tab 键选中时,该元素将会获得聚焦

可以通过 document.activeElement 来获取当前所聚焦的元素

// 监听事件
elem.onfocus = function(){}

// 主动触发
elem.focus()

blur

元素失去焦点时触发,可能发生在用户点击页面的其它地方,或者按下 Tab 键跳转到下一个表单字段,亦或是其它途径的时候

// 监听事件
elem.onblur = function(){}

// 主动触发
elem.onblur()

change

当元素更改完成时,将触发 change 事件

  • 对于文本输入框,当其失去焦点时,就会触发 change 事件,但是键入内容时不会触发 change 事件
  • 对于其它元素:selectinput type=checkbox/radio,会在选项更改后立即触发 change 事件

input

用户对输入值进行修改后,(只要值改变了)就会触发 input 事件

  • 当输入值更改后,就会触发 input 事件,无法使用 event.preventDefault()

cut/copy/paste

事件发生于剪切/拷贝/粘贴一个值的时候

  • 属于 ClipboardEvent 类,并提供了对剪切/拷贝/粘贴的数据的访问方法
  • 在剪切/复制事件处理程序中调用 event.clipboardData.getData(...) 只会得到一个空字符串,因为此时数据还未存入剪切板
    • 可以使用 event.preventDefault() 来中止行为,什么都不会被复制/粘贴
<input type="text" id="input">
<script>
  input.onpaste = function(event) {
    alert("paste: " + event.clipboardData.getData('text/plain'));
    event.preventDefault();
  };
  input.oncut = input.oncopy = function(event) {
    alert(event.type + '-' + document.getSelection());
    event.preventDefault();
  };
</script>

注意

focus/blur事件委托

方法一:两个事件均不会向上冒泡,但会在捕获阶段向下传播

<form id="form">
  <input type="text" name="name" value="Name">
  <input type="text" name="surname" value="Surname">
</form>
<style> .focused { outline: 1px solid red; } </style>
<script>
  // 将处理程序置于捕获阶段(最后一个参数为 true)
  form.addEventListener("focus", () => form.classList.add('focused'), true);
  form.addEventListener("blur", () => form.classList.remove('focused'), true);
</script>

方法二:使用 focusinfocusout 事件,与 focus/blur 事件完全一样,只是它们会冒泡

<form id="form">
  <input type="text" name="name" value="Name">
  <input type="text" name="surname" value="Surname">
</form>
<style> .focused { outline: 1px solid red; } </style>
<script>
  form.addEventListener("focusin", () => form.classList.add('focused'));
  form.addEventListener("focusout", () => form.classList.remove('focused'));
</script>

例子

可编辑的 div

点击 div 变成文本框来进行编辑

按 Enter 键或者通过 blur 来保存结果变回 div

<!DOCTYPE HTML>
<html>

  <head>
    <meta charset="utf-8">
    <style>
      .view,
      .edit {
        height: 150px;
        width: 400px;
        font-family: sans-serif;
        font-size: 14px;
        display: block;
      }

      .view {
        /* padding + border = 3px */
        padding: 2px;
        border: 1px solid black;
      }

      .edit {
        /* replace padding with border (still 3px not to shift the contents) */
        border: 3px solid blue;
        padding: 0px;
      }

      .edit:focus {
        /* remove focus border in Safari */
        outline: none;
      }
    </style>
  </head>

  <body>
    <div id="view" class="view">Text</div>
    <script>
      let area = null;
      let view = document.getElementById('view');

      view.onclick = function() {
        editStart();
      };

      function editStart() {
        area = document.createElement('textarea');
        area.className = 'edit';
        area.value = view.innerHTML;

        area.onkeydown = function(event) {
          if (event.key == 'Enter') {
            this.blur();
          }
        };

        area.onblur = function() {
          editEnd();
        };

        view.replaceWith(area);
        area.focus();
      }

      function editEnd() {
        view.innerHTML = area.value;
        area.replaceWith(view);
      }
    </script>

  </body>
</html>

模态框

<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8">
    <style>
      html,
      body {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100%;
      }

      #prompt-form {
        display: inline-block;
        padding: 5px 5px 5px 70px;
        width: 200px;
        border: 1px solid black;
        background: white url(https://en.js.cx/clipart/prompt.png) no-repeat left 5px;
        vertical-align: middle;
      }

      #prompt-form-container {
        position: fixed;
        top: 0;
        left: 0;
        z-index: 9999;
        display: none;
        width: 100%;
        height: 100%;
        text-align: center;
      }

      #prompt-form-container:before {
        display: inline-block;
        height: 100%;
        content: '';
        vertical-align: middle;
      }

      #cover-div {
        position: fixed;
        top: 0;
        left: 0;
        z-index: 9000;
        width: 100%;
        height: 100%;
        background-color: gray;
        opacity: 0.3;
      }

      #prompt-form input[name="text"] {
        display: block;
        margin: 5px;
        width: 180px;
      }
    </style>
  </head>

  <body style="height:3000px">

    <h2>Click the button below</h2>

    <input type="button" value="Click to show the form" id="show-button">


    <div id="prompt-form-container">
      <form id="prompt-form">
        <div id="prompt-message"></div>
        <input name="text" type="text">
        <input type="submit" value="Ok">
        <input type="button" name="cancel" value="Cancel">
      </form>
    </div>

    <script>
      // Show a half-transparent DIV to "shadow" the page
      // (the form is not inside, but near it, because it shouldn't be half-transparent)
      function showCover() {
        let coverDiv = document.createElement('div');
        coverDiv.id = 'cover-div';

        // make the page unscrollable while the modal form is open
        document.body.style.overflowY = 'hidden';

        document.body.append(coverDiv);
      }

      function hideCover() {
        document.getElementById('cover-div').remove();
        document.body.style.overflowY = '';
      }

      function showPrompt(text, callback) {
        showCover();
        let form = document.getElementById('prompt-form');
        let container = document.getElementById('prompt-form-container');
        document.getElementById('prompt-message').innerHTML = text;
        form.text.value = '';

        function complete(value) {
          hideCover();
          container.style.display = 'none';
          document.onkeydown = null;
          callback(value);
        }

        form.onsubmit = function() {
          let value = form.text.value;
          if (value == '') return false; // ignore empty submit

          complete(value);
          return false;
        };

        form.cancel.onclick = function() {
          complete(null);
        };

        document.onkeydown = function(e) {
          if (e.key == 'Escape') {
            complete(null);
          }
        };

        let lastElem = form.elements[form.elements.length - 1];
        let firstElem = form.elements[0];

        lastElem.onkeydown = function(e) {
          if (e.key == 'Tab' && !e.shiftKey) {
            firstElem.focus();
            return false;
          }
        };

        firstElem.onkeydown = function(e) {
          if (e.key == 'Tab' && e.shiftKey) {
            lastElem.focus();
            return false;
          }
        };

        container.style.display = 'block';
        form.elements.text.focus();
      }

      document.getElementById('show-button').onclick = function() {
        showPrompt("Enter something<br>...smart :)", function(value) {
          alert("You entered: " + value);
        });
      };
    </script>
  </body>
</html>
上次编辑于:
贡献者: sunzhenyang