构建交互网站

在本单元中,你将学习如何使用 JavaScript 构建交互式网站。

本单元的目标是了解 JavaScript 如何为网站添加交互体验。

完成本单元后,你将能够:

  • 为网站添加 JavaScript 以实现交互功能
  • 描述什么是 DOM
  • 解释什么是 DOM 事件
  • 使用 HTML 创建表单,并用 JavaScript 对其进行验证

JavaScript and HTML

HTML 使用页面元素作为构建模块来定义网页的结构。然而,仅靠 HTML 并不能实现网页的交互功能,这正是 JavaScript 发挥作用的地方。

下面我们看到一张便签纸,上面画着一个典型的小火柴人。我们可以把它看作 HTML,其中的头部、身体和四肢就像网页上的元素:

在网页开发中,CSS 为 HTML 的结构添加样式。如下图所示,小火柴人现在穿上了一套漂亮的礼服:

如果说 HTML 和 CSS 分别提供了网页的"结构"和"样式",那么 JavaScript 则赋予了网页"交互性",让小火柴人动起来。下面,小火柴人开始上下摆动,这就是 JavaScript 的功劳:

网页开发者使用 JavaScript 让网页变得动态且具有交互性。这种强大的脚本语言通过一个专属的 HTML 元素来嵌入页面:<script> 元素。你可以把 <script> 元素看作 HTML 连接 JavaScript 的"门户"。本课将深入讲解 <script> 元素在网页中能发挥什么作用,以及在 HTML 文件中插入 JavaScript 的最佳实践。

script标签

<script> 元素允许你在 HTML 文件中添加 JavaScript 代码。下面的例子展示了如何使用 <script> 元素嵌入合法的 JavaScript 代码:

html 复制代码
<h1>This is an embedded JS example</h1>
<script>
  function Hello() {
    alert('Hello World');
  }
</script>

说实话,没有 <script> 标签,网站将变得无法点击,甚至有些无趣。

<script> 元素和大多数 HTML 元素一样,有一个开始和结束的尖括号(标签)。结束标签标志着 <script> 元素中内容的结束。就像我们用 <style> 标签嵌入 CSS 代码一样,你可以使用 <script> 标签嵌入合法的 JavaScript 代码。

例如:

html 复制代码
<!DOCTYPE html>
<html>
<head>
	<link rel="stylesheet" href="style.css">
</head>
  
<body>
	<section class = "container">
  	<img src = "normal.png" id= "myImage">
  	<p onclick="blooming()">Codecademy</p>
	</section>

  <!-- Paste your code in the script element below:  -->
  <script>
  function blooming() {
  var image = document.getElementById('myImage');
  if (image.src.match("normal")) {
    image.src = "flower.png";
  } else {
    image.src = "normal.png";
  }	
}	

  </script>
</body>  
</html>

src属性

既然你已经知道如何在 <script> 元素中嵌入代码,现在我们来说说"链接"代码。链接代码更受欢迎,是因为一个编程概念:关注点分离(Separation of Concerns,简称 SoC)。与其把所有混乱的代码都写在一个文件里,Web 开发者更倾向于将代码拆分到不同的文件中,这样每一部分的"职责"就更清晰,也更方便在需要更改时进行维护。

在本练习中,我们不会在 HTML 文件里写 JavaScript,而是会把它写进自己的文件中,然后通过文件路径来引用这段代码。我们会用到一个你可能已经熟悉的属性:src 属性!

如果你觉得这个属性眼熟,那是因为你可能已经用 <img><link> 元素链接过外部文件了。这个属性的用法是一样的,只不过这次它的值指定的是脚本文件的位置。

如果脚本文件和 HTML 文件在同一个项目文件夹中,src 的值就会是一个相对路径名。下面是一个为 JavaScript 文件提供相对路径的例子:

html 复制代码
<script src="./exampleScript.js"></script>

上面的 <script> 标签会去查找一个名为 exampleScript.js 的文件,该文件应当和 index.html 文件位于同一文件夹或目录中。

如果你需要引用托管在外部(如 CDN)上的 JavaScript 文件,你也可以将 src 设置为该文件的完整链接地址。

script是如何加载的?

快速回顾一下:<script> 元素允许 HTML 文件加载并执行 JavaScript。JavaScript 可以直接写在 <script> 标签内部,也可以通过引用外部文件来加载脚本。在深入了解之前,我们先花点时间了解一下浏览器是如何将 HTML 文件解析为网页的,这会帮助我们明白该把 <script> 标签放在哪里。

浏览器内置了 HTML 解析器,用来帮助浏览器按照规则渲染网页中的各个元素。HTML 文件中的元素(包括 <script> 元素)默认是按照它们在 HTML 文件中出现的顺序 进行解析的。当解析器遇到一个 <script> 元素时,它会先加载并执行该脚本的内容,然后才继续解析 HTML 的其余部分。

这里有两个重点要注意:

  1. HTML 解析器在加载并执行 <script> 元素之前,不会处理下一个 HTML 元素,这会造成网页加载的延迟,从而带来不好的用户体验。
  2. 脚本是按顺序加载的,所以如果某个脚本依赖于另一个脚本,那么它们在 HTML 中的顺序也必须正确。

defer属性

当 HTML 解析器遇到一个 <script> 元素时,它会暂停解析 HTML,去加载该脚本的内容。脚本加载完成后,JavaScript 代码会立即执行,然后 HTML 解析器才会继续处理接下来的内容。这种行为可能导致网页加载速度变慢。

为了解决这个问题,HTML4 引入了 <script> 元素的两个新属性:deferasync,它们可以根据不同的场景来优化网站的加载体验,减少用户等待时间。

defer 属性的作用是:在 HTML 文件完全解析完成之后再执行脚本 。当 HTML 解析器遇到带有 defer 属性的 <script> 元素时,它会开始加载脚本文件,但不会立刻执行,而是等到整个 HTML 都解析完毕后再执行该脚本。

下面是一个使用 defer 属性的例子:

css 复制代码
<script src="example.js" defer></script>

当脚本中包含需要与 DOM 交互的功能时,使用 defer 是非常合适的。这样可以确保整个 HTML 文件已经解析完成,脚本执行时 DOM 是完整可用的。

async属性

async 属性可以异步加载并执行脚本 ,与网页的其他部分同时进行。也就是说,与 defer 属性类似,HTML 解析器在加载脚本的同时还会继续解析 HTML 页面,不会暂停等待脚本加载完成。

但与 defer 不同的是 ,使用 async 属性时,脚本在下载完成后就会立刻执行不等 HTML 页面完全解析完成

下面是一个使用 async 属性的例子:

html 复制代码
<script src="example.js" async></script>

async什么时候用?

async 非常适用于不依赖其他脚本,也不需要等待 DOM 加载完成的脚本,比如第三方广告、数据统计工具(如 Google Analytics)等。

如果脚本的执行时机无所谓,并且它是独立的、不影响页面主功能,那么使用 async 是最合适的方式,可以有效优化网页加载速度

总结:

  • HTML 构建网页的骨架 ,但 JavaScript 引入了交互性

  • <script> 元素有开始标签和结束标签。你可以在这两个标签之间嵌入 JavaScript 代码。

  • 如果要链接外部的 JavaScript 文件,可以在 <script> 标签的开始标签中使用 src 属性

  • 默认情况下 ,浏览器的 HTML 解析器在遇到 <script> 标签时,会立即加载并执行其中的脚本,然后才继续解析页面中剩余的元素。

  • 使用 defer 属性,可以确保整个 HTML 文件解析完成后再执行脚本。

  • 使用 async 属性,浏览器在下载脚本的同时会继续解析 HTML 页面,一旦脚本下载完成就立即执行,而不是等待页面解析完。

  • 旧的做法是将 <script> 标签放在 </body> 标签之前,以防止脚本阻塞页面加载。现在的推荐做法 是将 <script> 标签放在 <head> 中,并结合使用 deferasync 属性。

dom

什么是dom

dom = document object model

dom是浏览器对网页的内部表示,每个页面都依赖于dom来准确描述页面上的内容,以及这些内容与其他内容之间的关系。

web page = document

网页简称为文档,文档一词实际上指的是任何构建信息的方式,书籍、文章、论文等等。但从文档开发的角度来看,文档是网页的另一种说法,因此,dom是网页或文档中所有内容的模型。

网页上的内容就是对象,至少dom是这么称呼它们的。你可能听说过节点或者元素,它们用来描述网页上的内容,这些都是对象的类型,但这些对象到底是什么呢?

网页上最明显的东西就是内容,每个页面都有文字内容,还包括图片、视频、按钮等,所有这些都被视为对象。你可能也听说过这些东西被称为元素,它是一种特定的对象。

另一种东西是结构元素,比如div容器,你可能真的能看到网页上所有的结构对象,也可能看不到,但它们是组织内容所必需的。

剩下的东西主要是属性,每个元素都具有属性,例如html元素可以有类、样式、大小等。所有这些属性都是文档对象模型中的对象,但它们不是像内容或者结构这样的元素。

model:某种东西的表示,它可以帮助我们理解某种东西是如何组合在一起的。你能想象到的任何东西都有模型,因为复杂的东西需要某种方式来被普遍理解、分析和使用。

dom是网页的模型,是一种对文档中的对象进行建模的方法。或者更简单的说,dom代表网页上的元素和属性。

dom是一种树形数据结构,这意味着每个对象都在另一个对象下有层次。一个对象可以有多个子对象,但只能有一个父对象。当我们讨论父对象和子对象时,我们指的是对象之间的关系。

父对象拥有其子对象列表,如果从这棵树上移除任何对象,它的所有子对象,子对象的子对象都会被移除

浏览器通过实现 DOM,使得 JavaScript 可以以有组织的方式访问、修改和更新 HTML 网页的结构。

因此,我们可以把 DOM 看作 HTML 网页与脚本语言之间的桥梁。

dom作为树形结构

树状建模在许多领域中都有应用,包括进化科学和数据分析。也许你已经熟悉"家谱"这一概念:它通过图表来展示某个家族中后代之间的亲属关系。

DOM 树的逻辑与家谱图类似。家谱由家庭成员及其与家族姓氏的关系构成。在计算机科学中,我们会将每个家庭成员称为一个"节点"。

我们将"节点"定义为树状结构中的交叉点,每个节点都包含数据。

在 DOM 树中,最顶层的节点被称为"根节点"(root node),它代表整个 HTML 文档。根节点的后代是文档中的 HTML 标签,从 <html> 标签开始,接着是 <head><body> 标签,依此类推。

父节点(parent node) :指的是某个节点的直接上级节点,也就是它的祖先之一。

子节点(child node) :指的是某个节点的直接下级节点,其上级被称为父节点。

了解这些术语有助于你理解并讨论 DOM 这种树状结构。事实上,当我们讨论 HTML 代码的嵌套结构时,也会使用这些术语。程序员通常将嵌套在其他元素内部的元素称为"子元素",而包含它们的元素称为"父元素"。

dom中的节点和元素

如前所述,节点就相当于家谱中的每一个家庭成员。节点是树状结构中的一个交叉点,同时也包含数据。

在 DOM 树中存在多种类型的节点对象。在我们的示意图中,带有尖角矩形的表示元素节点(Element nodes) ,而带有圆角矩形的表示文本节点(Text nodes) ,因为它们代表的是 HTML 段落元素中的文字内容。

在修改网页时,脚本通常主要与 DOM 中的元素节点 交互,有时也会操作文本节点

元素节点的属性

DOM 中的元素节点用于模拟 HTML 文档中的各个元素。

就像 HTML 页面中的元素一样,DOM 允许我们访问节点的各种属性,比如它的 classid 和行内样式(inline style)。

在右侧的示意图中,我们高亮显示了 HTML 文档中一个 id'bio' 的段落元素。如果我们在脚本中访问这个元素节点,DOM 就可以让我们修改这些属性,或者直接读取它们的值以供代码使用。

总结

让我们回顾一下你目前学到的内容:

  • DOM 是网页的结构化模型,它允许脚本语言访问该页面。
  • DOM 的组织系统模仿了 HTML 文档的嵌套结构。
  • 被嵌套在某个元素中的元素称为该元素的子元素 ,而包含它们的元素则被称为它们的父元素
  • DOM 还允许访问 HTML 元素的各种属性,比如 styleid 等。

通过这些知识,你就可以开始运用脚本语言的强大功能来创建更新维护网页啦!

修改元素

在脚本中使用 DOM 访问某个 HTML 元素时,无论是一个 <li> 元素,还是整个 <body> 元素,你都可以访问该元素的所有属性。

这意味着你可以修改该元素的内容、属性以及其他特性,比如更改 <p> 元素中的文字,或为 <div> 元素设置新的背景颜色。例如,.innerHTML 属性可以让你访问或设置一个元素的内容。

来看一个例子,我们将 <body> 元素的内容重新赋值为文本 'The cat loves the dog.'

javascript 复制代码
document.body.innerHTML = 'The cat loves the dog.';

.innerHTML 属性也可以插入任何合法的 HTML 元素。下面这个例子将 <body> 元素的内容替换为一个 <h2> 元素,也就是给 <body> 添加了一个子元素:

js 复制代码
document.body.innerHTML = '<h2>This is a heading</h2>';

选择并修改元素

在上一个练习中,我们使用 document 关键字访问了 <body> 元素!

那么,如果我们想选择 <body> 之外的某个特定元素该怎么办呢?DOM 接口允许我们使用 CSS 选择器 来访问特定的元素。

CSS 选择器通常用于定义哪些元素应用某组 CSS 样式,但我们也可以使用这些相同的选择器,通过 JavaScript 来访问 DOM 元素!选择器可以是标签名、类名,或 ID。

.querySelector() 方法允许我们将一个 CSS 选择器作为字符串传入,并返回匹配该选择器的第一个元素 。下面的代码将返回文档中的第一个段落元素

javascript 复制代码
document.querySelector('p');

除了 .querySelector() 之外,JavaScript 还提供了更多有针对性的方法,允许我们根据元素的类名、ID 或标签名来选择元素。

例如,如果你想通过元素的 ID 直接访问它,可以使用命名直观的 .getElementById() 方法:

js 复制代码
document.getElementById('bio').innerHTML = 'The description';

在这个例子中,我们选中了 ID 为 'bio' 的元素,并将它的 .innerHTML 设置为 'The description'。注意,这里的 ID 是以字符串形式传入的(用引号括起来)。

此外,还有 .getElementsByClassName().getElementsByTagName() 方法,这两个方法返回的是一个元素数组,而不是单个元素。你可以使用中括号([])来访问数组中的某个具体元素:

js 复制代码
// 将第一个 class 为 student 的元素内容设置为 'Not yet registered'
document.getElementsByClassName('student')[0].innerHTML = 'Not yet registered';

// 将第二个 <li> 标签的内容设置为 'Cedric Diggory'
document.getElementsByTagName('li')[1].innerHTML = 'Cedric Diggory';

在上面的示例中,我们修改了第一个类名为 'student' 的元素和第二个 <li> 元素的 HTML 内容。

给元素设置样式

另一种修改元素的方法是更改它的 CSS 样式。DOM 元素的 .style 属性提供了对该 HTML 标签内联样式的访问。

语法遵循 element.style.property 的格式,其中 property 表示一个 CSS 属性。例如,以下代码选择了第一个类名为 blue 的元素,并将其 background-color 设置为蓝色:

js 复制代码
let blueElement = document.querySelector('.blue');
blueElement.style.backgroundColor = 'blue';

与 CSS 不同的是,DOM 中的 .style 属性不使用连字符 (如 background-color),而是使用驼峰式命名法(camelCase),即 backgroundColor。你可以参考 W3C 的 HTML DOM Style 对象文档,了解各种 CSS 属性在 JavaScript 中是如何转换的。

下面这种链式写法也是可以的:

css 复制代码
document.querySelector('.blue').style.fontFamily = 'Roboto';

遍历 DOM

让我们来回顾一下 DOM 层级结构中的父子关系:

  • 父节点(parent node) 是某个节点的直接祖先;
  • 子节点(child node) 是某个节点的直接后代,该节点称为该子节点的父节点。

这种关系遵循 HTML 代码的嵌套结构:如果某个元素嵌套在另一个 HTML 元素中,那么它就是该元素的子元素。

每个元素都有 .parentNode.children 属性:

  • .parentNode 返回指定元素在 DOM 层级中的父节点。注意:document 元素是根节点,因此它的 .parentNode 返回 null
  • .children 返回该元素的所有子节点组成的 类数组对象 (不是标准数组)。如果该元素没有任何子节点,则返回 null

来看下面这段 HTML:

html 复制代码
<ul id='groceries'>
  <li id='must-have'>Toilet Paper</li>
  <li>Apples</li>
  <li>Chocolate</li>
  <li>Dumplings</li>
</ul>

在上面的代码中,有一个 ID 为 groceries<ul> 元素,内部包含了四个 <li> 元素。

对应的 JavaScript 代码如下:

js 复制代码
let parentElement = document.getElementById('must-have').parentNode; 
// 返回 <ul> 元素

let childElements = document.getElementById('groceries').children; 
// 返回一个包含四个 <li> 元素的"类数组"

解释:

  • parentElement 获取的是 ID 为 must-have<li> 元素的父节点,也就是 ID 为 groceries<ul> 元素;
  • childElements 获取的是 ID 为 groceries<ul> 元素的子节点们,即四个 <li> 元素。

创建并插入元素

就像 DOM 允许脚本修改已有元素一样,它也允许创建新的元素。

.createElement() 方法可以根据传入的标签名参数创建一个新的元素。不过,它不会自动将这个元素添加到文档中。它只会创建一个没有内部 HTML 内容的空元素。

js 复制代码
let paragraph = document.createElement('p');

在上面的示例代码中,.createElement() 方法传入 'p' 作为参数,这将创建一个空的 <p> 元素,并将其赋值给变量 paragraph

我们可以像以前修改已有元素一样,为新创建的元素设置属性:

js 复制代码
paragraph.id = 'info'; 
paragraph.innerHTML = 'The text inside the paragraph';

上面的代码中,我们使用 .id 属性将 'info' 设置为这个元素的 ID,并使用 .innerHTML 属性设置 <p> 元素的内容为 'The text inside the paragraph'

为了将新创建的元素添加到网页中,必须将其指定为一个已存在的 DOM 元素的子元素,我们称这个已有元素为"父元素"。这个过程称为"追加"。.appendChild() 方法会将子元素添加为父元素的最后一个子节点。下面的代码将存储在 paragraph 变量中的 <p> 元素追加到 document.body 上:

js 复制代码
document.body.appendChild(paragraph);

.appendChild() 方法不会替换父元素(在这个例子中是 body)中的内容,而是将新元素追加为该父元素的最后一个子元素。

移除元素

除了可以修改或从头创建一个元素,DOM 还允许我们移除某个元素。.removeChild() 方法可以从父元素中移除指定的子元素。

javascript 复制代码
let paragraph = document.querySelector('p');
document.body.removeChild(paragraph);

在上面的示例代码中,.querySelector() 方法返回文档中的第一个 <p> 元素。然后,这个 paragraph 元素作为参数传递给其父元素(document.body)调用的 .removeChild() 方法,从而将这个段落从文档的 <body> 中移除。

如果你只是想隐藏一个元素,而不是完全删除它,可以使用 .hidden 属性。通过将这个属性设置为 truefalse,可以控制元素的显示与隐藏:

js 复制代码
document.getElementById('sign').hidden = true;

上面的代码并没有从 DOM 中移除 ID 为 'sign' 的元素,而是将它隐藏了起来。

添加点击交互

你可以通过为 DOM 元素指定一个函数,在特定事件发生时运行,从而为它添加交互功能。事件可以包括点击、鼠标悬停等操作。我们将在后面的"使用 JavaScript 操作 DOM 事件"课程中更深入地了解事件的使用。现在,我们先来看一下在点击事件发生时如何修改一个元素。

.onclick 属性允许你为元素指定一个在点击事件发生时执行的函数:

js 复制代码
let element = document.querySelector('button');

element.onclick = function() { 
  element.style.backgroundColor = 'blue'; 
};

你也可以将 .onclick 属性赋值为一个已命名的函数:

javascript 复制代码
let element = document.querySelector('button');

function turnBlue() {
   element.style.backgroundColor = 'blue';
}

element.onclick = turnBlue;

在上面的示例代码中,当 <button> 元素检测到点击事件时,其背景颜色会变成 'blue'

总结

在本节课中,你通过 JavaScript 的 DOM(文档对象模型)接口操作了网页的结构。

我们来回顾一下学到的内容:

  • document 关键字可以让你访问 JavaScript 中 DOM 的根节点。
  • DOM 接口允许你使用 .querySelector() 方法,通过 CSS 选择器选中一个特定的元素。
  • 你可以使用 .getElementById() 方法直接通过元素的 ID 获取该元素,该方法返回一个单独的元素。
  • 你可以使用 .getElementsByClassName().getElementsByTagName() 方法获取一个元素的数组,然后通过数组下标来引用其中的某个元素。
  • .innerHTML.style 属性分别可以修改元素的内容和样式。
  • 你可以使用 .createElement().appendChild().removeChild() 方法分别来创建、添加和移除元素。
  • .onclick 属性可以为 DOM 元素添加点击事件,实现交互功能。
  • .children 属性返回一个元素的所有子元素列表,而 .parentNode 属性则返回该元素向上查找的最近的父节点。

这些知识点可以帮助你用 JavaScript 动态地操作网页内容和结构,打造更加丰富的用户体验。

js的dom事件

什么是事件

当你刷新邮箱、双击帖子,或滚动浏览新闻推送时------浏览器里就会发生一些很酷的事情。这些操作就叫做"事件"!

网页上的事件是指用户的交互行为或浏览器的动作,你可以通过编程方式来响应这些事件,从而触发相应的功能。下面是一些常见事件的例子:

  • 鼠标点击按钮
  • 网页文件在浏览器中加载完成
  • 用户在图片上向右滑动

当用户执行上述任意操作时,他们就"触发"了一个事件。比如,"点击按钮时触发了一个点击事件"。能够响应这些事件的网页,才是有交互性和动态体验的网页。

触发事件

在文档对象模型(DOM)中的某个特定元素上触发特定事件后,可以创建一个**事件处理函数(event handler function)**来作为响应执行操作。在本课中,我们将学习这些事件处理函数是如何在事件触发后修改和更新 DOM 元素的。

我们可以将事件的触发类比为一个更熟悉的场景:一只听到铃声就会开始吃东西的狗!(这被称为巴甫洛夫条件反射。)

如图所示,铃声的响起就像是 JavaScript 中事件的触发。而狗狗被训练去响应铃声,这就好比是在设置一个事件监听器(event listener) 。当狗狗听到铃声后,它就会走过来吃食物!这个反应就类似于事件处理函数的执行,它会运行代码,比如在网页上改变元素的颜色、文本,等等。

大多数浏览器中的事件都会悄悄发生------因为没有绑定事件处理函数,这些事件就不会被"察觉"或响应。而事件处理函数正是使用 JavaScript 创建交互式网站的关键所在。

注册事件处理程序

现在是时候深入了解如何使用事件处理函数来创建交互效果了。

使用 .addEventListener() 方法,我们可以让一个 DOM 元素监听某个特定事件 ,并在该事件被检测到时执行一段代码。这个监听事件的 DOM 元素被称为事件目标(event target) ,而当事件发生时运行的那段代码被称为事件处理函数(event handler)

我们来看下面这段代码:

js 复制代码
let eventTarget = document.getElementById('targetElement');

eventTarget.addEventListener('click', function() {
  // 当 eventTarget 元素上发生点击事件时,这段代码会被执行
});

我们来逐步解析一下这段代码:

  • 我们使用 document.getElementById('targetElement') 从 DOM 中选中了我们的事件目标。
  • 然后在这个 eventTarget 元素上使用了 .addEventListener() 方法。
  • .addEventListener() 方法接受两个参数:一个是表示事件名称的字符串,另一个是事件处理函数。我们将在后面的课程中了解可以使用哪些不同的事件名称。
  • 在这个例子中,我们使用了 'click' 事件,也就是当用户点击 eventTarget 元素时会触发的事件。
  • 当监听到 'click' 事件时,事件处理函数中的代码块就会被执行。

虽然在 .addEventListener() 方法中直接使用**匿名函数(anonymous function)是可以的,但最佳实践是使用具名函数(named function)**来作为事件处理函数。这样你的代码会更加有条理、更易复用,即使代码变得复杂也更容易维护。

来看使用具名函数的语法示例:

javascript 复制代码
function eventHandlerFunction() {
  // 当点击事件发生时,这段代码将被执行
}

eventTarget.addEventListener('click', eventHandlerFunction);

在这个例子中,我们将具名函数 eventHandlerFunction 作为 .addEventListener() 方法的第二个参数传入,而不是在方法内部定义一个匿名函数!

addEventListener('click', eventHandlerFunction)和onclick(eventHandlerFunction)有什么区别

  1. 是否可以绑定多个事件处理函数

onclick 只能绑定一个 函数。后绑定的会覆盖掉前面的:

js 复制代码
eventTarget.onclick = function () { console.log('第一次'); };
eventTarget.onclick = function () { console.log('第二次'); }; // 覆盖了上面的
// 点击时只会打印「第二次」

addEventListener 可以绑定多个函数,互不影响:

js 复制代码
eventTarget.addEventListener('click', function () { console.log('第一次'); });
eventTarget.addEventListener('click', function () { console.log('第二次'); });
// 点击时会依次打印「第一次」「第二次」
  1. 支持的时间类型和高级功能

addEventListener 支持更多功能,比如监听事件捕获阶段(第三个参数),或指定只触发一次({ once: true })等高级配置。

javascript 复制代码
eventTarget.addEventListener('click', handler, { once: true });

onclick 只支持冒泡阶段,不能配置这些高级功能。

  1. 移除事件监听的方式不同

addEventListener 绑定后可以用 removeEventListener 精确移除:

javascript 复制代码
function handleClick() {
  console.log('clicked');
}

eventTarget.addEventListener('click', handleClick);
eventTarget.removeEventListener('click', handleClick);

onclick 想"移除"只能设置为 null 或重新赋值:

javascript 复制代码
eventTarget.onclick = null;

添加事件处理程序

我们已经学习了如何使用 .addEventListener() 方法来注册事件处理程序,但其实还有另一种方法!

事件处理程序也可以通过在 DOM 元素(事件目标)上设置 .onevent 属性的方式来注册。注册特定事件的模式是:给一个元素添加 .on 加上小写的事件类型名称

例如,如果我们想要注册一个点击事件(click event),可以这样写:

js 复制代码
eventTarget.onclick = eventHandlerFunction;

在这个例子中,我们给 DOM 元素 eventTarget 设置了 .onclick 属性,并将其值设置为事件处理函数 eventHandlerFunction

需要注意的是:.onevent 属性和 .addEventListener() 方法都可以注册事件监听器。但是:

  • 使用 .onevent(比如 .onclick)的方式只能为一个事件目标附加一个事件处理函数,后添加的会覆盖前一个;
  • 而使用 .addEventListener() 方法,可以附加多个事件处理函数,它们都会被依次调用。

在后续的练习中,我们会主要使用 .addEventListener() 的语法,不过我们也想介绍 .onevent 的用法,因为这两种方式在实际开发中都非常常见。

移除事件处理程序

.removeEventListener() 方法用于撤销 .addEventListener() 方法的效果。这个方法可以让事件目标不再"监听"某个事件的触发,当事件监听不再需要时,就可以使用它。

.removeEventListener() 方法也需要两个参数:

  1. 事件类型(以字符串形式表示)
  2. 要移除的事件处理函数

来看一个使用 .removeEventListener() 方法移除点击事件的语法示例:

js 复制代码
eventTarget.removeEventListener('click', eventHandlerFunction);

由于一个特定事件可能绑定了多个事件处理函数,.removeEventListener() 必须提供完全相同的事件类型名称你想移除的事件处理函数的函数名,才能成功移除。

⚠️ 注意:如果你在 .addEventListener() 中使用的是匿名函数 ,那么这个事件监听器是无法被移除的

事件对象的属性

JavaScript 会将事件保存为 事件对象(Event object) ,其中包含与该事件相关的数据和功能,这些内容以属性和方法的形式存在。

当某个事件被触发时,事件对象可以作为一个 参数 传递给事件处理函数。

js 复制代码
function eventHandlerFunction(event){
   console.log(event.timeStamp);
}

eventTarget.addEventListener('click', eventHandlerFunction);

在上面的示例中,当 eventTarget 上触发 'click' 事件时,eventHandlerFunction 函数会接收一个名为 event 的参数。这个参数就是一个 事件对象 ,**它包含了关于 'click' 事件的相关信息。 ** 接着,我们通过访问事件对象的 .timeStamp 属性,打印出从文档加载完成到该事件被触发所经过的时间(以毫秒为单位)。

事件对象中有一些预定义的属性,你可以调用它们来查看与事件有关的信息,例如:

  • .target 属性:引用事件被注册的那个元素;
  • .type 属性:获取事件的名称(比如 'click');
  • .timeStamp 属性:获取从文档加载完成到事件触发之间经过的毫秒数。

隐藏注册的元素:

js 复制代码
event.target.style.display = 'none';

事件类型

除了点击(click)事件之外,浏览器中还有各种各样的 DOM 事件可以被触发!

了解这一点很重要:大多数 DOM 中的事件都会在没有被察觉的情况下发生,这是因为没有为它们绑定事件处理器。

同样也要注意,有些注册的事件不依赖用户的交互 也能被触发。

例如,load 事件会在网站的文件完全加载到浏览器之后自动触发。

浏览器还可以在没有用户操作的情况下触发很多其他事件 ------ 你可以在 MDN 的 Events Reference 页面 上查看这些事件的列表。

当然,很多事件还是需要用户与 DOM 进行交互后才会触发。

你已经熟悉的一个用户交互事件就是 click 事件 ------ 当用户在 DOM 元素上按下并释放鼠标按钮 时,click 事件就会被触发。

鼠标事件

当你在网站上使用鼠标设备时,会触发各种鼠标事件。你已经见过 clickwheel 事件,但实际上,还有更多鼠标相关的事件可以探索!

  • mousedown 事件 会在用户按下鼠标按钮 时触发。

    这和 click 事件不同,因为 mousedown 不需要等到鼠标按钮释放就会触发。

  • mouseup 事件 会在用户松开鼠标按钮 时触发。

    它和 clickmousedown 都不同,因为 mouseup 不依赖鼠标是否按下才能触发。

  • mouseover 事件 会在鼠标进入某个元素的内容区域时触发。

  • mouseout 事件 会在鼠标离开某个元素时触发

js 复制代码
function increaseWidth() {
  itemOne.style.width = '401px';
}

itemOne.addEventListener('mouseover', increaseWidth);

function changeBackground(){
  itemTwo.style.backgroundColor = 'blue';
}

itemTwo.addEventListener('mouseup', changeBackground);

function changeText(){
  itemThree.innerHTML = 'The mouse has left the element';
}

itemThree.addEventListener('mouseout', changeText);

function showItem(){
  itemFive.style.display = 'block';
}

itemFour.addEventListener('mousedown', showItem);

键盘事件

除了鼠标事件,键盘事件 也是非常常见的一类事件!
键盘事件是由用户在浏览器中与键盘键交互时触发的。

  • keydown 事件 会在用户按下某个键 时触发。
    (Key Down 事件图示)

  • keyup 事件 会在用户松开某个键 时触发。
    (Key Up 事件图示)

  • keypress 事件 会在用户按下并松开一个键 时触发。

    它不同于 keydownkeyup 组合起来的效果,因为 keypress一个完整的事件

键盘事件拥有其独特的事件对象属性,比如 .key 属性,它用于存储用户按下的键的值

你可以在事件处理函数中编程,让它:

  • 某个特定键作出响应,或者
  • 任意键盘操作都作出响应。
js 复制代码
let ball = document.getElementById('float-circle');

// Write your code below
function up(){
  ball.style.bottom = '250px';
}

function down(){
  ball.style.bottom = '50px';
}

document.addEventListener('keydown', up);

document.addEventListener('keyup', down);

document 代表整个文档对象模型(DOM)------将 document 作为事件目标,意味着你可以在 DOM 的任何地方触发事件。

你可以使用 .addEventListener() 方法或 .on[event] 属性来为事件类型 keydown 注册你的事件处理函数。

总结

我们来回顾一下你学到的内容:

  • 你可以使用 .addEventListener() 方法将事件注册到 DOM 元素上。
  • .addEventListener() 方法接受两个参数:事件类型 和 事件处理函数。
  • 当事件在事件目标上被触发时,注册的事件处理函数会被执行。
  • 事件处理函数也可以作为事件目标的 .onevent 属性值进行注册。
  • 事件对象的属性(如 .target.type.timeStamp)可以提供关于事件的信息。
  • .addEventListener() 方法可以为同一个事件添加多个事件处理函数。
  • .removeEventListener() 方法可以停止特定的事件处理器监听特定的事件。
相关推荐
巴巴_羊30 分钟前
yarn npm pnpm
前端·npm·node.js
chéng ௹2 小时前
vue2 上传pdf,拖拽盖章,下载图片
前端·css·pdf
嗯.~2 小时前
【无标题】如何在sheel中运行Spark
前端·javascript·c#
A_aspectJ5 小时前
【Bootstrap V4系列】学习入门教程之 组件-输入组(Input group)
前端·css·学习·bootstrap·html
兆。5 小时前
电子商城后台管理平台-Flask Vue项目开发
前端·vue.js·后端·python·flask
互联网搬砖老肖5 小时前
Web 架构之负载均衡全解析
前端·架构·负载均衡
sunbyte6 小时前
Tailwind CSS v4 主题化实践入门(自定义 Theme + 主题模式切换)✨
前端·javascript·css·tailwindcss
湛海不过深蓝7 小时前
【css】css统一设置变量
前端·css
程序员的世界你不懂7 小时前
tomcat6性能优化
前端·性能优化·firefox
爱吃巧克力的程序媛7 小时前
QML ProgressBar控件详解
前端