本文是系列文章的一部分:Web 基础知识
DOM 代表网页的结构,使开发人员能够实时修改内容、样式和元素。在本文中,我们将探讨 DOM 操作的基础知识,并为您提供增强 Web 开发项目所需的基本技术和示例。
我们将在本文中学习什么
-
操作 DOM 元素
- 创建元素
- 选择元素
- 添加和删除
styles
esclass
和内容 - 监听事件
-
数据加载
document
document
是你与 DOM 及其中加载的所有内容进行交互的方式。我们使用document.
前缀来操作构成我们正在查看的网页的所有元素。
理解 DOM
如果你还没读过上一章,现在是时候读一读了。我们将直接深入探讨如何操作 DOM,但理解浏览器显示内容背后的背景也很重要。
创建元素
我们可以使用该方法创建 HTML 元素createElement('tag')
。让我们创建一个由<p>
标签指定的新段落。
js
const newParagraph = document.createElement('p');
newParagraph.textContent = "This is a new paragraph.";
但这只是在const
我们创建的文档中存储了一个新元素。为了显示内容,我们必须将一个元素附加到文档中。
我们将新段落附加到<body>
标签中。
js
const newParagraph = document.createElement('p');
newParagraph.textContent = "This is a new paragraph.";
document.body.appendChild(newParagraph);
如果我们查看我们的文档,我们现在会看到我们的段落是正文的一部分。
我们可以使用createElement('tag')
HTML 来创建任何支持的标签。在第一章 HTML 中,我们讲解了语义元素。记得回顾一下!
选择元素
在上面的演示中,我们已经利用了 JavaScript 的选择方法来使我们的演示具有交互性。但这里列出了我们可以从 DOM 获取元素的方法。
方法 | JavaScript | 返回 |
---|---|---|
选择依据id |
document.getElementById("myElement") |
唯一的 HTMLElement |
选择依据class |
document.getElementsByClassName("myClass") |
HTMLCollection(实时) |
选择依据<tag> |
document.querySelectAll("p") |
节点列表(静态) |
操作元素
现在我们了解了如何选择元素并将它们绑定到变量,我们可以开始以任何我们想要的方式操作它们。
我们现在将使用在 Web 基础知识中学到的所有内容 --- --- 变量、样式、类、继承,并通过 JavaScript 应用它们!
添加和删除样式
CSS 属性被称为styles
,我们可以通过 JavaScript 访问它们。
在 CSS 中,属性显示如下:
css
body { background-color: lightblue;}
在 JavaScript 中访问属性时,我们使用不同的语法,即驼峰式命名法。
使用document
,我们可以访问和应用我们想要的任何样式。
ini
document.body.style.backgroundColor = "lightblue";
然而,也有一些限制。JavaScript 与 CSS 不同,没有简写。我们以padding
为例。
css
body { padding: 24px 48px; }
然而,在 JavaScript 中,我们不能同时应用这些属性。在 JavaScript 中,我们需要分别应用每个属性。
js
document.body.style.paddingTop = "24px";
document.body.style.paddingBottom = "24px";
document.body.style.paddingLeft = "48px";
document.body.style.paddingRight = "48px";
添加和删除类
在 HTML 中,我们可以根据需要将任意数量的类应用于元素。
html
<button class="btn-emphasized btn-large btn-icon">Button label</button>
在 CSS 中,这些被声明为单独的类。
css
.btn-emphasized { };.btn-large { };.btn-icon { };
在 JavaScript 中,我们可以使用后缀访问元素的类.classList
。
我们可以使用以下命令添加一个类。
js
const element = document.getElementById("#element");element.classList.add("myClass");
我们可以使用以下命令删除一个类:
js
const element = document.getElementById("#element");
element.classList.remove("myClass");
我们还可以使用来切换类 .toggle
。
js
const element = document.getElementById("#element");
element.classList.toggle("myClass");
在下面的演示中,我们在卡片上切换两个类:.small
和.no-image
。当两者同时应用时,我们也会执行样式更改!
css
.small { width: 240px;}
.no-image > #illustration { display: none;}
.pfp-card.small.no-image { border-radius: 24px;}
添加和删除事件
在本系列的所有演示中,我们都将事件附加到元素上,使其具有交互性。现在我们将正式讨论它们。
要添加事件,我们必须使用 将其附加到元素addEventListener(type, listener)
。
js
const toggleButton = document.getElementById("myButton");
toggleButton.addEventListener('click', myFunction);
function myFunction() = { ... };
在上面的例子中,我们addEventListener()
正在检测的交互类型'click'
,并在触发时执行该myFunction
功能。
事件类型
并非所有事件都生而平等。不仅特定元素可以触发特定事件,不同的交互源也可以触发特定事件。

获取数据
现在让我们将数据添加到布局中!到目前为止,我们一直在处理静态数据,即手动输入显示给用户的每条内容。但是,如果我们想从 API 获取数据呢?
为此,我们使用一个fetch
请求,或者一个GET
请求。假设我们的 API 提供了两个密钥对:一个标题和一个图片 URL。
为了获取这些数据,我们需要编写GET
如下请求:
js
fetch("https://example.com/api/random-image")
.then(response => response.json())
.then(data => {const title = data.title; const imageUrl = data.url; })
.catch(error => console.error('Error:', error));
使用async
和await
我们可以优化代码,使其更具可读性。使用async function
和await
,我们可以从代码中删除.then()
命令,使其更易于理解和解析。
js
async function fetchRandomImage() {
try {
const response = await fetch("https://example.com/api/random-image");
const data = await response.json();
const title = data.title;
const imageUrl = data.url;
} catch (error) { console.error('Error:', error); }}
注意,我们已经完全替换了.then(data => {})
块。有了async
,那些就不再需要了。
了解有关 await 和 async 的更多信息
如果您想了解有关数据获取的更多信息,我们有一篇文章适合您!
填充布局
现在让我们修改代码,以便它填充整个项目网格!
在下面的演示中,我们将对函数进行多次迭代
fetch
,并创建一个包含所有项目的数组。数组完成后,我们为每个项目构建一张新卡片
appendChild
,然后将该卡片添加到网格中!
处理错误
处理 API 调用时,可能会出现一些问题。目前为止,我们看到的所有示例中都存在一个catch()
命令,但它实际上很少处理 API 调用中可能出现的错误。
js
catch (error) { console.error('Error:', error); }
那么我们该如何解决这个问题呢?
处理网络错误
该response.status
命令专门用于检查 HTTP 请求是否成功。这意味着它不会验证响应本身的任何数据。它让我们具体了解在特定情况下会给出哪些错误代码,如下所示:
js
if (response.status === 404) throw new Error('Not found');
if (response.status === 500) throw new Error('Internal server error');
这使得开发人员可以根据错误情况向用户显示不同的消息和操作。例如,当出现Not found
错误时,可以为用户提供返回上一页的快捷方式;同样,当服务器发生内部错误(这种情况可能不会再次发生)时,也可以提供刷新按钮。
这是可以返回的 HTTP 代码范围。
代码 | 类别 |
---|---|
100 -199 |
信息响应 |
200 -299 |
成功的响应 |
300 -399 |
重定向消息 |
400 -499 |
客户端错误响应 |
500 -599 |
服务器错误响应 |
查看所有回复 | 查看 MDN 文档以了解完整详情。 |
处理数据错误
现在假设,尽管期待某种类型的数据,但却没有返回。
我们简要查看的代码并未涵盖这些情况,为了捕获这些错误,我们必须自己验证它们。
让我们以前面的例子为基础来验证我们的数据。
js
async function fetchRandomImage() {
try {
const response = await fetch("https://example.com/api/random-image");
if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); }
const data = await response.json();
const title = data.title;
const imageUrl = data.url;
} catch (error) { console.error('Error:', error); }}
默认情况下,某些数据错误会被捕获catch()
。例如,如果我们data
返回一个格式错误的 JSON,则在尝试使用 解析它时会自动捕获该错误response.json()
并抛出错误。
然而,在上面的例子中,我们无法确保返回的数据是有效的。我们也需要能够处理这些数据错误!
自定义错误
我们的代码需要一个title
和一个URL
,但我们无法确保它们存在,也无法确保响应是否有效!如果其中任何一个失败,catch()
它本身不会执行任何操作,这就是为什么我们必须自己验证它们。
让我们来看看之前的代码,完全没有改变:
js
async function fetchRandomImage() {
try {
const response = await fetch("https://example.com/api/random-image");
const data = await response.json(); // Parse the response as JSON
if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); }
switch (true) {
case !data.title && !data.url:
throw new Error('Both title and URL are missing.');
case !data.title:
throw new Error('Title is missing.');
case !data.url:
throw new Error('URL is missing.');
default:
const title = data.title;
const imageUrl = data.url;
}
} catch (error) { console.error('Error:', error); }}
在上面的例子中,我们处理了请求中提供给我们的数据可能存在问题的所有可能情况fetch()
。
但是,您可能会注意到所有这些都会抛出一个新的Error
。如果您想在这些失败时提供默认响应,以免破坏应用程序,该怎么办?我们可以简单地修改 switch 语句,将这些响应作为值提供。
js
const title = '';const imageUrl = '';switch (true) { case !data.title && !data.url: title = 'Title is missing.'; url = "URL is missing."; case !data.title && data.url: title = 'Title is missing.'; url = data.url; case data.title && !data.url: title = data.title; url = "URL is missing."; }
但对于这个用例来说,这太冗长了。我们可以为 和 的值定义默认值title
,imageUrl
以进一步优化代码。
js
const title = data.title || 'Title is missing.';const url = data.url || 'URL is missing.';
如果您需要处理默认值并且不想Error
在任何数据丢失或无效时抛出异常,这是迄今为止最优化的选项!
处理装载
处理大型数据集或图形时,可能需要一些时间才能将内容显示给用户。我们在网格示例中就遇到过这种情况;我们的组件需要一段时间才能加载,但我们不会向用户显示任何正在获取数据的警告。
让我们改变它!
我们将重构代码,使其不再逐个填充网格。相反,我们将:
- 等待所有图像加载完毕。
- 将它们添加到数组中。
- 使用数组的内容填充列表
这样,它们就会同时更新。
见面onload
我们可以通过监听onload
事件来检测某些内容是否已加载。根据**MDN 文档**,我们甚至可以将其设置为属性来选择 HTML 元素。
html
<body onload="functionName()"></body><img src="image.png" onload="functionName()" /><iframe src="index.html" onload="functionName()"></iframe><link rel="stylesheet" href="stylesheet.css" onload="functionName()" /><script src="main.js" onload="functionName()"></script>
该onload="functionName()"
属性调用匹配的 JavaScript 函数并在对象完全加载后自动运行它。
了解了这一点,我们可以使用以下代码片段重构我们的代码:
js
function fetchDogs() { // Declare the array for all cards const dogCardArray = []; const requests = []; for (let i = 0; i < maxItems; i++) { const request = fetch("https://dog.ceo/api/breeds/image/random") .then(response => response.json()); requests.push(request); } Promise.all(requests) .then(results => { results.forEach(data => { /* Unchanged */ // Check when the dogImage for each card is loaded dogImage.onload = () => { // Build the dogCard components dogCard.appendChild(dogImage); dogCard.appendChild(dogName); dogCardArray.push(dogCard); /* Check if the dogArray has reached the maximum amount of items prior to displaying them */ if (dogCardArray.length === maxItems) { /* Clear the grid as the items are ready to be rerendered. */ dogGrid.innerHTML = ''; /* Populate the grid */ dogCardArray.forEach(card => { dogGrid.appendChild(card); }) } } }); }) .catch(error => console.error('Error:', error));}
在这个演示中,有一些添加的调整。
现在,加载时,所有卡片都略微透明,以显示其内容正在被替换并且处于非活动状态。
然而,这还不够好。 我们希望为用户提供更直观、更易用的指标。
为此,让我们实际显示一个加载微调器,并更改标签以指示正在发生的事情!首先,我们必须稍微重构一下代码。
- 将加载微调器添加到按钮。
- 将按钮标签包裹在
<p>
标签中,以便我们的innerText
更改不会在此过程中删除加载指示器。 - 根据我们的需要切换加载指示器的位置和可见性。
js
const refreshLabel = document.getElementById("refresh-btn-label");const loadingSpinner = document.getElementById("loading-spinner");/* Initial state */loadingSpinner.style.visibility = 'hidden';loadingSpinner.style.position = 'absolute';
js
function fetchDogs() { loadingSpinner.style.visibility = 'visible'; loadingSpinner.style.position = 'unset'; refreshLabel.innerText = "Fetching new dogs..." dogGrid.style.opacity = 0.5; // Fade the cards ...
js
if (dogCardArray.length === maxItems) { dogGrid.innerHTML = ''; dogCardArray.forEach(card => { refreshLabel.innerText = "Fetch new dogs"; dogGrid.appendChild(card); }) /* Hide spinner once everything is populated */ loadingSpinner.style.visibility = 'hidden'; loadingSpinner.style.position = 'absolute'; dogGrid.style.opacity = 1;}
结论
至此,我们已经了解了 Web 基础知识。
我要感谢每一位阅读过本系列文章、提供过反馈以及认为它有帮助的人!
如果您想查看本系列的更多文章,探讨我们遗漏的主题,请在 Discord 中告诉我们! 我们一直在关注博客文章的请求,并希望帮助越来越多的人开启他们的 Web 开发之旅。
下次再见。保重,祝开发顺利!