Front end foundation course 10

上节课的作业

同学们交的作业完成的质量都挺不错的,这里对后两道题给一个例子。

上节课的 DEMO

本次的 demo 是为了复习和巩固 HTML 和 CSS 的基础知识,所以我们来一起仿造一个静态页面。

上节课因为课上练习的时间比较长,所以 demo 就没做。 不过我觉得这个练习还是很有意义的,所以这次课我们先继续完成上节课的 demo。

DOM 基础 Part 2

DOM 事件

我们之前已经学了如何通过 JS 操作 DOM 节点。但在实际的开发中,并不是页面就是按照我们写好的流程跑一边就结束了。 大多数时候我们是在对用户的行为进行响应,以达到服务用户的目的。那么如何监听并响应用户的行为,就和我们这节课要讲的 DOM 事件相关。

学习目标

  • 知道什么是事件以及大致有哪些事件
  • 会用 JS 进行事件监听和响应
  • 知道事件冒泡、捕获

什么是事件

生活中我们执行的一些动作(如:洗衣服,做饭)我们会称之为一件事情。 那么同理,用户在网页中执行的一些动作,就是我们这里讲到的事件。 如:点击按钮、移动鼠标、加载图片、刷新网页、滚动页面等。

什么是事件绑定

生活中当我们摔倒会下意识的伸手扶地保护自己,或者当收到惊吓会下意识的尖叫,其实这就类似于计算机中的事件绑定。 计算机中事件绑定即指在发生某个事件的时候,自动触发相应的处理机制。如当用户滚动页面到底部会出现回到顶部的按钮。

如何实现事件绑定

给节点绑定事件大致上可以通过两种方式,一种是修改事件属性,一种是使用事件接口。

事件属性

每个 DOM 节点都会有一些事件相关的属性,它们通常以 'on' 开头,属性值为空。 可以通过修改这些属性的值来指定事件的处理代码。例如:

<a id="alert-btn">点我试试</a>
<script>
    var btn = document.getElementById('alert-btn');
    btn.onclick = function () {
        alert('你还真点啊');
    };
</script>

事件接口

设想一下如果需要对一个节点绑定多个事件应该怎么处理呢? 通过上面的方法可能会很麻烦,但可以通过节点的 addEventListenerremoveEventLister 方法来方便的实现。 例如上面的代码也可以这样实现:

<a id="alert-btn">点我试试</a>
<script>
    var btn = document.getElementById('alert-btn');
    btn.addEventListener('click', function () {
        alert('你还真点啊');
    });
</script>

IE 低版本需要使用 attachEventdetachEvent

常用事件

  • click 鼠标点击
  • dbclick 鼠标双击
  • mousemove 鼠标移动
  • mouseenter 鼠标移入
  • mouseleave 鼠标移出
  • keydown 键盘按下
  • keyup 键盘松开
  • keypress 键盘按键
  • scroll 文档滚动
  • load 加载完成

更完整的事件列表可以看这里

事件参数

事件处理函数的第一个参数为事件对象,其中包含了事件触发的元素、位置、事件类型等非常多有用的信息,供需要的时候使用。 通过下面的方式可以输出事件对象以查看其中的内容:

btn.addEventListener('click', function (e) {
    console.log(e);
});

关于冒泡和捕获

在事件传递的过程中,分为冒泡和捕获两个过程。这里给大家做简单的描述,更多内容感兴趣的同学可自行扩展学习。

捕获

捕获过程即事件从最外层的元素开始触发,然后逐渐触发到当前元素。

例如对于如下的 DOM 结构:

<body>
    <div class="father">
        <div class="child"></div>
    </div>
</body>

如果 child 元素触发事件,那么捕获的过程为:body -> div.father -> div.child 。

冒泡

冒泡与捕获相反,从当前元素开始触发,逐渐向外扩展直到最外层元素或者被终止。

例如对于上面的 DOM 结构,如果 child 元素触发事件,那么冒泡的过程为:div.child -> div.father -> body 。

如何使用

在支持的浏览器中,可以通过参数来控制发生的过程究竟是在捕获还是在冒泡,如下:

element.addEventListener('click', function () {}, false);

其中第三个参数为 false 表示发生在冒泡阶段(默认),为 true 表示发生在捕获阶段。

事件代理

设想一下这种场景,有一个列表,其中点击列表项需要触发一个事件,假如说这个列表有 1000 项,那是不是需要绑定 1000 次事件? 如果这还不够麻烦的话,那如果列表内容也是可变的,每次添加新的还需要再绑定事件,删除已有的也需要删除相应的事件,那么我添加 1000 项再删除 1000 次呢?

我们现在已经知道了事件存在冒泡的机制,那么对于类似的场景,我们可以通过事件代理来处理。 直接在外层的父节点上绑定事件监听,再通过 target 属性获取到事件触发的元素即可。

    <ul id="list">
        <li>test1</li>
        <li>test2</li>
        <li>test3</li>
        <li>test4</li>
        <li>test5</li>
        <li>test6</li>
    </ul>
    <script>
        var ul = document.getElementById('list');
        ul.addEventListener('click', function (e) {
            console.log('事件代理成功,触发事件的元素为:');
            console.log(e.target);
        }, false);
    </script>

阻止行为

在事件处理函数中,可以通过 e.stopPropagation() 来阻止事件冒泡,通过 e.preventDefault() 来阻止默认行为的触发。 例如:

html

Homework

  1. 写一个计时器页面,拥有开始和停止按钮。按下开始按钮后开始计时,按下停止按钮后输出中间间隔的时间。
  2. 写一个 todo 列表页面,包含一个列表、一个输入框、一个添加按钮。点击添加按钮可以把输入框的内容添加进列表。
  3. 写一个抽奖页面,包含抽奖按钮、结果列表。点击抽奖按钮随机产生抽奖结果,并将结果添加进列表中,要求中奖几率为20%。
  4. 第二题的扩展,增加清除按钮,点击后可清空列表。
  5. 第二题的扩展,增加完成功能,即点击某个列表项后,改项目变为完成状态,颜色变灰,并添加删除线。
  6. 上次作业计时器页面的扩展,增加控制按钮:进入页面时时间静止,按钮为开始;点击后时间开始动,按钮为停止;再次点击后时间停止,按钮为开始。如此循环。
  7. 写一个归类列表页面,包含一个输入框、两个按钮和两个列表。输入内容后,(内容非空时)点击按钮1内容进入列表1,点击按钮2内容进入列表2。

预告

下节课开始进入一些综合实战的部分。

Fighting!

2015.01.11