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
最后一个习题太复杂,以后有时间再做。