Lesson 24: Web forms
表单提交
表单通常包括一个提交按钮,它实际上只是另一个表单控件。当单击提交按钮时,它指示用户代理(浏览器)提交表单。提交意味着浏览器向表单元素的action属性中给定的URL发出新的HTTP请求,并将所有表单数据沿着发送。服务器将处理请求并返回一些HTTP响应。然后浏览器显示这个响应,就像它显示任何其他响应一样:它加载一个新页面,其中包含响应正文作为内容(假设服务器通过发送HTML进行响应)。
Web表单支持GET和POST。要使用的方法在表单的方法属性中指定。(If属性缺失,默认为GET)。
在GET请求中,表单数据作为URL的查询部分发送到服务器。如果我在上面的例子中的表单的文本小部件中添加“Harold Fellermann”,浏览器将发送HTTP请求:
GET submit.php?username=Harold%20Fellermann HTTP/1.1
到服务器。请注意表单数据是如何作为(URL转义的)明文包含到查询字符串中的。对于密码等敏感数据,永远不应该这样做!因为浏览器使用GET,所以也假设请求对资源的状态没有任何影响,也就是说它不会改变服务器上的任何状态(GET是安全的)。表单也可以多次提交而没有任何副作用(GET是幂等的)。
在POST请求中,表单数据在HTTP请求的主体中发送。如果上面的示例在form元素中使用method=“POST”,则HTTP请求将改为
POST submit.php HTTP/1.1 name=Harold%20Fellermann
表单组织
组织表单的标签/属性
产品<fieldset>用途<legend>,<label>
- 加上id属性
提供块级结构
- 减少内部所需的标记
例如<p><div>等
为用户提供视觉提示
- 具有描述性名称的逻辑块
增强的可用性/可访问性
- 将标签与控件关联
- 增加“命中面积!“在小控件上
GET方法
当用户提交表单时,浏览器会生成一个处理URL的HTTP GET请求
- 成功的表单数据作为查询字符串附加到URL
- 处理脚本将需要在查询字符串中查找,提取数据并使用
POST方法
使用method=“post”浏览器进行HTTP POST请求处理URL
- 用于将数据携带到服务器的具体方法,请求
- 以相同格式但在请求的不同部分发送的表单数据消息
- 处理脚本将需要在查询字符串中查找,提取数据并使用它
GET | POST |
Default method (used if no specific method supplied)) | Needs to be explicitly invoked via a form or script |
Data submitted (including passwords!) visible in the querystring | Submitted data not (easily) viewable as it sent in the request body |
Results page can be bookmarked as data to re-run submission is stored in URL | Results pages cannot be bookmarked or returned to later as POST data is not stored |
Refreshing a result page will repeat the submission with no warning | User warned (by browser) before repeat action |
Not suitable for large amount of data as URL is often truncated by browser | Can handle large amounts of data as post body can be any size |
Good for searches and simple applications where repeat submission not a problem | Always use for login/password submissions |
处理表单数据
一旦用户与表单交互,结果必须处理
通常包括三个阶段:
1.验证
- 检查表格是否填写完整,类型是否正确输入的数据
有助于确保提交处理的数据
完整和一致性(必填字段等)格式和类型
可在客户端使用JavaScript完成
- 快速、高效、更好的可用性
- 可被恶意用户使用/禁用
可通过Web服务器应用程序在服务器端完成安装
- 坚固耐用,可能更强大,但速度可能更慢
- 如果操作正确,解决很慢
对于数据敏感型应用程序,最好在终端都进行数据交换
- 具体的数据库格式
- 消除恶意输入等。
2.处理
- 提取提交的数据并对其做一些有用的事情
提交的数据通常通过Web服务器传递到服务器端脚本应用程序
- 通常通过CGI(公共网关接口)
脚本应用程序可能是许多应用程序之一
- PHP,Perl,Python
- ASP和/或.NET
- Java
- 等等
应用程序可能会执行一些验证,则功能取决于手头的任务...
3.反馈
- (可选)返回给用户一些东西
Lesson 25: Javascript
JavaScript --官方名称为ECMAScript--是一种编程语言,大多数现代浏览器都支持它,以便让网站提供更丰富的用户交互方式。传统上在程序员中名声不好(主要是因为浏览器供应商之间的不兼容性,现在已经被克服了),它现在代表了一种美丽的语言,并跻身于最流行的编程语言之列。在被创建为仅在浏览器中运行之后,现在有了独立的JavaScript引擎(最重要的是Node.js链接到外部网站)。可以本地运行JavaScript应用程序,例如用于服务器端脚本。
JavaScript是一种在客户端计算机上的Web浏览器
- 浏览器有一个JavaScript解释器,是脚本执行环境
XML JavaScript嵌入在XHTML中
- 即要运行的代码随网页一起到达
JavaScript JavaScript是一种具有C风格语法的命令式语言
- 具有动态类型和运行时评估
与Java的唯一联系是名称和核心C-样式语法...
与用户行为的交互
- 鼠标移动、按键等。
与浏览器环境的交互
- 浏览器驱动的事件
- 浏览器类型驱动的行为
互动可以是:
- 验证表单数据
- 创建和控制浏览器窗口
- 动态风格/演示效果
- 动态内容创建/包含
- 异步数据采集
Lesson 26: Javascript & the HTML DOM
文档对象模型(DOM)提供了一种编程方式来从JavaScript与HTML文档进行交互:它允许脚本评估和修改任何页面内容以及浏览器窗口。对DOM节点的任何更改都将立即显示在浏览器窗口中。
DOM是页面内容的分层表示。DOM的根节点是window,它是浏览器窗口的编程表示。例如,window的子元素是window.location(窗口的当前URL)和window.document(当前HTML文档)--后者也直接通过全局变量document暴露给JavaScript。JavaScript行
console.log(document)
将在开发人员工具控制台中显示整个HTML文档的编程表示。
为了选择特定的DOM节点,标准提供了以下访问函数
document.getElementByID('sec_history') /* select the DOM element specified via its ID */ document.getElementsByTagName('a') /* select all elements of the give type (watch out for the plural form in the function name) */ document.getElementsByClassName('external') /* select elements that declare the given class */ document.querySelector('a.external') /* select first element that matches given CSS selector */ document.querySelectorAll('#sec_history a.external') /* select all elements that match given CSS selector */
许多浏览器支持较旧的、现在已弃用的访问DOM节点的方法,例如ass document.NAME、document.all、document. forms。别用那个!
一旦你选择了一个DOM节点,你可以几乎没有限制地改变它的状态:
let new-item = document.createElement('li'); let text = document.createTextNode('a new list item'); new-item.appendChild(text); document.getElementById('result-list').appendChild(new-item);
DOM:原则
DOM =(Document Object Model)文档对象模型
- 文档的语言/平台中立界面
- 定义文档对象,它们之间的关系和它们的方法和性质
HTML DOM描述HTML文档,包含它们的浏览器窗口
允许访问文档的任何部分
- 作为预定义(由标准/浏览器)对象
- 通过分层文档树中的位置
- 作为作者定义的对象(使用id属性)
XHTML DOM:基本层次结构
浏览器窗口(或框架)是窗口对象
- 为其他对象提供全局上下文。
当前网页由文档表示对象
- 所有HTML元素都是文档的组成部分
DOM:访问和寻址
通过一系列方法访问的CNODOM节点
- -有些比其他更具体
- 有些限制性更强
最标准化的W3C DOM 1.0,但...
....历史上,浏览器开发了不同的DOM(s)
- Netscape(层)-旧的,所以完全忽略
- Internet Explorer(所有)-现在不太重要(但测试有用)
- W3C(ID)DOM -在大多数浏览器中使用的标准
DOM:通过名称访问
最初按名称分层引用的对象
问题
- 你得知道等级制度
- 在form以外的元素中使用已弃用的name属性控件和链接(不能用于严格的HTML)
- 不适用于所有页面元素
DOM:通过内置访问对象集合
浏览器为某些DOM对象构建预定义集合
- 按来源顺序填写,通过文档访问
- 名称的必要性
DOM:直接访问
使用通用地址语法和文档方法访问文档中的任何对象
- 通过其DOM位置或显式地通过类型/id等
Internet Explorer(All)DOM是早期的实现(自IE 4/5+起)
- 在当前版本中仍然支持
使用名为all的集合来定位对象id
document.all['thisObjectId'].property = value
IE 6+支持W3C DOM
- V有用的,能够测试document.all
DOM:W3C DOM访问
DOM 1.0是一个已建立的Web标准
使用一系列方法访问文档对象
支持所有现代浏览器
- 有点怪癖!
重要的是要知道什么是它返回什么和你可以做什么
- String?Array/Collection?/Another object?
DOM:简单功能检测
通常是个好主意!
- 支持W3C DOM的一些怪癖
特征感应不是浏览器检测(最好的情况下不可靠!)
- 查找预期DOM的已知/所需功能,然后使用浏览器特定语法(如果需要)
DOM:脚本访问
JavaScript JavaScript通过DOM访问页面对象
文档对象内部的Web页面组件将有...
- A类型(单选按钮radio button、链接link等)
- 属性properites(读/写read/write,单个值single values)
...而且可能
- 集合Collections(子对象of sub objects)(例如<option>,<select>对象)
- 方法(在它们发生时调用或处理)
使用点运算符(.)访问对象
启动流程的各种方式
- 解释性地识别对象,从集合中评估
DOM:标准属性,方法和集合
页面中的所有HTMLElement对象都支持一个集合,基本属性、方法和集合
Lesson 27: DOM Events
像Java这样的编程语言带有一个“main”函数,它是程序的主要入口点。这样的主函数通常会调用其他函数来执行其目的。退出主函数将终止程序。
JavaScript本身没有main函数。相反,您可以将浏览器视为为您提供主要功能。可以肯定的是,任何写在<script>HTML文档头部或主体中的岛中的代码都将从第一行执行到最后一行。但你的计划并不需要在那之后终止。
function popupNotice() { window.alert("This code keeps running"); }; window.setInterval(popupNotice, 60*1000);
最后一行要求浏览器每60秒运行一次作为第一个参数提供的函数。它将继续这样做,即使脚本内容已经执行了浏览器窗口打开的时间。
偶数驱动编程
一种非常强大的编程风格是提供在页面上出现特定条件(称为事件)时执行的代码,而不是定期执行代码。例如,一旦用户单击链接或提交表单,就会执行一段JavaScript。我们不是编写一个单一的程序,而是设计一些小的功能,即所谓的事件处理程序或事件侦听器,来处理特定的事件。这种编码风格是GUI编程的典型风格。其中GUI框架提供了一个主要功能,该功能观察用户界面的变化,并在发生诸如用户动作之类的特定事件时将执行移交给应用程序代码。在我们的例子中,主函数是一个浏览器,我们可以告诉它我们想要监听一些事件。在符合现代标准的HTML+JavaScript中,有三种主要方法可以做到这一点:
function sayThanks(event) { window.alert("Thanks for clicking me"); } let button = document.getElementByID("click-me-button"); button.onclick = sayThanks;
事件侦听器的通用附件
之前解决方案的另一种方法是调用任何HTML DOM元素的泛型方法addEventEvent,并将事件的名称作为参数:
let button = document.getElementByID("click-me-button"); button.addEventListener('click', sayThanks);
完全分离关注点
这种方法很好,但是它要求我们显式地将每个HTML元素与它的JavaScript侦听器关联起来。通常情况下,这已经足够好了,但是如果您有许多元素都需要相同的事件侦听器(例如,一个按钮出现在一个冗长的表的每一行中),它可能会变得乏味。它还要求HTML内容作者了解JavaScript,以便将某些行为与内容相关联。我们可以走得更远。考虑以下HTML:
<button class="onclick-sayThanks">Click me!</button>
它根本不包含任何JavaScript。然后,外部JavaScript文件可以使用以下元素选择器将事件侦听器挂钩到任何此类链接:
let elements = document.getElementsByClassName("onclick-sayThanks"); for(let i=0; i<elements.length; i++) { elements[i].addEventListener('click', sayThanks); });
在这种方法中,内容作者不需要知道<button>通过JavaScript获得了一些功能增强。对于他所关心的一切,他把内容注释为属于一个特殊的阶级。这个带注释的元素是通过CSS还是JavaScript进行扩充,内容作者并不关心。
DOMContentLoaded
除了上述第一种解决方案之外,所有解决方案都依赖于在执行脚本之前完全加载内容。否则,脚本会尝试修改尚未创建的DOM元素。我们如何确保脚本只在页面设置好后运行?同样,我们有三个选择:
将任何<script>依赖于正在加载的内容的元素放在页面底部<body>。只有在页面完全构造完成后,才会加载脚本。虽然在过去是一种广泛的方法,但现在有更好的方法来获得相同的行为和脚本<body>。毕竟,它们真的不是视觉页面内容的一部分,所以应该在语义上进入<head>部分。
使用加载任何外部脚本,<script src="..." defer>以确保仅在生成页面时执行这些脚本。这当然是可行的,但是如果有其他脚本化的操作不依赖于DOM的完全构建,则可能太过暴力。
将所有初始化代码放入DOMContentLoaded事件的事件侦听器中。一旦DOM完全构造完成,将执行此侦听器中的代码,并且仅执行此侦听器中的代码。使用这种方法的插件最好<script src="..." async>在<head>HTML文档的部分中加载。
上面的函数,在DOM准备好进行操作时执行:
document.addEventListener('DOMContentLoaded', function(event) { /* once the DOM is built, we search for elements that want listeners */ let elements = document.getElementsByClassName("onclick-sayThanks"); for(let i=0; i<elements.length; i++) { elements[i].addEventListener('click', sayThanks); }); });
DOM:事件event
用户或浏览器启动
使用事件处理程序检测到的错误
- 与HTML内联
- 在脚本中注册
可通过编程方式调用事件
- 使用对象方法,例如
document.getElementById('thisBox').focus()
...会导致一个焦点focus事件,由...
<input type=“text”name=“user”id=“thisBox”οnfοcus="runThis();" />
DOM:简单(内联)事件处理程序
DOM:事件处理程序注册
事件处理程序可以在脚本中注册
- 不需要HTML事件处理程序属性
- 实现此目的的几种方法,与DOM的演变有关
- 可能会让人感到困惑(即准备进入某些浏览器差异)
DOM:准备好了吗?
一旦页面完成,就会触发脚本运行
在屏幕上呈现(使用onLoad),以确保所需对象实际存在
然而,脚本访问DOM可以发生在页面呈现在屏幕上
DOM:检测“DOM准备就绪”
标准方法是添加一个事件
DOMContentLoaded的侦听器文档发生的事件
document.addEventListener("DOMContentLoaded", doSomething,false)
不幸的是,IE不支持此功能
- addEventEvent和检测
关于addEventListener和DOMContentLoaded的检测
解决方法是让IE加载一个(虚拟)脚本文件,并告诉你什么时候发生了什么
Lesson 28: Asynchronous Requests via AJAX
在2008年左右,这种传统的客户端-服务器交互方式得到了一种机制的补充,该机制允许脚本语言直接发送HTTP请求并等待和处理其响应。这项技术以首字母缩略词AJAX(“异步JavaScript和XML”的缩写)而闻名,并由称为XMLHttpRequest链接到外部站点的浏览器API提供支持。
XMLHttpRequest允许JavaScript编写HTTP请求并将其发送到Web服务器。一旦服务器响应此请求,API将发出一个事件,脚本可以监听该事件以处理服务器的响应数据。下面是一个简短的演示,演示如何使用它通过GET请求从服务器获取Web资源,并将服务器响应转储到浏览器控制台:
var request = new XMLHttpRequest(); request.addEventListener("load", function(event) { console.log(request.response); }); request.open("GET", url); request.send();
第一行是一个新的XMLHttpRequest对象。第2行到第4行附加了一个事件处理程序,它只是将响应打印到控制台。在第6行,我们打开一个到服务器的连接,这里声明对变量url中声明的URL发出GET请求。最后,最后一行发送组装后的请求。
请注意,任何超出此点的脚本行都将在此之后立即执行。JavaScript解释器不等待请求被满足(因此称为异步)。相反,浏览器在收到响应的任何时刻都会通过触发“load”事件来通知脚本响应已经准备好。
Lesson29: Object-Oriented Javascript
JavaScript对象
JavaScript是一种面向对象的语言。JavaScript中的一切都是对象。虽然像Java和C++这样的语言使用不同的类型,比如int、float、函数--它们与对象不同,但JavaScript没有这样的区别:一切都是对象。正如上一课中提到的,即使函数也是可以传递的对象,例如作为其他函数的参数。
JavaScript中的对象本质上是关联数组:它们形成一个数据存储,让您将键与值关联起来。与允许通过整数索引访问值的常规数组相反,关联数组中的键是字符串。在面向对象编程的术语中,关联数组的键也称为对象的属性。让我们看看这是如何工作的。
在JavaScript中创建对象的最简单方法是所谓的对象字面量。
let joe = { givenName: "Joe", lastName: "Biden", age: 78 }
这段代码创建了一个对象,并将其存储在变量person中。对象本身有三个属性:firstName、lastName和age。
删除、修改和删除属性
为了访问对象的属性,JavaScript提供了两种可能性,它们在语法上不同,但具有完全相同的语义,也就是说它们在浏览器上的行为和效果是相同的:
console.log(joe["givenName"]);
and
console.log(joe.givenName);
第一种语法强调对象是关联数组,其中键/值查找通常用方括号执行(认为listValue =myList[42])。第二种方法是许多面向对象语言的既定语法。
类似地,可以使用任一语法设置或赋予对象属性一个新值
joe["givenName"] = "Joseph Robinette"; joe.occupation = "president"
请注意,第二行创建了对象的一个新属性,该属性在创建对象时并不存在。在Java中,这样的代码会产生编译时错误。在JavaScript中,它是完全正常的代码。读取对象的未定义属性甚至不是错误,如以下控制台会话所示:
> joe.income <- undefined
属性甚至可以从对象中完全删除:
> delete joe.lastName > joe <- {givenName: 'Joseph Robinette', age: 78, occupation: 'president'}
方法和属性
前面已经提到,JavaScript中的所有东西都是对象,可以用作值。这允许我们使用函数作为对象的值。(In在面向对象的编程术语中,这样的函数被称为方法)。下面的代码对此进行了说明:
joe.greet = function(adressee) { let greeting = adressee || "fellow citizen"; window.alert("Hello there, " + greeting + "!"); }
在这里,一个匿名函数被分配给属性greet。要在创建对象时定义一个方法,而不是将其分配给一个已经存在的对象,我们可以使用以下简短的表示法:
let joe = { givenName: "Joe", lastName: "Biden", age: 78, greet(adressee) { let greeting = adressee || "fellow citizen"; window.alert("Hello there, " + greeting + "!"); } }
调用对象的方法将在请求程序中问候用户:
joe.greet();
joe.greet("poor fellow");
对象、类和属性
JavaScript一直是一种面向对象的语言,但在其历史的很长一段时间里,它并没有使用类!相反,JavaScript是建立在对象和所谓的原型之上的。在基于类的语言(如Java)中,您习惯于使用类构造函数编写类以创建新对象。在传统的JavaScript中,情况并非如此:
function createPerson(name, age) { this.name = name; this.age = age; } let person = new createPerson("Joe", 78); console.log(person.name); // will print "Joe"
正如你所看到的,任何函数都可以用new关键字调用,它将充当一个新对象的构造函数。解释器创建一个新的空关联数组,并在执行函数时将其绑定到this。通常,我们会期望函数在返回之前适当地初始化新创建的对象,以便它扮演类构造函数或工厂函数的角色。
这是JavaScript中一个有点奇怪的变量。与Java不同,在Java中,这只能发生在类方法中,在JavaScript中,这是到处定义的,但它并不总是计算为您可能假设的对象。例如,如果您只是在JavaScript控制台中键入this,它将计算为Window对象。在onclick等事件处理程序中,它指触发处理程序的事件。JavaScript运行时将自动将其绑定到一些有用的对象。如果你想将一个特定的对象绑定到一个函数,你可以使用JavaScript bind方法:
function greet() { window.alert("Hello there, my name is " + this.name + "."); } function createPerson(name, age) { this.name = name; this.age = age; this.greet = greet.bind(this); }
JavaScript类
自ECMAScript 6以来,JavaScript获得了方便的语法,允许人们以我们习惯的方式定义类:
class Person { constructor(name, age) { this.name = name; this.age = age; } greet(adressee) { let greeting = adressee || "fellow citizen"; window.alert("Hello there, " + greeting + "!"); } } let joe = new Person('Joe', 78);
Lesson 30: Promises
JavaScript的控制流通常是事件驱动的。JavaScript的各个片段(称为回调)在发生某些事件(如按钮单击)时等待浏览器执行它们。我们使用addEventEvent注册这些回调。方便的是,我们可以将回调作为事件侦听器作为匿名函数参数传递,在我们需要的地方定义函数:
document.getElementById("secret-button").addEventListener("click", function(event) { document.getElementById("secret").classList.toggle("visible"); });
let form1 = document.getElementById("form-donate"); form1.addEventListener("submit", function() { let req1 = new XMLHttpRequest(); req1.addEventListener("load", function replaceForm() { form1.outerHTML = req1.response; let form2 = document.getElementById("form-details"); form2.addEventListener("submit", function() { let req2 = new XMLHttpRequest(); req2.addEventListener("load", function acknowledge() { form2.outerHTML = req2.response; }); req2.addEventListener("error", window.alert); // [...] prepare form2Data req2.open("POST", form2.action); req2.send(form2Data); }); }); req1.addEventListener("error", window.alert); // [...] prepare form1Data req1.open("POST", form1.action); req1.send(form1Data); });
.如果我们能以某种方式编写如下代码,那么它会更清晰,在这里我们可以表达表单向导的顺序特性,尽管处理需要异步发生:
post(form1).then( // success function replaceForm(response) { form1.outerHTML = response; let form2 = document.getElementById("form-details"); return post(form2); }, // error window.alert ).then( function acknowledge(response) { let form2 = document.getElementById("form-details"); form2.outerHTML = response; }, // error window.alert );
输入Promises。Promise是一个表示异步操作的JavaScript对象,其结果可能仍然是挂起的。在任何时候,Promise都可以被解决(期望的行为),被拒绝(错误行为),或待定(尚未解决)。在上面的代码示例中,假设函数post返回一个执行HTTP请求的Promise。promise提供了一个方法,该方法有两个参数:一个在promise被解析时执行的函数,一个在promise被拒绝时执行的函数。很好地,promise可以被链接起来,这允许我们编写后续的“then”子句块,直接表达我们的顺序控制流--不受任何其他操作(例如,用户交互,如文本突出显示,CSS渲染或其他脚本)的影响,而任何promise都是挂起的。
为了将前面的XmlHTTPRequest转换为Promise,我们将其代码包装到一个JavaScript Promise实例化中。
function post(form) { return new Promise(function(resolve, reject) { let req = new XMLHttpRequest(); req.addEventListener("load", event => resolve(req.response)); req.addEventListener("error", event => reject(req.response)); // [...] prepare formData req.open("POST", form.action); req.send(formData); }); }
请注意,在调用post中尚未发送任何请求!该方法只返回一个“知道”如何执行请求的promise(我们在传递的函数值中教它)。promise保留了这个函数,但还没有执行它。当then在promise上被调用时,它将执行它的操作,也就是说,它将调用传递到它的构造函数中的函数,传递它的resolve和reject参数作为相应结果的回调。
目录
Lesson 26: Javascript & the HTML DOM