《JavaScript设计与开发新思维》——2.5 拼凑一些代码

简介: 现在有两个事件监听器。第一个监听window的load事件,该事件对于每个页面只发生一次,触发时调用init()函数。第二个监听器等待表单的提交,可能发生任意次(也可能一次都不发生)。每当该事件发生时,都调用validateForm()函数。定义该函数是渐进增强的最后一步。

本节书摘来自异步社区《JavaScript设计与开发新思维》一书中的第2章,第2.5节,作者:【美】Larry Ullman著,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.5 拼凑一些代码

利用已经介绍过的一些基础知识,我们继续前进,开始接触JavaScript。当然,我不期望你已经了解JavaScript—这是本书的目的—但是下一个示例阐述了JavaScript的易理解性,并且为第2部分的材料(也即正规训练)提供一些背景知识。

2.5.1 开发基本功能
作为一个简单而实用的例子,我们来创建一个登录表单,然后用JavaScript进行验证。在稍后的章节中,你将学习如何为这个表单应用Ajax,但是在这时添加Ajax可能有点过于复杂。首先创建HTML表单,该表单有3个元素:一个电子邮件地址、一个密码和一个提交按钮。下面是最关键的HTML,存储在login.html文件中(图2.8)。
screenshot

<form action="login.php" method="post" id="loginForm**">
    <fieldset>
         <legend>Login</legend>
         <div><label for="email">Email Address</label>
<input type="email" name="email"
id="email" required></div>
         <div><label for="password">Password</label>
<input type="password" name="password"
id="password" required></div>
         <div><label for="submit"></label>**<input type=**"**submit**"
value="Login &rarr; " id="submit"></div>
    </fieldset>
</form>
<script src="js/login.js"></script>

为了简单起见,该页面上除了前述的表单之外,什么也没有。该页面还使用了一个基本的CSS文件以添加一些样式;你可以从本书对应网站上下载该文件(CSS文件将在整个可下载脚本的ch02文件夹中)。

上述表单将被提交给login.php。该脚本将进行如下工作:

  • 验证提交的电子邮件地址。
  • 验证提交的密码。
  • 确认提交的值与以前存储在数据库中的相符。
  • 如果匹配,发送一个cookie或者启动一个跟踪用户的会话。
  • 将用户重定向到欢迎页面。

如果你还不知道如何自己实现PHP和MySQL,在以后的章节中,你将看到这个表单的所有实际运行状况。这是基线功能,不管浏览器的JavaScript设置和能力如何,这些功能都有效。如果客户能够加载一个HTML页面,这个系统就很好。下一步是对其进行渐进增强。

2.5.2 添加JavaScript层次
在我们所举的特殊例子中,渐进增强意味着JavaScript将被用于在客户端验证表单数据,仅允许在数据通过时将表单提交给服务器(正如图1.4中的注册示例)。

首先要注意这个表单和没有绑定JavaScript的表单之间的唯一区别—每个元素都有name属性和id属性。name值在表单数据提交到服务器端PHP脚本时使用,而JavaScript将使用id属性值。从逻辑上说,这两个值对于每个元素来说都相同。页面上的每个元素(表单或者其他元素)都必须有唯一的id值。

这个渐进增强的页面还使用了一个外部JavaScript文件login. js。它应该包含在HTML页面中body结束标志之前:

<script src="js/login.js"></script>
现在,至少对于本书的这个阶段来说,这个文件的内容有些复杂。为了理解这个文件当中应该有什么样的JavaScript代码,你必须有事件处理的基本知识。

2.5.3 处理事件
正如第1章中所提到的,JavaScript是一种事件驱动语言,这意味着它仅在事件发生之后进行某些工作。事件的例子包括:

  • 网页加载;
  • 单击一个元素,如按钮或者链接;
  • 在表单元素中输入文本;
  • 将光标移动到一个元素上(也就是mouseover事件);
  • 将光标从一个元素上移开(也就是mouseout事件)。

为了让JavaScript验证一个HTML表单,你必须确定触发验证代码的事件。常用于表单验证的事件如下:

表单提交;
单击提交按钮(这也触发表单提交事件);
改变表单元素的值;
表单元素失去焦点时(不管值是否改变都触发)。
第8章“事件处理”更详细地讨论事件。现在,我们只是在提交的时候验证表单,为此,必须为表单添加一个事件监听器(Event listener)。事件监听器是一个在“这个事件”发生在“这个对象”上时调用的函数。每个对象,不管是整个浏览器窗口还是页面中的具体元素(不管是否为表单元素),都有能够触发的特定事件。被调用的函数通常是你自己定义的。对象、事件类型和函数的组合造成了多种多样的可能性。

为了观察表单的提交事件,我们首先要获得对表单本身的引用。使用 document 对象的 getElement-ById()方法是一种简单而可靠的方式。document对象代表整个HTML内容:从html开始标记到head和body等元素。

Document对象有一个getElementById()方法,它以ID名称为参数,返回对应元素的引用,返回值可以赋值给一个变量供以后使用:

var loginForm = document.getElementById('loginForm');
此时,只要有一个元素(毫无疑问可以是任何类型)的id值为loginForm, loginForm变量将是对该元素的引用。第9章更加详细地研究DOM操纵,但是getElementById()方法非常重要且很容易使用,所以值得在本章里介绍。

有了对表单的引用,就可以用下列代码添加事件监听器:

_element_.on_event _= _function_;
例如:

loginForm.onsubmit = validateForm;
下文的补充说明中将更详细地解释这种语法,但是这一代码行只是说明在loginForm元素遇到一个提交事件时,将调用validate-Form()函数。注意,该函数的名称用在这一赋值语句的右侧,没有引号,结尾也没有圆括号。下面这两种写法都不正确:

loginForm.onsubmit = 'validateForm'; // NO!
loginForm.onsubmit = validateForm(); // NO!

理论上,下一步应该是定义validate-Form()函数,这个函数执行实际的表单验证。遗憾的是,需要先执行另一个步骤。

我将对此加以解释。

从一个服务器请求文档时,客户将按照顺序接受文档的数据。对于HTML页面,这意味着浏览器首先接收DOCTYPE,然后是html开始标记、head标记和head的内容,接着是文件体及其内容,以此类推,直到文档结束。当浏览器遇到对其他必须下载的素材的引用时,就必须相应地进行下载,这些素材包括CSS文件、图像和其他媒体、JavaScript、Flash等。就DOM操纵来说,这一过程很重要,因为浏览器在完全了解HTML 页面(图2.9)之前无法表现DOM。对于JavaScript,这意味着在浏览器加载页面的 HTML 之前,你无法安全地使用 document. getElementById()。

screenshot

知道何时能够安全地引用DOM元素的最可靠方法是确认浏览器完全加载了整个页面。当然,这是一个事件,可以设置事件监听器,观察事件的发生:

window.onload = init;
提示: 当JavaSript放在靠近文档结尾处时,网站的加载似乎更快,这是因为浏览器在等待JavaScript加载时必须暂停HTML的渲染。

注意: 我打算简化浏览器下载和加载网页的过程,以表达关键的要点。如果你对此过程感到好奇,在网上搜索就能找到很多细节。

对象事件属性

第1章中曾经提到,对象是一个特殊的变量类型,具有预先定义的属性(也就是它自己的内部变量)和方法(即函数)。对象记法(或者圆点)语法用于访问对象的属性和方法。loginForm.onsubmit = validateForm这样的代码将validateForm()函数赋给loginForm对象的onsubmit属性。这似乎有些奇怪,但是这和将数值赋值给一个变量的概念相同: var num = 2; 但在事件监听器的情况下,变量是对象的一个属性,赋给的值是一个函数:稍微复杂一些,但是原理相同。 loginForm对象有一个onsubmit属性,因为loginForm代表一个或者多个触发提交事件的表单元素。这段代码不能用于一个链接,因为链接没有onsubmit属性(但是链接有onclick属性)。引用基于事件的对象属性时,全部使用小写字母:onsubmit而不是onSubmit。 对于上述赋值语句,函数必须与这个事件相关联;因此函数的名称在语句的右侧提供。你不能在函数名称上加引号,这样它将变成一个字符串值而不是函数。你也不能使用functionName()的形式,带上圆括号表示一次实际的函数调用。

这段代码表明,window对象触发加载事件时应该调用init()函数。然后,init()函数可以将事件监听器添加到表单,因为这时进行DOM引用是安全的:

function init() {
    var loginForm = document.getElementById('loginForm');
   loginForm.onsubmit = validateForm;
}

第 7 章“创建函数”介绍自定义函数所必须了解的内容,但是函数的基础知识实际上很简单。首先,使用function关键字,后面跟上函数的名称和圆括号。(init函数的调用很常见,init是Initialize(初始化)的缩写,该函数用于初始化一些必要的JavaScript和浏览器行为。)该函数的实际代码出现在大括号之间,代表着函数调用时将会执行的任务。

作为附加的保护措施,我们在这里添加对象检测,这样表单的事件监听器将仅在浏览器支持document.getElement-ById()的方法下才被添加:

function init() {
     if (document && document.getElementById) {
          var loginForm = document.getElementById('loginForm');
          loginForm.onsubmit = validateForm;
     }
}

现在有两个事件监听器。第一个监听window的load事件,该事件对于每个页面只发生一次,触发时调用init()函数。第二个监听器等待表单的提交,可能发生任意次(也可能一次都不发生)。每当该事件发生时,都调用validateForm()函数。定义该函数是渐进增强的最后一步。

注意: 实际上,浏览器对document对象和getElementById()方法的支持已经超过10年之久,所以对象检测的这种特殊用法实际上并没有必要。

2.5.4 执行验证
validateForm()函数应该验证表单数据并返回表示数据有效性的一个布尔值。如果函数返回true(真),就允许表单提交给服务器端脚本。如果函数返回false(假),那么表单的提交将被阻止。

该函数的外壳如下:

function validateForm() {
}

现在是执行基本验证的时候了,这些验证发生在上述的外壳中。对于电子邮件地址和密码,验证应该检查是否存在某个值(确认电子邮件地址格式有效是可能的,但是需要许多代码)。对于文本输入,可以检查其长度完成简单的验证(也就是证明输入了任意的内容)。这一切从获得每个输入域的引用开始,仍然使用getElementById():

var email = document.getElementById('email');
var password = document.getElementById('password');

提示: 记住,如果网页使用HTML5,而浏览器支持HTML5,将会应用自动化的客户端验证(如前面的插图所示)。

这时,每个变量是对应的表单元素的一个引用。引用变量的value属性就能找到元素的当前值:如email.value和password.value。因为两者都是文本元素,value属性都为一个字符串值,甚至可能是一个空字符串。JavaScript中的所有字符串都有length属性,存储该串中的字符数量。因此,email.value.length是输入到电子邮件输入域的字符数量,然后,可以根据字符数量创建一条简单的条件语句:

if ( (email.value.length > 0) && (password.value.length > 0) ) {
     return true;
} else {
     return false;
}

注意: 检查元素值的长度对于文本输入域是有效的,其他类型的元素以不同的方式验证。

这里使用的是一个简单的验证例程。除非两个表单元素中都输入了一些内容,否则表单就不会提交给服务器端脚本。但是,除了阻止表单提交以外,用户还应该知道错误的发生。可以用更高级的方式提醒用户,但是现在一个警告框可能就足够了(图2.10和图2.11):

if ( (email.value.length > 0) && (password.value.length > 0) ) 
{
     return true;
} else {
     alert('Please complete the form! ');
     return false;
}

screenshot

注意: 客户端验证对于最终用户来说很方便,服务器端验证始终是必要的。

你现在有了一个简单、渐进增强、无干扰的JavaScript,在HTML表单发送到服务器之前进行验证。下面的代码块展示了汇总后的所有代码,以及说明关键部分的一些注释。该脚本有三个顶级的(也就是非嵌套的)组件:

validateForm()函数的定义;
init()函数的定义;
将init()函数注册为window.onload事件监听器。
注意: 因为服务器端脚本login.php尚未编写,在表单通过验证时,浏览器试图访问不存在的文件,你将会看到服务器错误。

但是,出于技术性的原因,这3个组件编写的顺序没有关系,我选择以这种顺序编码是因为:

validateForm()在被init()函数引用之前定义。
init()函数在被赋予window.onload属性之前定义。
同样,这种顺序也不是必需的,但是这对于编码的结构有逻辑上的意义。每个函数也以如下代码开始:

'use strict';
使用这一行代码的原因在“引用严格模式”补充说明中加以解释。

// login.js
// Function called when the form is submitted.
// Function validates the form data and returns a Boolean value.
function validateForm() {
    'use strict';
    // Get references to the form elements:
    var email = document.getElementById('email');
    var password = document.getElementById('password');
    // Validate!
    if ( (email.value.length > 0) && (password.value.length > 
0) ) {
         return true;
    } else {
         alert('Please complete the form! ');
         return false;
    }
} // End of validateForm() function.
// Function called when the window has been loaded.
// Function needs to add an event listener to the form.
function init() {
    'use strict';
    // Confirm that document.getElementById() can be used:
    if (document && document.getElementById) {
         var loginForm = document.getElementById('loginForm');
         loginForm.onsubmit = validateForm;
    }
} // End of init() function.
// Assign an event listener to the window's load event:
window.onload = init;

引用严格模式

JavaScript自己的严格模式和已经讨论过的浏览器严格模式不同,是在你所写的代码中强制更严格的JavaScript行为的一种方式。严格模式在ECMAScript 5中加入,通过在JavaScript中放置如下字符串引用: 'use strict'; 这一行代码在每个脚本开始可以使用一次,但是正如你在本书中所看到的,用作每个函数的第一行更为可靠。 引用严格模式时,JavaScript将与非严格模式中的执行方式略有不同,一般来说,严格模式将: 导致可能有问题的代码生成错误; 改进安全性和性能; 对将在该语言的未来标准中被删除的代码提出警告。 简而言之,严格模式强制你编写更好的代码,这是一件非常非常好的事情。 如果你希望了解严格模式强制形成的变化细节,可以在网络上搜索,但是在现阶段的学习中对你没有太多意义。

相关文章
|
8天前
|
JSON JavaScript 前端开发
JavaScript原生代码处理JSON的一些高频次方法合集
JavaScript原生代码处理JSON的一些高频次方法合集
|
22天前
|
开发框架 JavaScript 安全
js开发:请解释什么是Express框架,以及它在项目中的作用。
Express是Node.js的Web开发框架,简化路由管理,支持HTTP请求处理。它采用中间件系统增强功能,如日志和错误处理,集成多种模板引擎(EJS、Jade、Pug)用于HTML渲染,并提供安全中间件提升应用安全性。其可扩展性允许选用合适插件扩展功能,加速开发进程。
|
23天前
|
缓存 JavaScript 前端开发
js开发:请解释什么是Webpack,以及它在项目中的作用。
Webpack是开源的JavaScript模块打包器,用于前端项目构建,整合并优化JavaScript、CSS、图片等资源。它实现模块打包、代码分割以提升加载速度,同时进行资源优化和缓存。借助插件机制扩展功能,并支持热更新,加速开发流程。
16 4
|
23天前
|
缓存 JavaScript 数据安全/隐私保护
js开发:请解释什么是ES6的Proxy,以及它的用途。
`ES6`的`Proxy`对象用于创建一个代理,能拦截并自定义目标对象的访问和操作,应用于数据绑定、访问控制、函数调用的拦截与修改以及异步操作处理。
17 3
|
23天前
|
JavaScript
js开发:请解释什么是ES6的类(class),并说明它与传统构造函数的区别。
ES6的类提供了一种更简洁的面向对象编程方式,对比传统的构造函数,具有更好的可读性和可维护性。类使用`class`定义,`constructor`定义构造方法,`extends`实现继承,并可直接定义静态方法。示例展示了如何创建`Person`类、`Student`子类以及它们的方法调用。
20 2
|
24天前
|
自然语言处理 JavaScript 网络架构
js开发:请解释什么是ES6的箭头函数,以及它与传统函数的区别。
ES6的箭头函数以`=&gt;`定义,简化了函数写法,具有简洁语法和词法作用域的`this`。它无`arguments`对象,不能用作构造函数,不支持`Generator`,且不改变`this`、`super`、`new.target`绑定。适用于简短表达式,常用于异步编程和高阶函数。
17 5
|
1天前
|
开发框架 前端开发 JavaScript
采用C#.Net +JavaScript 开发的云LIS系统源码 二级医院应用案例有演示
技术架构:Asp.NET CORE 3.1 MVC + SQLserver + Redis等 开发语言:C# 6.0、JavaScript 前端框架:JQuery、EasyUI、Bootstrap 后端框架:MVC、SQLSugar等 数 据 库:SQLserver 2012
|
23天前
|
JSON 前端开发 JavaScript
16个重要的JavaScript代码
16个重要的JavaScript代码
30 1
|
23天前
|
Web App开发 JavaScript 前端开发
js开发:请解释什么是Node.js,以及它的应用场景。
Node.js是基于V8的JavaScript运行时,用于服务器端编程。它的事件驱动、非阻塞I/O模型使其在高并发实时应用中表现出色,如Web服务器、实时聊天、API服务、微服务、工具和跨平台桌面应用(使用Electron)。适用于高性能和实时需求场景。
18 4
|
23天前
|
JavaScript 前端开发 编译器
js开发: 请解释什么是Babel,以及它在项目中的作用。
**Babel是JavaScript编译器,将ES6+代码转为向后兼容版本,确保在旧环境运行。它在前端构建中不可或缺,提供语法转换、插件机制、灵活配置及丰富的生态系统,支持代码兼容性和自定义编译任务。**
17 6