基本事件模型

  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 />
        密&nbsp;&nbsp;码:<input type="password" name="pass" /><br />
        电&nbsp;&nbsp;邮:<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等属性。
  同样是上面的例子,此时不再为元素指定onsubmit属性,下面是表单的代码片段。

<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文档控件支持的事件及对应描述。


IT料理 IT料理

事件处理函数和关键字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 />
    密&nbsp;&nbsp;码:<input type="password" name="pass" /><br />
    电&nbsp;&nbsp;邮:<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讲义 ,详细内容请参阅书籍。