关于JavaScript与HTML的交互事件


Posted in Javascript onApril 12, 2013

JavaScript和HTML的交互是通过事件实现的。JavaScript采用异步事件驱动编程模型,当文档、浏览器、元素或与之相关对象发生特定事情时,浏览器会产生事件。如果JavaScript关注特定类型事件,那么它可以注册当这类事件发生时要调用的句柄。

事件流

事件流描述的是从页面中接收事件的顺序,比如有两个嵌套的div,点击了内层的div,这时候是内层的div先出发click事件还是外层先触发?目前主要有三种模型

IE的事件冒泡:事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的元素

Netscape的事件捕获:不太具体的节点更早接收事件,而最具体的元素最后接收事件,和事件冒泡相反

DOM事件流:DOM2级事件规定事件流包括三个阶段,事件捕获阶段,处于目标阶段,事件冒泡阶段,首先发生的是事件捕获,为截取事件提供机会,然后是实际目标接收事件,最后是冒泡句阶段。

Opera、Firefox、Chrome、Safari都支持DOM事件流,IE不支持事件流,只支持事件冒泡

如有以下html,点击div区域

<!DOCTYPE html >
<html>
<head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <title>Test Page</title>
</head>
<body>
    <div>
        Click Here</div>
</body>
</html>

关于JavaScript与HTML的交互事件

事件处理程序(handler)

我们也称之为事件侦听器(listener),事件就是用户或浏览器自身执行的某种动作。比如click、load、moseover等,都是事件类型(俗称事件名称),而响应某个事件的方法就叫做事件处理程序或者事件监听器或者事件句柄,事件处理程序名字是:on+事件类型。

了解了这些,我们看看如何给元素添加事件处理程序

HTML事件处理程序元素支持的每个事件都可以使用一个相应事件处理程序同名的HTML属性指定。这个属性的值应该是可以执行的JavaScript代码,我们可以为一个button添加 click事件处理程序

<input type="button" value="Click Here" onclick="alert('Clicked!');" />

在HTML事件处理程序中可以包含要执行的具体动作,也可以调用在页面其它地方定义的脚本,刚才的例子可以写成这样
<input type="button" value="Click Here" onclick="showMessage();" />
    <script type="text/javascript">
        function showMessage() {
            alert('Clicked!');
        }

在HTML中指定事件处理程序书写很方便,但是有两个缺点。

首先,存在加载顺序问题,如果事件处理程序在html代码之后加载,用户可能在事件处理程序还未加载完成时就点击按钮之类的触发事件,存在时间差问题。

其次,这样书写html代码和JavaScript代码紧密耦合,维护不方便。

JavaScript指定事件处理程序通过JavaScript指定事件处理程序就是把一个方法赋值给一个元素的事件处理程序属性。每个元素都有自己的事件处理程序属性,这些属性名称通常为小写,如onclick等,将这些属性的值设置为一个方法,就可以指定事件处理程序,如下

<input id="btnClick" type="button" value="Click Here" />
    <script type="text/javascript">
        var btnClick = document.getElementById('btnClick');
        btnClick.onclick = function showMessage() {
            alert(this.id);
        };
    </script>

这样处理,事件处理程序被认为是元素的方法,事件处理程序在元素的作用域下运行,this就是当前元素,所以点击button结果是:btnClick

这样还有一个好处,我们可以删除事件处理程序,只需把元素的onclick属性赋为null即可

DOM2事件处理程序DOM2级事件定义了两个方法用于处理指定和删除事件处理程序的操作:addEventListener和removeEventListener。所有的DOM节点都包含这两个方法,并且它们都接受三个参数:事件类型、事件处理方法、一个布尔值。最后布尔参数如果是true表示在捕获阶段调用事件处理程序,如果是false,则是在事件冒泡阶段处理。

刚才的例子我们可以这样写

<input id="btnClick" type="button" value="Click Here" />
    <script type="text/javascript">
        var btnClick = document.getElementById('btnClick');
        btnClick.addEventListener('click', function() {
            alert(this.id);
        }, false);
    </script>

上面代码为button添加了click事件的处理程序,在冒泡阶段触发,与上一种方法一样,这个程序也是在元素的作用域下运行,不过有一个好处,我们可以为click事件添加多个处理程序
<input id="btnClick" type="button" value="Click Here" />
    <script type="text/javascript">
        var btnClick = document.getElementById('btnClick');
        btnClick.addEventListener('click', function() {
            alert(this.id);
        }, false);
        btnClick.addEventListener('click', function() {
            alert('Hello!');
        }, false);
    </script>

这样两个事件处理程序会在用户点击button后按照添加顺序依次执行。

通过addEventListener添加的事件处理程序只能通过removeEventListener移除,移除时参数与添加的时候相同,这就意味着刚才我们添加的匿名函数无法移除,因为匿名函数虽然方法体一样,但是句柄却不相同,所以当我们有移除事件处理程序的时候可以这样写

<input id="btnClick" type="button" value="Click Here" />
    <script type="text/javascript">
        var btnClick = document.getElementById('btnClick');
        var handler=function() {
            alert(this.id);
        }
        btnClick.addEventListener('click', handler, false);
        btnClick.removeEventListener('click', handler, false);
    </script>

下面就是老生常谈的IE兼容性问题了。。。

IE并不支持addEventListener和removeEventListener方法,而是实现了两个类似的方法attachEvent和detachEvent,这两个方法都接收两个相同的参数,事件处理程序名称和事件处理程序方法,由于IE指支持事件冒泡,所以添加的程序会被添加到冒泡阶段。

使用attachEvent添加事件处理程序可以如下

<input id="btnClick" type="button" value="Click Here" />
    <script type="text/javascript">
        var btnClick = document.getElementById('btnClick');
        var handler=function() {
            alert(this.id);
        }
        btnClick.attachEvent('onclick' , handler);
    </script>

结果是undefined,很奇怪,一会儿我们会介绍到

使用attachEvent添加的事件处理程序可以通过detachEvent移除,条件也是相同的参数,匿名函数不能被移除。

<input id="btnClick" type="button" value="Click Here" />
    <script type="text/javascript">
        var btnClick = document.getElementById('btnClick');
        var handler=function() {
            alert(this.id);
        }
        btnClick.attachEvent('onclick', handler);
        btnClick.detachEvent('onclick', handler);
    </script>

跨浏览器的事件处理程序

前面内容我们可以看到,在不同的浏览器下,添加和移除事件处理程序方式不相同,要想写出跨浏览器的事件处理程序,首先我们要了解不同的浏览器下处理事件处理程序的区别

在添加事件处理程序事addEventListener和attachEvent主要有几个区别
1. 参数个数不相同,这个最直观,addEventListener有三个参数,attachEvent只有两个,attachEvent添加的事件处理程序只能发生在冒泡阶段,addEventListener第三个参数可以决定添加的事件处理程序是在捕获阶段还是冒泡阶段处理(我们一般为了浏览器兼容性都设置为冒泡阶段)

2. 第一个参数意义不同,addEventListener第一个参数是事件类型(比如click,load),而attachEvent第一个参数指明的是事件处理函数名称(onclick,onload)

3. 事件处理程序的作用域不相同,addEventListener得作用域是元素本身,this是指的触发元素,而attachEvent事件处理程序会在全局变量内运行,this是window,所以刚才例子才会返回undefined,而不是元素id

4. 为一个事件添加多个事件处理程序时,执行顺序不同,addEventListener添加会按照添加顺序执行,而attachEvent添加多个事件处理程序时顺序无规律(添加的方法少的时候大多是按添加顺序的反顺序执行的,但是添加的多了就无规律了),所以添加多个的时候,不依赖执行顺序的还好,若是依赖于函数执行顺序,最好自己处理,不要指望浏览器

了解了这四点区别后我们可以尝试写一个浏览器兼容性比较好的添加事件处理程序方法

function addEvent(node, type, handler) {
            if (!node) return false;
            if (node.addEventListener) {
                node.addEventListener(type, handler, false);
                return true;
            }
            else if (node.attachEvent) {
                node.attachEvent('on' + type, handler, );
                return true;
            }
            return false;
        }

这样,首先我们解决了第一个问题参数个数不同,现在三个参数,采用事件冒泡阶段触发,第二个问题也得以解决,如果是IE,我们给type添加上on,第四个问题目前还没有解决方案,需要用户自己注意,一般情况下,大家也不会添加很多事件处理程序,试试这个方法感觉很不错,但是我们没有解决第三个问题,由于处理程序作用域不同,如果handler内有this之类操作,那么就会出错在IE下,实际上大多数函数都会有this操作。
function addEvent(node, type, handler) {
            if (!node) return false;
            if (node.addEventListener) {
                node.addEventListener(type, handler, false);
                return true;
            }
            else if (node.attachEvent) {
                node.attachEvent('on' + type, function() { handler.apply(node); });
                return true;
            }
            return false;
        }

这样处理就可以解决this的问题了,但是新的问题又来了,我们这样等于添加了一个匿名的事件处理程序,无法用detachEvent取消事件处理程序,有很多解决方案,我们可以借鉴大师的处理方式,jQuery创始人John Resig是这样做的
function addEvent(node, type, handler) {
            if (!node) return false;
            if (node.addEventListener) {
                node.addEventListener(type, handler, false);
                return true;
            }
            else if (node.attachEvent) {
                node['e' + type + handler] = handler;
                node[type + handler] = function() {
                    node['e' + type + handler](window.event);
                };
                node.attachEvent('on' + type, node[type + handler]);
                return true;
            }
            return false;
        }

在取消事件处理程序的时候
function removeEvent(node, type, handler) {
            if (!node) return false;
            if (node.removeEventListener) {
                node.removeEventListener(type, handler, false);
                return true;
            }
            else if (node.detachEvent) {
                node.detachEvent('on' + type, node[type + handler]);
                node[type + handler] = null;
            }
            return false;
        }

John Resig很巧妙地利用了闭包,看起来很不错。

事件对象

在触发DOM上的某个事件的时候会产生一个事件对象event,这个对象包含着所有与事件有关的信息,包括产生事件的元素、事件类型等相关信息。所有浏览都支持event对象,但支持方式不同。

DOM中的事件对象兼容DOM的浏览器会产生一个event对象传入事件处理程序中。应用一下刚才我们写的addEvent方法

var btnClick = document.getElementById('btnClick');
    addEvent(btnClick, 'click', handler);

点击button的时候我们可以看到弹出内容是click的弹窗

event对象包含与创建它的特定事件有关的属性和方法,触发事件的类型不同,可用的属性和方法也不同,但是所有事件都会包含

属性/方法 类型 读/写 说明
bubbles Boolean 只读 事件是否冒泡
cancelable Boolean 只读 是否可以取消事件的默认行为
currentTarget Element 只读 事件处理程序当前处理元素
detail Integer 只读 与事件相关细节信息
eventPhase Integer 只读 事件处理程序阶段:1 捕获阶段,2 处于目标阶段,3 冒泡阶段
preventDefault() Function 只读 取消事件默认行为
stopPropagation() Function 只读 取消事件进一步捕获或冒泡
target Element 只读 事件的目标元素
type String 只读 被触发的事件类型
view AbstractView 只读 与事件关联的抽象视图,等同于发生事件的window对象
在事件处理程序内部,this始终等同于currentTarget,而target是事件的实际目标。

要阻止事件的默认行为,可以使用preventDefault()方法,前提是cancelable值为true,比如我们可以阻止链接导航这一默认行为

document.getElementsByTagName('a').onclick = function (e) {
            e.preventDefault();
        }

stopPaopagation()方法可以停止事件在DOM层次的传播,即取消进一步的事件捕获或冒泡。我们可以在button的事件处理程序中调用stopPropagation()从而避免注册在body上的事件发生
var handler = function (e) {
            alert(e.type);
            e.stopPropagation();
        }
        addEvent(document.body, 'click', function () { alert('Clicked body')});
        var btnClick = document.getElementById('btnClick');
        addEvent(btnClick, 'click', handler);

若是注释掉e.stopPropagation(); 在点击button的时候,由于事件冒泡,body的click事件也会触发,但是调用这句后,事件会停止传播。

IE中的事件对象访问IE中的event对象有几种不同的方式,取决于指定事件处理程序的方法。直接为DOM元素添加事件处理程序时,event对象作为window对象的一个属性存在

var handler = function () {
            var e = window.event;
            alert(e.type);
        }
        var btnClick = document.getElementById('btnClick');
        btnClick.onclick = handler;

我们通过window.event取得了event对象,并检测到了其类型,可是如果事件处理程序是通过attachEvent添加的,那么就会有一个event对象被传入事件处理程序中
var handler = function (e) {
            alert(e.type);
        }
        var btnClick = document.getElementById('btnClick');
        attachEvent(btnClick, handler);

当然这时候也可以通过window对象访问event,方便起见,我们一般会传入event对象,IE中所有的事件都包含以下属性方法

属性/方法 类型 读/写 说明
cancelBulle Boolean 读/写 默认为false,设置为true后可以取消事件冒泡
returnValue Boolean 读/写 默认为true,设为false可以取消事件默认行为
srcElement Element 只读 事件的目标元素
type String 只读 被触发的事件类型
跨浏览器的事件对象虽然DOM和IE的event对象不同,但基于它们的相似性,我们还是可以写出跨浏览器的事件对象方案
function getEvent(e) {
            return e || window.event;
        }
        function getTarget(e) {
            return e.target || e.scrElement;
        }
        function preventDefault(e) {
            if (e.preventDefault)
                e.preventDefault();
            else
                e.returnValue = false;
        }
        function stopPropagation(e) {
            if (e.stopPropagation)
                e.stopPropagation();
            else
                e.cancelBubble = true;
        }

常用HTML事件

有一些HTML事件我们会经常用到,这些事件不一定与用户操作有关,这里只是简单提及,详细用法大家就得百度谷歌了

1.load:当页面完全加载后在window上触发,当图像加载完成后在img元素上触发,或当嵌入内容加载完成时,在object元素上触发
2.unload:页面完全卸载后在window上触发,或嵌入内容卸载后在object元素触发
3.select:用户选择文本框中的字符时触发
4.change:文本框焦点变化后其值改变时触发
5.submit:用户提交表单的时候触发
6.resize:窗口或框架大小变化的时候在window上触发
7.scrool:用户滚动带滚动条的元素时,在该元素上触发
8.focus:页面或元素获得焦点时在window及相应元素上触发
9.blur:页面或元素失去焦点时在window及相应元素上触发
10.beforeunload:页面卸载前在window上触发
11.mousewheel:不算HTML的,当用户通过鼠标滚轮与页面交互,在垂直方向滚动页面时触发

Javascript 相关文章推荐
JavaScript 事件冒泡简介及应用
Jan 11 Javascript
Javascript:为input设置readOnly属性(示例讲解)
Dec 25 Javascript
JavaScript获取图片的原始尺寸以宽度为例
May 04 Javascript
鼠标左键单击冲突的问题解决方法(防止冒泡)
May 14 Javascript
分享一则JavaScript滚动条插件源码
Mar 03 Javascript
JS设置cookie、读取cookie
Feb 24 Javascript
详解js中的apply与call的用法
Jul 30 Javascript
移动前端图片压缩上传的实例
Dec 06 Javascript
微信小程序实现流程进度的图样式功能
Jan 16 Javascript
3种vue路由传参的基本模式
Feb 22 Javascript
Angular模版驱动表单的使用总结
May 05 Javascript
vue基于better-scroll实现左右联动滑动页面
Jun 30 Javascript
Js中setTimeout()和setInterval() 何时被调用执行的用法
Apr 12 #Javascript
js实现单一html页面两套css切换代码
Apr 11 #Javascript
获取内联和链接中的样式(js代码)
Apr 11 #Javascript
JavaScript在XHTML中的用法详解
Apr 11 #Javascript
JavaScript中的noscript元素属性位置及作用介绍
Apr 11 #Javascript
javascript标签在页面中的位置探讨
Apr 11 #Javascript
JS添加删除一组文本框并对输入信息加以验证判断其正确性
Apr 11 #Javascript
You might like
php 进度条实现代码
2009/03/10 PHP
自编函数解决pathinfo()函数处理中文问题
2014/11/03 PHP
百度地图经纬度转换到腾讯地图/Google 对应的经纬度
2015/08/28 PHP
php模拟post上传图片实现代码
2016/06/24 PHP
全面解析PHP操作Memcache基本函数
2016/07/14 PHP
php自定义函数转换html标签示例
2016/09/29 PHP
php+redis实现消息队列功能示例
2019/09/19 PHP
javascript 手动给表增加数据的小例子
2013/07/10 Javascript
使用node.js半年来总结的 10 条经验
2014/08/18 Javascript
使用Angular自定义字段校验指令的方法示例
2019/02/01 Javascript
Angular进行简单单元测试的实现方法实例
2020/08/16 Javascript
[02:03]永远的信仰DOTA2 中国军团历届国际邀请赛回顾
2016/06/26 DOTA
python编程-将Python程序转化为可执行程序[整理]
2007/04/09 Python
在Python中使用元类的教程
2015/04/28 Python
python机器学习实战之最近邻kNN分类器
2017/12/20 Python
python调用百度语音识别api
2018/08/30 Python
Python连接Redis的基本配置方法
2018/09/13 Python
Python基于机器学习方法实现的电影推荐系统实例详解
2019/06/25 Python
django echarts饼图数据动态加载的实例
2019/08/12 Python
Django 解决model 反向引用中的related_name问题
2020/05/19 Python
Python模拟登入的N种方式(建议收藏)
2020/05/31 Python
python 操作excel表格的方法
2020/12/05 Python
Html5之webcoekt播放JPEG图片流
2020/09/22 HTML / CSS
美国最大点评网站:Yelp
2018/02/14 全球购物
求网格中的黑点分布
2013/11/06 面试题
New delete 与malloc free 的联系与区别
2013/02/04 面试题
外语系毕业生找工作的求职信
2013/11/28 职场文书
毕业生自荐书模版
2014/01/04 职场文书
企业军训感言
2014/02/08 职场文书
促销活动总结范文
2014/04/30 职场文书
有关环保的标语
2014/06/13 职场文书
2014法院四风问题对照检查材料思想汇报
2014/10/04 职场文书
大学生自荐信范文
2015/03/05 职场文书
实习单位推荐信
2015/03/27 职场文书
2015小学教师年度工作总结
2015/05/12 职场文书
Python使用sql语句对mysql数据库多条件模糊查询的思路详解
2021/04/12 Python