构建交互网站

在本单元中,你将学习如何使用 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> 元素的两个新属性:defer 和 async,它们可以根据不同的场景来优化网站的加载体验,减少用户等待时间。

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> 中,并结合使用 defer 或 async 属性。

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 允许我们访问节点的各种属性,比如它的 class、id 和行内样式(inline style)。

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

总结

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

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

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

修改元素

在脚本中使用 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 属性。通过将这个属性设置为 true 或 false,可以控制元素的显示与隐藏:

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 事件就会被触发。

鼠标事件

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

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

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

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

    它和 click 或 mousedown 都不同,因为 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 事件 会在用户按下并松开一个键 时触发。

    它不同于 keydown 和 keyup 组合起来的效果,因为 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() 方法可以停止特定的事件处理器监听特定的事件。
相关推荐
能源革命11 分钟前
Vue 网格拖拽布局组件全景指南:7 大方案深度对比
前端·javascript·vue.js
deli00734 分钟前
Skill 怎么写才被 Agent 命中?18 条规则做成 SKILL.md 校验器,规范样例 92 分
前端
Hum8le35 分钟前
CTF题目《easy_web》(安洵杯 2019 变种 Web)
前端·安全·web安全
coding漫漫长路35 分钟前
二值化后还剩1212个黑点,OCR却认成了乱码
前端
不可能片场37 分钟前
AI视频口型对齐 用音频驱动说话镜头
前端·electron
Lank_M37 分钟前
OCR一个字没认错,倾斜2.5°却把行序排乱了
前端
daols8841 分钟前
vue 表格组件 vxe-table 实现自定义多行编辑功能
前端·javascript·vue.js·vxe-table
不要试图纠正别人1 小时前
别让大模型算钱:我把优惠券组合优化写成了确定性算法
前端
IT_Octopus1 小时前
【零基础入门 LLM 开发 · Day 11】:ChatOpenAI vs init_chat_model——一行换供应商
java·前端·javascript
柚yuzumi1 小时前
React 19 Hooks 入门:函数组件的四大法宝 (useState / useEffect / useRef / useContext)
前端·javascript·架构