Eloquent JavaScript 笔记 十八:Forms and Form Fields

1. Fields

fields是可以放在form中的各种控件,常用的有:<input>, <textarea>, <select>,其中<input> 有多种类型:text, password, checkbox, radio, file。

<p><input type="text" value="abc" autofocus>(text)</p>
<p><input type="password" value="abc">(password)</p>
<p><input type="checkbox" checked>(checkbox)</p>
<p>
    <input type="radio" value="A" name="choice">
    <input type="radio" value="B" name="choice">
    <input type="radio" value="C" name="choice">(radio)
</p>
<p><input type="file">(file)</p>
<textarea>
    one
    two
    three
</textarea>
<br>
<select>
    <option>Pancakes</option>
    <option>Pudding</option>
    <option>Ice cream</option>
</select>
fields是DOM elements,它们不仅可以用在form中,也可以放在form外面。

2. Focus

每个fields可以获得键盘焦点,只有当获得焦点时,这个field才能获取键盘输入。

相关函数和属性:

  element.focus() : 获取焦点

  blur() :放弃焦点 

  document.activeElement : 当前获得焦点的element

<input type="text">
<script>
  document.querySelector("input").focus();
  console.log(document.activeElement.tagName);
  // → INPUT
  document.querySelector("input").blur();
  console.log(document.activeElement.tagName);
  // → BODY
</script>

  autofocus : document 加载后,自动获得焦点

  tabindex:焦点顺序(大部分类型的node都不能获得焦点,但添加此属性之后就能获得焦点了)

<input type="text" autofocus>

<input type="text" tabindex=1> <a href=".">(help)</a>
<button οnclick="console.log('ok')" tabindex=2>OK</button>

3. Disabled Fields

<button>I'm all right</button>
<button disabled>I'm out</button>
使node不能获得用户输入,通常会变灰。

4. The Form As a Whole

<form action="example/submit.html">
  Name: <input type="text" name="name"><br>
  Password: <input type="password" name="password"><br>
  <button type="submit">Log in</button>
</form>
<script>
  var form = document.querySelector("form");
  console.log(form.elements[1].type);
  // → password
  console.log(form.elements.password.type);
  // → password
  console.log(form.elements.name.form == form);
  // → true
</script>
说明:

  1. <form>中的每个field都有个属性叫form,指向<form>。

  2. <form>的属性elements,像一个Array,包含所有的fields。

  3. 可以通过数字索引,或者field的name来访问各个field。

  4. 当submit时,field的name作为querystring中的字段名。

  5. 点击type等于submit的button时,触发一个 "submit" 事件,"submit" 的默认行为是向server发送form中的数据。

  6. action属性的值,是submit的目标 url 。可以设置POST或GET属性。默认是GET。

例子,处理submit事件:

<form action="example/submit.html">
  Value: <input type="text" name="value">
  <button type="submit">Save</button>
</form>
<script>
  var form = document.querySelector("form");
  form.addEventListener("submit", function(event) {
    console.log("Saving value", form.elements.value.value);
    event.preventDefault();
  });
</script>
event.preventDefault() 阻止向server发送请求,相当于中断了submit的过程。对用户输入做本地验证时,会用到这个方法。如果用户输入的数据格式不合法,本地验证失败,不必再向server发送请求。

5. Text Fields

有三种text fields:

  1. <input type="text">

  2. <input type="password">

  3. <textarea></textarea>

它们的value属性,存储了编辑框中的字符串。

当用户选择了编辑框中的部分内容时,selectionStart是起始位置, selectionEnd是选择的结束位置。如果没有选择,这两个属性的值相等,都等于当前光标所在位置。

<textarea></textarea>
<script>
  var textarea = document.querySelector("textarea");
  textarea.addEventListener("keydown", function(event) {
    // The key code for F2 happens to be 113
    if (event.keyCode == 113) {
      replaceSelection(textarea, "Khasekhemwy");
      event.preventDefault();
    }
  });
  function replaceSelection(field, word) {
    var from = field.selectionStart, to = field.selectionEnd;
    field.value = field.value.slice(0, from) + word +
                  field.value.slice(to);
    // Put the cursor after the word
    field.selectionStart = field.selectionEnd =
      from + word.length;
  }
</script>
当点击F12时,把选中的字符串替换成 "Khasekhemwy" ,如果没有选中字符串,则在光标位置插入。

注意,F12键在不同的系统keyCode可能不一样。我的机器上是123.

对于text field,用户每次输入/修改字符,并不会每次都触发 "change" 事件,当失去焦点时才会触发。每次输入会触发 "input" 事件。

<input type="text"> length: <span id="length">0</span>
<script>
  var text = document.querySelector("input");
  var output = document.querySelector("#length");
  text.addEventListener("input", function() {
    output.textContent = text.value.length;
  });
</script>
这段代码使用 input 事件,实时显示输入内容的长度。

6. Checkboxes

<input type="checkbox" id="purple">
<label for="purple">Make this page purple</label>
<script>
  var checkbox = document.querySelector("#purple");
  checkbox.addEventListener("change", function() {
    document.body.style.background =
      checkbox.checked ? "mediumpurple" : "";
  });
</script>
说明:

  1. checked 属性

  2. label,for属性等于对应<input> 的id,当点击这个label时,相应的<input>会获得焦点。

7. Radio Buttons

Color:
<input type="radio" name="color" value="mediumpurple"> Purple
<input type="radio" name="color" value="lightgreen"> Green
<input type="radio" name="color" value="lightblue"> Blue
<script>
  var buttons = document.getElementsByName("color");
  function setColor(event) {
    document.body.style.background = event.target.value;
  }
  for (var i = 0; i < buttons.length; i++)
    buttons[i].addEventListener("change", setColor);
</script>
说明:

  1. name 相同的radio成为一组,只有一个能选中。

  2. getElementsByName() 类似于getElementsByClass(),返回的集合不是Array类型,不能直接用forEach方法,所以,这里用普通的for循环遍历它。

8. Select Fields

<select multiple>
  <option>Pancakes</option>
  <option>Pudding</option>
  <option>Ice cream</option>
</select>

说明:

  1. <select> 没有multiple时,是个下拉列表(drop-down),只支持单选。有multiple时,是个普通列表。

  2. <select> 的size属性用于设定列表的高度,size=3 使得列表显示3行,想看到更多需要滚动。

  3. 每个<option>都有一个value属性,如果没有设置,则等于contentText,如上面的:Pancakes, Pudding,Ice cream。

  4. <select> 也有一个value属性,它等于当前选中的option的value。 当multiple时,<select> 的value不可用,因为,它只能存储一个option的value。

  5. <select> 有个options属性,类似于Array,存储了它包含的所有<option>。

  6. <option> 有个selected属性,当选中时为true。

看一个multiple的例子:

<select multiple>
  <option value="1">0001</option>
  <option value="2">0010</option>
  <option value="4">0100</option>
  <option value="8">1000</option>
</select> = <span id="output">0</span>
<script>
  var select = document.querySelector("select");
  var output = document.querySelector("#output");
  select.addEventListener("change", function() {
    var number = 0;
    for (var i = 0; i < select.options.length; i++) {
      var option = select.options[i];
      if (option.selected)
        number += Number(option.value);
    }
    output.textContent = number;
  });
</script>

9. File Fields

<input type="file">
<script>
  var input = document.querySelector("input");
  input.addEventListener("change", function() {
    if (input.files.length > 0) {
      var file = input.files[0];
      console.log("You chose", file.name);
      if (file.type)
        console.log("It has type", file.type);
    }
  });
</script>
前面的章节讲过,browser构建了一个sandbox,js代码无法访问本地文件。file field使之成为可能。当然,它的主要功能是用来向server上传文件。但,也可以用于为本地的 js 程序提供访问本地文件的支持。

说明:

  1. input.files ,为什么是files? 因为,这种input 有个multiple属性,可以上传多个文件。

  2. files 中存储的是Object,它有name, size, type 等属性。

读取文件内容:

<input type="file" multiple>
<script>
  var input = document.querySelector("input");
  input.addEventListener("change", function() {
    Array.prototype.forEach.call(input.files, function(file) {
      var reader = new FileReader();
      reader.addEventListener("load", function() {
        console.log("File", file.name, "starts with",
                    reader.result.slice(0, 20));
      });
      reader.readAsText(file);
    });
  });
</script>
FileReader 是js库提供的对象,专门用于读取文件内容。readAsText() 是个异步函数,它不会直接返回文件内容,需要绑定 "load" 事件。

用Promise方式处理异步读取文件:

<script src="../promise-7.0.4.js"></script>

<input type="file" multiple>
<script>
    var input = document.querySelector("input");
    input.addEventListener("change", function () {
        Array.prototype.forEach.call(input.files, function (file) {
            readFile(file).then(function (result) {
                console.log("File", file.name, "starts with", result.slice(0,20));
            }, function (error) {
                console.log("Error:" + error);
            });
        });
    });

    function readFile(file) {
        return new Promise(function(succeed, fail) {
            var reader = new FileReader();
            reader.addEventListener("load", function() {
                succeed(reader.result);
            });
            reader.addEventListener("error", function() {
                fail(reader.error);
            });
            reader.readAsText(file);
        });
    }

</script>

10. Storing Data Client-Side

localStorage 这是js提供的全局对象,可用于长期存储数据,关闭网页、关闭浏览器数据都不会丢失。

localStorage.setItem("username", "marijn");
console.log(localStorage.getItem("username"));
// → marijn
localStorage.removeItem("username");

说明:

  1. localStorage 用键/值对存储数据。 setItem(), getItem(), removeItem()。

  2. 不同域名的website有各自独立的localStorage对象,互不干扰。

  3. localStorage可存储的数据大小有限制,通常是几M。

看一个比较长的例子,做一个网页版的笔记本,数据存储在本地:

Notes: <select id="list"></select>
<button οnclick="addNote()">new</button><br>
<textarea id="currentnote" style="width: 100%; height: 10em">
</textarea>

<script>
  var list = document.querySelector("#list");
  function addToList(name) {
    var option = document.createElement("option");
    option.textContent = name;
    list.appendChild(option);
  }

  // Initialize the list from localStorage
  var notes = JSON.parse(localStorage.getItem("notes")) ||
              {"shopping list": ""};
  for (var name in notes)
    if (notes.hasOwnProperty(name))
      addToList(name);

  function saveToStorage() {
    localStorage.setItem("notes", JSON.stringify(notes));
  }

  var current = document.querySelector("#currentnote");
  current.value = notes[list.value];

  list.addEventListener("change", function() {
    current.value = notes[list.value];
  });
  current.addEventListener("change", function() {
    notes[list.value] = current.value;
    saveToStorage();
  });

  function addNote() {
    var name = prompt("Note name", "");
    if (!name) return;
    if (!notes.hasOwnProperty(name)) {
      notes[name] = "";
      addToList(name);
      saveToStorage();
    }
    list.value = name;
    current.value = notes[name];
  }
</script>

sessionStorage也是用来做本地存储的,用法和localStorage类似。当session关闭时,数据就会被清除。

11. Exercise: A JavaScript Workbench

执行 textarea中的js代码,显示返回值,或者错误信息。

<textarea id="code">return "hi";</textarea>
<button id="button">Run</button>
<pre id="output"></pre>

<script>
    document.querySelector("#button").addEventListener("click", function () {
        var code = document.querySelector("#code").value;
        var outputNode = document.querySelector("#output");
        try {
            var result = new Function(code)();
            outputNode.innerText = String(result);
        }
        catch (e) {
            outputNode.innerText = "Error: " + e;
        }
    });
</script>
关键在于 new Function(code)() , 这个方法在前面的章节讲过了。

12. Exercise: Autocompletion

<script src="../promise-7.0.4.js"></script>

<input type="text" id="field">
<div id="suggestions" style="cursor:pointer"></div>

<script>
    var terms = [];
    for (var name in window)
        terms.push(name);

    var textfield = document.querySelector("#field");
    var suggestions = document.querySelector("#suggestions");

    textfield.addEventListener("input", function () {
        var matching = terms.filter(function (term) {
            return term.indexOf(textfield.value) == 0;
        });

        suggestions.textContent = "";
        matching.slice(0, 20).forEach(function (term) {
            var node = document.createElement("div");
            node.textContent = term;
            node.addEventListener("click", function () {
                textfield.value = term;
                suggestions.textContent = "";
            });
            suggestions.appendChild(node);
        })
    })
</script>

13. Exercise: Conway's Game of Life

最后一个习题太复杂,以后有时间再做。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值