diff --git a/.github/workflows/wangdoc.yml b/.github/workflows/wangdoc.yml new file mode 100644 index 0000000..c3e5cbe --- /dev/null +++ b/.github/workflows/wangdoc.yml @@ -0,0 +1,36 @@ +name: JavaScript tutorial CI +on: + push: + branches: + - master + +jobs: + page-generator: + name: Generating pages + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 'latest' + - name: Install dependencies + run: npm install + - name: Build pages + run: npm run build + - name: Deploy to website + uses: JamesIves/github-pages-deploy-action@v4 + with: + git-config-name: wangdoc-bot + git-config-email: yifeng.ruan@gmail.com + repository-name: wangdoc/website + token: ${{ secrets.WANGDOC_BOT_TOKEN }} + branch: master # The branch the action should deploy to. + folder: dist # The folder the action should deploy. + target-folder: dist/javascript + clean: true # Automatically remove deleted files from the deploy branch + commit-message: update from JavaScript tutorial + diff --git a/.gitignore b/.gitignore index 744d17c..01a7204 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ node_modules/ dist/ -package-lock.json npm-debug.log +package-lock.json diff --git a/.travis.yml b/.travis.yml.bak similarity index 60% rename from .travis.yml rename to .travis.yml.bak index ead9a81..3ad5ed5 100644 --- a/.travis.yml +++ b/.travis.yml.bak @@ -1,11 +1,18 @@ language: node_js node_js: -- '8' +- 'node' branches: only: - master +install: +- npm ci +# keep the npm cache around to speed up installs +cache: + directories: + - "$HOME/.npm" + script: bash ./deploy.sh env: global: diff --git a/README.md b/README.md index 193c114..b3f09b2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ -本教程全面介绍 JavaScript 核心语法,从最简单的开始讲起,循序渐进、由浅入深,力求清晰易懂。所有章节都带有大量的代码实例,便于理解和模仿,可以用到实际项目中,即学即用。 +本教程全面介绍 JavaScript 核心语法,覆盖了 ES5 和 DOM 规范的所有内容。 + +内容上从最简单的讲起,循序渐进、由浅入深,力求清晰易懂。所有章节都带有大量的代码实例,便于理解和模仿,可以用到实际项目中,即学即用。 + +本教程适合初学者当作 JavaScript 语言入门教程,学完后就可以承担实际的网页开发工作,也适合当作日常使用的参考手册。 + +JavaScript 后续新增的 ES6 语法,请看[《ES6 标准入门教程》](https://wangdoc.com/es6/)。 -本教程适合初学者当作 JavaScript 语言的入门教程,也适合当作日常使用的参考手册。 diff --git a/chapters.yml b/chapters.yml index 71a865b..d15194b 100644 --- a/chapters.yml +++ b/chapters.yml @@ -1,6 +1,7 @@ -- introduction.md: 导论 -- history.md: 历史 -- grammar.md: 基本语法 +- basic/: 入门篇 +- basic/introduction.md: 导论 +- basic/history.md: 历史 +- basic/grammar.md: 基本语法 - types/: 数据类型 - types/general.md: 概述 - types/null-undefined-boolean.md: null,undefined 和布尔值 @@ -19,5 +20,73 @@ - features/conversion.md: 数据类型的转换 - features/error.md: 错误处理机制 - features/style.md: 编程风格 +- features/console.md: console 对象与控制台 - stdlib/: 标准库 - stdlib/object.md: Object 对象 +- stdlib/attributes.md: 属性描述对象 +- stdlib/array.md: Array 对象 +- stdlib/wrapper.md: 包装对象 +- stdlib/boolean.md: Boolean 对象 +- stdlib/number.md: Number 对象 +- stdlib/string.md: String 对象 +- stdlib/math.md: Math 对象 +- stdlib/date.md: Date 对象 +- stdlib/regexp.md: RegExp 对象 +- stdlib/json.md: JSON 对象 +- oop/: 面向对象编程 +- oop/new.md: 实例对象与 new 命令 +- oop/this.md: this 关键字 +- oop/prototype.md: 对象的继承 +- oop/object.md: Object 对象的相关方法 +- oop/strict.md: 严格模式 +- async/: 异步操作 +- async/general.md: 概述 +- async/timer.md: 定时器 +- async/promise.md: Promise 对象 +- dom/: DOM +- dom/general.md: 概述 +- dom/node.md: Node 接口 +- dom/nodelist.md: NodeList 接口,HTMLCollection 接口 +- dom/parentnode.md: ParentNode 接口,ChildNode 接口 +- dom/document.md: Document 节点 +- dom/element.md: Element 节点 +- dom/attributes.md: 属性的操作 +- dom/text.md: Text 节点和 DocumentFragment 节点 +- dom/css.md: CSS 操作 +- dom/mutationobserver.md: Mutation Observer API +- events/: 事件 +- events/eventtarget.md: EventTarget 接口 +- events/model.md: 事件模型 +- events/event.md: Event 对象 +- events/mouse.md: 鼠标事件 +- events/keyboard.md: 键盘事件 +- events/progress.md: 进度事件 +- events/form.md: 表单事件 +- events/touch.md: 触摸事件 +- events/drag.md: 拖拉事件 +- events/common.md: 其他常见事件 +- events/globaleventhandlers.md: GlobalEventHandlers 接口 +- bom/: 浏览器模型 +- bom/engine.md: 浏览器模型概述 +- bom/window.md: window 对象 +- bom/navigator.md: Navigator 对象,Screen 对象 +- bom/cookie.md: Cookie +- bom/xmlhttprequest.md: XMLHttpRequest 对象 +- bom/same-origin.md: 同源限制 +- bom/cors.md: CORS 通信 +- bom/storage.md: Storage 接口 +- bom/history.md: History 对象 +- bom/location.md: Location 对象,URL 对象,URLSearchParams 对象 +- bom/arraybuffer.md: ArrayBuffer 对象,Blob 对象 +- bom/file.md: File 对象,FileList 对象,FileReader 对象 +- bom/form.md: 表单,FormData 对象 +- bom/indexeddb.md: IndexedDB API +- bom/webworker.md: Web Worker +- elements/: 附录:网页元素接口 +- elements/a.md: +- elements/image.md: +- elements/form.md:
+- elements/input.md: +- elements/button.md:
+``` + +上面的事件属性代码只有一个语句。如果有多个语句,使用分号分隔即可。 + +### URL 协议 + +URL 支持`javascript:`协议,即在 URL 的位置写入代码,使用这个 URL 的时候就会执行 JavaScript 代码。 + +```html +点击 +``` + +浏览器的地址栏也可以执行`javascript:`协议。将`javascript:console.log('Hello')`放入地址栏,按回车键也会执行这段代码。 + +如果 JavaScript 代码返回一个字符串,浏览器就会新建一个文档,展示这个字符串的内容,原有文档的内容都会消失。 + +```html +点击 +``` + +上面代码中,用户点击链接以后,会打开一个新文档,里面有当前时间。 + +如果返回的不是字符串,那么浏览器不会新建文档,也不会跳转。 + +```javascript +点击 +``` + +上面代码中,用户点击链接后,网页不会跳转,只会在控制台显示当前时间。 + +`javascript:`协议的常见用途是书签脚本 Bookmarklet。由于浏览器的书签保存的是一个网址,所以`javascript:`网址也可以保存在里面,用户选择这个书签的时候,就会在当前页面执行这个脚本。为了防止书签替换掉当前文档,可以在脚本前加上`void`,或者在脚本最后加上`void 0`。 + +```html +点击 +点击 +``` + +上面这两种写法,点击链接后,执行代码都不会网页跳转。 + +## script 元素 + +### 工作原理 + +浏览器加载 JavaScript 脚本,主要通过` + + + +``` + +上面代码执行时会报错,因为此时`document.body`元素还未生成。 + +一种解决方法是设定`DOMContentLoaded`事件的回调函数。 + +```html + + + +``` + +上面代码中,指定`DOMContentLoaded`事件发生后,才开始执行相关代码。`DOMContentLoaded`事件只有在 DOM 结构生成之后才会触发。 + +另一种解决方法是,使用` +``` + +但是,如果将脚本放在页面底部,就可以完全按照正常的方式写,上面两种方式都不需要。 + +```html + + + + +``` + +如果有多个`script`标签,比如下面这样。 + +```html + + +``` + +浏览器会同时并行下载`a.js`和`b.js`,但是,执行时会保证先执行`a.js`,然后再执行`b.js`,即使后者先下载完成,也是如此。也就是说,脚本的执行顺序由它们在页面中的出现顺序决定,这是为了保证脚本之间的依赖关系不受到破坏。当然,加载这两个脚本都会产生“阻塞效应”,必须等到它们都加载完成,浏览器才会继续页面渲染。 + +解析和执行 CSS,也会产生阻塞。Firefox 浏览器会等到脚本前面的所有样式表,都下载并解析完,再执行脚本;Webkit则是一旦发现脚本引用了样式,就会暂停执行脚本,等到样式表下载并解析完,再恢复执行。 + +此外,对于来自同一个域名的资源,比如脚本文件、样式表文件、图片文件等,浏览器一般有限制,同时最多下载6~20个资源,即最多同时打开的 TCP 连接有限制,这是为了防止对服务器造成太大压力。如果是来自不同域名的资源,就没有这个限制。所以,通常把静态文件放在不同的域名之下,以加快下载速度。 + +### defer 属性 + +为了解决脚本文件下载阻塞网页渲染的问题,一个方法是对` + +``` + +上面代码中,只有等到 DOM 加载完成后,才会执行`a.js`和`b.js`。 + +`defer`属性的运行流程如下。 + +1. 浏览器开始解析 HTML 网页。 +2. 解析过程中,发现带有`defer`属性的` + +``` + +`async`属性的作用是,使用另一个进程下载脚本,下载时不会阻塞渲染。 + +1. 浏览器开始解析 HTML 网页。 +2. 解析过程中,发现带有`async`属性的`script`标签。 +3. 浏览器继续往下解析 HTML 网页,同时并行下载` +``` + +上面的`example.js`默认就是采用 HTTP 协议下载,如果要采用 HTTPS 协议下载,必需写明。 + +```html + +``` + +但是有时我们会希望,根据页面本身的协议来决定加载协议,这时可以采用下面的写法。 + +```html + +``` + +## 浏览器的组成 + +浏览器的核心是两部分:渲染引擎和 JavaScript 解释器(又称 JavaScript 引擎)。 + +### 渲染引擎 + +渲染引擎的主要作用是,将网页代码渲染为用户视觉可以感知的平面文档。 + +不同的浏览器有不同的渲染引擎。 + +- Firefox:Gecko 引擎 +- Safari:WebKit 引擎 +- Chrome:Blink 引擎 +- IE: Trident 引擎 +- Edge: EdgeHTML 引擎 + +渲染引擎处理网页,通常分成四个阶段。 + +1. 解析代码:HTML 代码解析为 DOM,CSS 代码解析为 CSSOM(CSS Object Model)。 +2. 对象合成:将 DOM 和 CSSOM 合成一棵渲染树(render tree)。 +3. 布局:计算出渲染树的布局(layout)。 +4. 绘制:将渲染树绘制到屏幕。 + +以上四步并非严格按顺序执行,往往第一步还没完成,第二步和第三步就已经开始了。所以,会看到这种情况:网页的 HTML 代码还没下载完,但浏览器已经显示出内容了。 + +### 重流和重绘 + +渲染树转换为网页布局,称为“布局流”(flow);布局显示到页面的这个过程,称为“绘制”(paint)。它们都具有阻塞效应,并且会耗费很多时间和计算资源。 + +页面生成以后,脚本操作和样式表操作,都会触发“重流”(reflow)和“重绘”(repaint)。用户的互动也会触发重流和重绘,比如设置了鼠标悬停(`a:hover`)效果、页面滚动、在输入框中输入文本、改变窗口大小等等。 + +重流和重绘并不一定一起发生,重流必然导致重绘,重绘不一定需要重流。比如改变元素颜色,只会导致重绘,而不会导致重流;改变元素的布局,则会导致重绘和重流。 + +大多数情况下,浏览器会智能判断,将重流和重绘只限制到相关的子树上面,最小化所耗费的代价,而不会全局重新生成网页。 + +作为开发者,应该尽量设法降低重绘的次数和成本。比如,尽量不要变动高层的 DOM 元素,而以底层 DOM 元素的变动代替;再比如,重绘`table`布局和`flex`布局,开销都会比较大。 + +```javascript +var foo = document.getElementById('foobar'); + +foo.style.color = 'blue'; +foo.style.marginTop = '30px'; +``` + +上面的代码只会导致一次重绘,因为浏览器会累积 DOM 变动,然后一次性执行。 + +下面是一些优化技巧。 + +- 读取 DOM 或者写入 DOM,尽量写在一起,不要混杂。不要读取一个 DOM 节点,然后立刻写入,接着再读取一个 DOM 节点。 +- 缓存 DOM 信息。 +- 不要一项一项地改变样式,而是使用 CSS class 一次性改变样式。 +- 使用`documentFragment`操作 DOM +- 动画使用`absolute`定位或`fixed`定位,这样可以减少对其他元素的影响。 +- 只在必要时才显示隐藏元素。 +- 使用`window.requestAnimationFrame()`,因为它可以把代码推迟到下一次重绘之前执行,而不是立即要求页面重绘。 +- 使用虚拟 DOM(virtual DOM)库。 + +下面是一个`window.requestAnimationFrame()`对比效果的例子。 + +```javascript +// 重流代价高 +function doubleHeight(element) { + var currentHeight = element.clientHeight; + element.style.height = (currentHeight * 2) + 'px'; +} + +all_my_elements.forEach(doubleHeight); + +// 重绘代价低 +function doubleHeight(element) { + var currentHeight = element.clientHeight; + + window.requestAnimationFrame(function () { + element.style.height = (currentHeight * 2) + 'px'; + }); +} + +all_my_elements.forEach(doubleHeight); +``` + +上面的第一段代码,每读一次 DOM,就写入新的值,会造成不停的重排和重流。第二段代码把所有的写操作,都累积在一起,从而 DOM 代码变动的代价就最小化了。 + +### JavaScript 引擎 + +JavaScript 引擎的主要作用是,读取网页中的 JavaScript 代码,对其处理后运行。 + +JavaScript 是一种解释型语言,也就是说,它不需要编译,由解释器实时运行。这样的好处是运行和修改都比较方便,刷新页面就可以重新解释;缺点是每次运行都要调用解释器,系统开销较大,运行速度慢于编译型语言。 + +为了提高运行速度,目前的浏览器都将 JavaScript 进行一定程度的编译,生成类似字节码(bytecode)的中间代码,以提高运行速度。 + +早期,浏览器内部对 JavaScript 的处理过程如下: + +1. 读取代码,进行词法分析(Lexical analysis),将代码分解成词元(token)。 +2. 对词元进行语法分析(parsing),将代码整理成“语法树”(syntax tree)。 +3. 使用“翻译器”(translator),将代码转为字节码(bytecode)。 +4. 使用“字节码解释器”(bytecode interpreter),将字节码转为机器码。 + +逐行解释将字节码转为机器码,是很低效的。为了提高运行速度,现代浏览器改为采用“即时编译”(Just In Time compiler,缩写 JIT),即字节码只在运行时编译,用到哪一行就编译哪一行,并且把编译结果缓存(inline cache)。通常,一个程序被经常用到的,只是其中一小部分代码,有了缓存的编译结果,整个程序的运行速度就会显著提升。 + +字节码不能直接运行,而是运行在一个虚拟机(Virtual Machine)之上,一般也把虚拟机称为 JavaScript 引擎。并非所有的 JavaScript 虚拟机运行时都有字节码,有的 JavaScript 虚拟机基于源码,即只要有可能,就通过 JIT(just in time)编译器直接把源码编译成机器码运行,省略字节码步骤。这一点与其他采用虚拟机(比如 Java)的语言不尽相同。这样做的目的,是为了尽可能地优化代码、提高性能。下面是目前最常见的一些 JavaScript 虚拟机: + +- [Chakra](https://en.wikipedia.org/wiki/Chakra_(JScript_engine)) (Microsoft Internet Explorer) +- [Nitro/JavaScript Core](http://en.wikipedia.org/wiki/WebKit#JavaScriptCore) (Safari) +- [Carakan](http://dev.opera.com/articles/view/labs-carakan/) (Opera) +- [SpiderMonkey](https://developer.mozilla.org/en-US/docs/SpiderMonkey) (Firefox) +- [V8](https://en.wikipedia.org/wiki/Chrome_V8) (Chrome, Chromium) + +## 参考链接 + +- John Dalziel, [The race for speed part 2: How JavaScript compilers work](http://creativejs.com/2013/06/the-race-for-speed-part-2-how-javascript-compilers-work/) +- Jake Archibald, [Deep dive into the murky waters of script loading](http://www.html5rocks.com/en/tutorials/speed/script-loading/) +- Mozilla Developer Network, [window.setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/window.setTimeout) +- Remy Sharp, [Throttling function calls](http://remysharp.com/2010/07/21/throttling-function-calls/) +- Ayman Farhat, [An alternative to JavaScript's evil setInterval](http://www.thecodeship.com/web-development/alternative-to-javascript-evil-setinterval/) +- Ilya Grigorik, [Script-injected "async scripts" considered harmful](https://www.igvita.com/2014/05/20/script-injected-async-scripts-considered-harmful/) +- Axel Rauschmayer, [ECMAScript 6 promises (1/2): foundations](http://www.2ality.com/2014/09/es6-promises-foundations.html) +- Daniel Imms, [async vs defer attributes](http://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html) +- Craig Buckler, [Load Non-blocking JavaScript with HTML5 Async and Defer](http://www.sitepoint.com/non-blocking-async-defer/) +- Domenico De Felice, [How browsers work](https://domenicodefelice.blogspot.com/2015/08/how-browsers-work.html) diff --git a/docs/bom/file.md b/docs/bom/file.md new file mode 100644 index 0000000..d44fcb1 --- /dev/null +++ b/docs/bom/file.md @@ -0,0 +1,164 @@ +# File 对象,FileList 对象,FileReader 对象 + +## File 对象 + +File 对象代表一个文件,用来读写文件信息。它继承了 Blob 对象,或者说是一种特殊的 Blob 对象,所有可以使用 Blob 对象的场合都可以使用它。 + +最常见的使用场合是表单的文件上传控件(``),用户选中文件以后,浏览器就会生成一个数组,里面是每一个用户选中的文件,它们都是 File 实例对象。 + +```javascript +// HTML 代码如下 +// +var file = document.getElementById('fileItem').files[0]; +file instanceof File // true +``` + +上面代码中,`file`是用户选中的第一个文件,它是 File 的实例。 + +### 构造函数 + +浏览器原生提供一个`File()`构造函数,用来生成 File 实例对象。 + +```javascript +new File(array, name [, options]) +``` + +`File()`构造函数接受三个参数。 + +- array:一个数组,成员可以是二进制对象或字符串,表示文件的内容。 +- name:字符串,表示文件名或文件路径。 +- options:配置对象,设置实例的属性。该参数可选。 + +第三个参数配置对象,可以设置两个属性。 + +- type:字符串,表示实例对象的 MIME 类型,默认值为空字符串。 +- lastModified:时间戳,表示上次修改的时间,默认为`Date.now()`。 + +下面是一个例子。 + +```javascript +var file = new File( + ['foo'], + 'foo.txt', + { + type: 'text/plain', + } +); +``` + +### 实例属性和实例方法 + +File 对象有以下实例属性。 + +- File.lastModified:最后修改时间 +- File.name:文件名或文件路径 +- File.size:文件大小(单位字节) +- File.type:文件的 MIME 类型 + +```javascript +var myFile = new File([], 'file.bin', { + lastModified: new Date(2018, 1, 1), +}); +myFile.lastModified // 1517414400000 +myFile.name // "file.bin" +myFile.size // 0 +myFile.type // "" +``` + +上面代码中,由于`myFile`的内容为空,也没有设置 MIME 类型,所以`size`属性等于0,`type`属性等于空字符串。 + +File 对象没有自己的实例方法,由于继承了 Blob 对象,因此可以使用 Blob 的实例方法`slice()`。 + +## FileList 对象 + +`FileList`对象是一个类似数组的对象,代表一组选中的文件,每个成员都是一个 File 实例。它主要出现在两个场合。 + +- 文件控件节点(``)的`files`属性,返回一个 FileList 实例。 +- 拖拉一组文件时,目标区的`DataTransfer.files`属性,返回一个 FileList 实例。 + +```javascript +// HTML 代码如下 +// +var files = document.getElementById('fileItem').files; +files instanceof FileList // true +``` + +上面代码中,文件控件的`files`属性是一个 FileList 实例。 + +FileList 的实例属性主要是`length`,表示包含多少个文件。 + +FileList 的实例方法主要是`item()`,用来返回指定位置的实例。它接受一个整数作为参数,表示位置的序号(从零开始)。但是,由于 FileList 的实例是一个类似数组的对象,可以直接用方括号运算符,即`myFileList[0]`等同于`myFileList.item(0)`,所以一般用不到`item()`方法。 + +## FileReader 对象 + +FileReader 对象用于读取 File 对象或 Blob 对象所包含的文件内容。 + +浏览器原生提供一个`FileReader`构造函数,用来生成 FileReader 实例。 + +```javascript +var reader = new FileReader(); +``` + +FileReader 有以下的实例属性。 + +- FileReader.error:读取文件时产生的错误对象 +- FileReader.readyState:整数,表示读取文件时的当前状态。一共有三种可能的状态,`0`表示尚未加载任何数据,`1`表示数据正在加载,`2`表示加载完成。 +- FileReader.result:读取完成后的文件内容,有可能是字符串,也可能是一个 ArrayBuffer 实例。 +- FileReader.onabort:`abort`事件(用户终止读取操作)的监听函数。 +- FileReader.onerror:`error`事件(读取错误)的监听函数。 +- FileReader.onload:`load`事件(读取操作完成)的监听函数,通常在这个函数里面使用`result`属性,拿到文件内容。 +- FileReader.onloadstart:`loadstart`事件(读取操作开始)的监听函数。 +- FileReader.onloadend:`loadend`事件(读取操作结束)的监听函数。 +- FileReader.onprogress:`progress`事件(读取操作进行中)的监听函数。 + +下面是监听`load`事件的一个例子。 + +```javascript +// HTML 代码如下 +// + +function onChange(event) { + var file = event.target.files[0]; + var reader = new FileReader(); + reader.onload = function (event) { + console.log(event.target.result) + }; + + reader.readAsText(file); +} +``` + +上面代码中,每当文件控件发生变化,就尝试读取第一个文件。如果读取成功(`load`事件发生),就打印出文件内容。 + +FileReader 有以下实例方法。 + +- FileReader.abort():终止读取操作,`readyState`属性将变成`2`。 +- FileReader.readAsArrayBuffer():以 ArrayBuffer 的格式读取文件,读取完成后`result`属性将返回一个 ArrayBuffer 实例。 +- FileReader.readAsBinaryString():读取完成后,`result`属性将返回原始的二进制字符串。 +- FileReader.readAsDataURL():读取完成后,`result`属性将返回一个 Data URL 格式(Base64 编码)的字符串,代表文件内容。对于图片文件,这个字符串可以用于``元素的`src`属性。注意,这个字符串不能直接进行 Base64 解码,必须把前缀`data:*/*;base64,`从字符串里删除以后,再进行解码。 +- FileReader.readAsText():读取完成后,`result`属性将返回文件内容的文本字符串。该方法的第一个参数是代表文件的 Blob 实例,第二个参数是可选的,表示文本编码,默认为 UTF-8。 + +下面是一个例子。 + +```javascript +/* HTML 代码如下 + + +*/ + +function previewFile() { + var preview = document.querySelector('img'); + var file = document.querySelector('input[type=file]').files[0]; + var reader = new FileReader(); + + reader.addEventListener('load', function () { + preview.src = reader.result; + }, false); + + if (file) { + reader.readAsDataURL(file); + } +} +``` + +上面代码中,用户选中图片文件以后,脚本会自动读取文件内容,然后作为一个 Data URL 赋值给``元素的`src`属性,从而把图片展示出来。 diff --git a/docs/bom/form.md b/docs/bom/form.md new file mode 100644 index 0000000..7b71500 --- /dev/null +++ b/docs/bom/form.md @@ -0,0 +1,626 @@ +# 表单,FormData 对象 + +## 表单概述 + +表单(``)用来收集用户提交的数据,发送到服务器。比如,用户提交用户名和密码,让服务器验证,就要通过表单。表单提供多种控件,让开发者使用,具体的控件种类和用法请参考 HTML 语言的教程。本章主要介绍 JavaScript 与表单的交互。 + +```html + +
+ + +
+
+ + +
+
+ +
+ +``` + +上面代码就是一个简单的表单,包含三个控件:用户名输入框、密码输入框和提交按钮。 + +用户点击“提交”按钮,每一个控件都会生成一个键值对,键名是控件的`name`属性,键值是控件的`value`属性,键名和键值之间由等号连接。比如,用户名输入框的`name`属性是`user_name`,`value`属性是用户输入的值,假定是“张三”,提交到服务器的时候,就会生成一个键值对`user_name=张三`。 + +所有的键值对都会提交到服务器。但是,提交的数据格式跟`
`元素的`method`属性有关。该属性指定了提交数据的 HTTP 方法。如果是 GET 方法,所有键值对会以 URL 的查询字符串形式,提交到服务器,比如`/handling-page?user_name=张三&user_passwd=123&submit_button=提交`。下面就是 GET 请求的 HTTP 头信息。 + +```http +GET /handling-page?user_name=张三&user_passwd=123&submit_button=提交 +Host: example.com +``` + +如果是 POST 方法,所有键值对会连接成一行,作为 HTTP 请求的数据体发送到服务器,比如`user_name=张三&user_passwd=123&submit_button=提交`。下面就是 POST 请求的头信息。 + +```http +POST /handling-page HTTP/1.1 +Host: example.com +Content-Type: application/x-www-form-urlencoded +Content-Length: 74 + +user_name=张三&user_passwd=123&submit_button=提交 +``` + +注意,实际提交的时候,只要键值不是 URL 的合法字符(比如汉字“张三”和“提交”),浏览器会自动对其进行编码。 + +点击`submit`控件,就可以提交表单。 + +```html + + +
+``` + +上面表单就包含一个`submit`控件,点击这个控件,浏览器就会把表单数据向服务器提交。 + +注意,表单里面的` + +``` + +上面表单的`