基本事件模型
JavaScript在浏览器中运行,浏览器中的HTML页面主要由DOM组成,这些DOM对象也对应到各种HTML元素,我们就可为这些DOM对象或HTML元素绑定事件处理函数(或多条JavaScript脚本),当这些DOM对象或HTML元素上发生某个动作时,这些事件处理函数(或多条JavaScript脚本)就会被触发,从而获得执行的机会。
为HTML元素绑定处理函数有很多种不同的方法。事实上,不同的浏览器又提供了自己的绑定方法,这些绑定事件处理函数的方法很难说哪种更好,哪种更差,不同的绑定方法有不同的侧重点。为了保证有更好的跨浏览器特性,通常推荐采用与浏览器无关的事件绑定方法。本节介绍的两种方式都可实现跨浏览器绑定。
绑定HTML元素属性
常用的绑定事件处理器的方法是直接绑定到HTML元素的属性,正如在前一章示例程序中所看到的,我们为多个HTML元素指定了onclick属性值。
绑定到HTML元素属性时,属性值是一条或多条JavaScript脚本,多条脚本之间以英文分号分隔。
大部分表单的数据校验都会采用这种方式。在提交表单时,JavaScript的事件处理程序将会对表单域进行校验,如果不能通过数据校验,则弹出警告对话框。
事件属性名称由事件类型前加一个“on”前缀构成,例如onclick、ondblclick等。这些属性的值也被称为事件处理器,因为它们指定了如何“处理”特定的事件类型。事件处理器属性的值是多条JavaScript脚本,最常见的值是一条调用某个JavaScript函数的语句。
例子
下面介绍一个表单校验的示例程序。下面的HTML页面包含3个表单控件:用户名、密码、电邮,其中用户名、密码不能为空;而电子邮件不能为空,且必须满足电子邮件格式。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK" />
<title> JavaScript校验表单 </title>
</head>
<body>
<div>
<h2>数据校验表单</h2>
<form method="post" onsubmit="return check(this);"
id="register" name="register" action="#">
用户名:<input type="text" name="user" /><br />
密 码:<input type="password" name="pass" /><br />
电 邮:<input type="text" name="email" /><br />
<input type="submit" value="提交" />
</form>
</div>
<script type="text/javascript">
// 为字符串增加trim方法,使用正则表达式截取空格
String.prototype.trim = function()
{
return this.replace( /^\s*/, "" ).replace( /\s*$/, "" );
}
// 负责处理表单submit事件的函数
var check = function()
{
// 访问页面中第一个表单
var form = document.forms[0];
// 错误字符串
var errStr = "";
// 当用户名为空
if (form.user.value == null
|| form.user.value.trim() == "")
{
errStr += "\n用户名不能为空!";
form.user.focus();
}
// 当密码为空
if (form.pass.value == null
|| form.pass.value.trim() == "")
{
errStr += "\n密码不能为空!";
form.pass.focus();
}
// 当电子邮件为空
if (form.email.value == null
|| form.email.value.trim() == "")
{
errStr += "\n电子邮件不能为空!";
form.email.focus();
}
// 使用正则表达式校验电子邮件的格式是否正确
if(!/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/
.test(form.email.value.trim()))
{
errStr += "\n电子邮件的格式不正确!";
form.email.focus();
}
// 如果错误字符串不为空,表明校验出错
if( errStr != "" )
{
// 弹出出错信息
alert(errStr);
// 返回false,用于阻止表单提交
return false;
}
}
</script>
</body>
</html>
上面的<form.../>
元素的onsubmit属性为"return check();",之所以使用return语句,是为了保证数据校验不能通过时拒绝表单提交。而check()函数就是负责校验表单的函数,当该表单被提交时,该check()函数就会被触发,从而获得执行的机会。
check()函数则使用DOM模型访问页面中的表单,并对表单里的表单控件进行校验,校验表单控件里的值是否符合业务要求。
这种事件绑定方式简单易用,但绑定事件处理器时需要直接修改HTML页面代码,因此存在如下几个坏处。
➢ 直接修改HTML元素属性,增加了页面逻辑的复杂度。
➢ 开发人员需要直接修改HTML页面,不利于团队协作开发。
但这种绑定方式也有一个优点:在这种绑定方式中,HTML元素的onclick等属性值是一条或多条JavaScript语句,因此可以在调用JavaScript函数时传入参数,典型的就是传入this、event等有特殊意义的参数。
绑定DOM对象属性
绑定到DOM对象属性时,开发者无须修改HTML元素的代码,而是将事件处理函数放在JavaScript脚本中绑定。
为了给特定的HTML元素绑定事件处理函数,必须先在代码中获得需要绑定事件处理函数的HTML元素对应的DOM对象,该DOM对象就是触发事件的事件源,然后给该DOM对象的onclick等属性赋值,其合法的属性值是一个JavaScript函数的引用。
值得指出的是,因为绑定到DOM对象属性时,该属性值只是一个JavaScript函数的引用,因此千万不要在函数后添加括号——一旦添加括号,那就变成了调用该函数,于是只是将该函数返回值赋给DOM对象的onclick等属性。
同样是上面的例子,此时不再为
<form method="post" name="register" action="#">
...
</form>
上面的表单元素没有直接绑定事件处理函数,因此,我们需要通过绑定DOM对象属性来设置事件处理函数。只要在JavaScript脚本最后添加如下一行:
//为第一个表单的onsubmit绑定事件处理器
document.forms[0].onsubmit = check;
上面代码中的onsubmit属性值只是check函数引用,没有在check函数后添加括号。在这种方式下,JavaScript事件绑定机制与Java事件绑定机制非常相似:事件源通常是一个可视化控件,事件监听器就是一个JavaScript函数(因为函数是JavaScript的“一等公民”)。
当采用这种方式为DOM对象绑定事件处理函数时,开发者无须修改HTML文档,只需在该页面中增加一行代码用于绑定即可。正如14.1.2节所介绍的,大部分时候我们都将JavaScript脚本写在单独的.js文件中,这样我们完全无须修改HTML页面,只需修改.js文件即可,这样更有利于团队协作。
JavaScript可以触发的不同事件
正如Java事件机制中不同的可视化控件支持不同的事件类型一样,不同的HTML控件也支持触发不同的事件。下图显示了所有标准的HTML文档控件支持的事件及对应描述。
事件处理函数和关键字this
JavaScript脚本通常处于window对象下运行,如果JavaScript脚本中使用this关键字,则通常引用到window本身。
当为HTML元素的onclick等属性指定一系列JavaScript脚本时,如果在这些JavaScript脚本中使用关键字this,则该关键字引用该HTML元素本身。
当为DOM对象的onclick等属性指定一个JavaScript函数引用时——由于JavaScript是一门动态语言,因此我们可以随时为某个对象添加属性和方法——这种方式可认为就是为该DOM对象增加了一个方法。因此,当在该函数中使用关键字this时,该this也是引用该DOM对象本身。
在一种极端的情况下,我们为DOM对象的onclick等属性指定的不是一个独立的JavaScript函数,而是某个对象的方法。
例子
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK" />
<title> 测试this关键字 </title>
</head>
<body>
<body>
<input id="bn1" type="button" value="按钮1"
onclick="alert(this.value);"/>
<input id="bn2" type="button" value="按钮2" />
<input id="bn3" type="button" value="按钮3" />
<script type="text/javascript">
var test = function()
{
alert(this.value);
}
// 将test函数设置为按钮bn3的事件处理器
document.getElementById("bn2").onclick = test;
// 使用JSON格式定义了一个对象
var p =
{
value: 'p对象',
info: function()
{
alert(this.value);
}
}
// 将对象p的info方法设置为按钮bn3的事件处理器
document.getElementById("bn3").onclick = p.info;
// 直接调用对象p的info()方法
p.info();
</script>
</body>
</html>
使用返回值改变默认行为
HTML元素大都包含了自己的默认行为。例如,单击超链接将导致页面导航到超链接所指的页面,单击表单的“提交”按钮将导致表单提交……但如果为这些元素增加对应的事件处理函数,将可以改变这种默认行为。例如阻止超链接导航,看下面的代码:
<a href="http://www.baidu.com" onclick="return false;">百度</a>
上面的“百度”超链接绑定了onclick事件处理器。如果没有指定该事件处理函数,单击该链接时页面将导航到“百度”站点。但该超链接绑定了onclick事件处理代码,该代码简单地返回false,这将阻止超链接的导航功能。还有在表单提交事件的事件处理函数返回false,也将阻止表单提交。
除了可以直接使用return返回值之外,还可以使用confirm提示框,单击该提示框的“确定”按钮将返回true,而单击“取消”按钮则返回false。
在代码中触发事件
除了可以让用户动作、窗口动作等触发JavaScript中的事件外,JavaScript还允许在脚本中触发事件,程序触发事件与用户动作触发事件的效果完全相同。所谓在脚本中触发事件,其关键就在于先获得该DOM对象或HTML对象,然后在JavaScript脚本中调用该对象的方法。
注意
定义表单控件的name、id属性值时,这些属性值不应该和表单对象原有的方法名、属性名相同,否则这些表单控件就会覆盖原有的方法、属性。
例子
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK" />
<title> 脚本触发事件 </title>
</head>
<body>
<h2>脚本触发事件</h2>
<form method="post" name="register"
action="http://www.crazyit.org">
用户名:<input type="text" name="user" /><br />
密 码:<input type="password" name="pass" /><br />
电 邮:<input type="text" name="email" /><br />
<input type="button" id="regist" value="提交"/>
</form>
<script type="text/javascript">
// 为字符串增加trim方法,使用正则表达式截取空格
String.prototype.trim = function()
{
return this.replace( /^\s*/, "" ).replace( /\s*$/, "" );
}
// 负责处理表单submit事件的函数
var check = function ()
{
// 访问页面中第一个表单
var form = document.forms[0];
// 错误字符串
var errStr = "";
// 当用户名为空
if (form.user.value == null
|| form.user.value.trim() == "")
{
errStr += "\n用户名不能为空!";
form.user.focus();
}
// 当密码为空
if (form.pass.value == null
|| form.pass.value.trim() == "")
{
errStr += "\n密码不能为空!";
form.pass.focus();
}
// 当电子邮件为空
if (form.email.value == null
|| form.email.value.trim() == "")
{
errStr += "\n电子邮件不能为空!";
form.email.focus();
}
//如果错误字符串不为空,表明数据校验出错
if(errStr != "")
{
//弹出出错提示
alert(errStr);
}
// 如果错误字符串为空,表明数据校验通过,可以提交表单
else
{
//在代码中手动提交表单
form.submit();
}
}
//为第一个表单的onclick绑定事件处理器
document.getElementById("regist").onclick = check;
</script>
</body>
</html>
注:本博客内容节选自李刚编著的疯狂HTML 5/CSS 3/JavaScript讲义 ,详细内容请参阅书籍。