HTML 面试问题

20+ HTML 问答,采用测验形式,由前 FAANG 面试官解答
由前面试官解答
涵盖关键主题

HTML interview questions are designed to assess your understanding of web development fundamentals and best practices. Interviewers typically focus on key topics such as:

  • Accessibility: Ensuring websites are accessible to users with disabilities using semantic HTML and ARIA roles.
  • Semantics: Recognizing the importance of semantic HTML tags for SEO, accessibility, and code clarity.
  • Forms: Building forms with proper input validation, accessibility features, and efficient handling of form submissions.
  • Multimedia: Embedding and managing images, audio, and video in HTML while optimizing for performance and accessibility.
  • Best Practices: Structuring HTML for readability, maintainability, and performance, including the proper use of meta tags, link attributes, and media queries.
  • SEO Optimization: Using semantic HTML elements and metadata to boost search engine ranking and improve web performance.

Below, you’ll find 20+ carefully curated HTML interview questions covering everything from core concepts to best practices and optimization strategies.

Each question includes:

  • Quick Answers (TL;DR): Concise, clear responses to help you answer confidently.
  • Detailed Explanations: In-depth insights to ensure you not only know the answers but understand the reasoning behind them.

Unlike most lists, our questions are carefully curated by real senior and staff engineers from top tech companies like Amazon, Meta, and more—not anonymous contributors or AI-generated content. Start practicing below and get ready to ace your HTML interview!

如果您正在寻找 HTML 编码问题 -我们也会为您提供:
Javascript 编码
  • 70+ HTML 编码面试问题
  • 一个模拟真实面试条件的浏览器内编码工作区
  • 来自 Big Tech 公司前面试官的参考解决方案
  • 一键式自动化、透明的测试用例
  • UI 相关问题的即时 UI 预览
开始使用
加入 50,000+ 工程师

JavaScript 和浏览器中 `mouseenter` 和 `mouseover` 事件有什么区别?

主题
Web APIHTMLJavaScript

TL;DR

主要区别在于 mouseentermouseover 事件的冒泡行为。mouseenter 不冒泡,而 mouseover 冒泡。

mouseenter 事件不冒泡。mouseenter 事件仅在鼠标指针进入元素本身时触发,而不是其后代元素。如果父元素有子元素,并且鼠标指针进入子元素,则不会再次在父元素上触发 mouseenter 事件,它仅在进入父元素时触发一次,而不考虑其内容。如果父元素和子元素都附加了 mouseenter 侦听器,并且鼠标指针从父元素移动到子元素,则 mouseenter 将仅为子元素触发。

mouseover 事件会在 DOM 树中冒泡。当鼠标指针进入元素或其后代时,会触发 mouseover 事件。如果父元素有子元素,并且鼠标指针进入子元素,则父元素也会再次触发 mouseover 事件。如果父元素有多个子元素,这可能导致多次触发事件回调。如果有子元素,并且鼠标指针从父元素移动到子元素,则 mouseover 将同时为父元素和子元素触发。

属性mouseentermouseover
冒泡
触发仅在进入自身时进入自身和进入后代时

mouseenter 事件:

  • 不冒泡mouseenter 事件不冒泡。它仅在鼠标指针进入附加了事件侦听器的元素时触发,而不是进入任何子元素时触发。
  • 触发一次:当鼠标指针进入元素时,mouseenter 事件仅触发一次,这使得它在某些情况下更可预测且更易于管理。

mouseenter 的一个用例是,当您希望检测鼠标进入元素而无需担心子元素多次触发事件时。

mouseover 事件:

  • 在 DOM 中冒泡mouseover 事件通过 DOM 冒泡。这意味着,如果您在父元素上有一个事件侦听器,当鼠标指针移动到任何子元素上时,它也会触发。
  • 多次触发:每次鼠标指针移动到元素或其任何子元素上时,都会触发 mouseover 事件。如果您有嵌套元素,这可能导致多次触发。

mouseover 的一个用例是,当您希望检测鼠标进入元素或其任何子元素,并且可以接受事件多次触发时。

示例

这是一个演示 mouseovermouseenter 事件之间区别的示例:

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Mouse Events Example</title>
<style>
.parent {
width: 200px;
height: 200px;
background-color: lightblue;
padding: 20px;
}
.child {
width: 100px;
height: 100px;
background-color: lightcoral;
}
</style>
</head>
<body>
<div class="parent">
Parent Element
<div class="child">Child Element</div>
</div>
<script>
const parent = document.querySelector('.parent');
const child = document.querySelector('.child');
// Mouseover event on parent.
parent.addEventListener('mouseover', () => {
console.log('Mouseover on parent');
});
// Mouseenter event on parent.
parent.addEventListener('mouseenter', () => {
console.log('Mouseenter on parent');
});
// Mouseover event on child.
child.addEventListener('mouseover', () => {
console.log('Mouseover on child');
});
// Mouseenter event on child.
child.addEventListener('mouseenter', () => {
console.log('Mouseenter on child');
});
</script>
</body>
</html>

预期行为

  • 当鼠标进入父元素时:
    • 父元素上的 mouseover 事件将触发。
    • 父元素上的 mouseenter 事件将触发。
  • 当鼠标进入子元素时:
    • 父元素上的 mouseover 事件将再次触发,因为 mouseover 从子元素冒泡。
    • 子元素上的 mouseover 事件将触发。
    • 子元素上的 mouseenter 事件将触发。
    • 父元素上的 mouseenter 事件将不会再次触发,因为 mouseenter 不冒泡。

延伸阅读

解释 `document.querySelector()` 和 `document.getElementById()` 之间的区别

主题
Web APIJavaScriptHTML

TL;DR

document.querySelector()document.getElementById() 都是用于从 DOM 中选择元素的方法,但它们有关键的区别。document.querySelector() 可以使用 CSS 选择器选择任何元素并返回第一个匹配项,而 document.getElementById() 通过其 ID 选择一个元素并返回具有该特定 ID 的元素。

// 使用 document.querySelector()
const element = document.querySelector('.my-class');
// 使用 document.getElementById()
const elementById = document.getElementById('my-id');

document.querySelector()document.getElementById() 之间的区别

document.querySelector()

  • 可以使用任何有效的 CSS 选择器选择元素,包括类、ID、标签、属性和伪类
  • 返回与指定选择器匹配的第一个元素
  • 更通用,但由于 CSS 选择器的灵活性,速度稍慢
// 选择具有类 'my-class' 的第一个元素
const element = document.querySelector('.my-class');
// 选择第一个 <div> 元素
const divElement = document.querySelector('div');
// 选择具有属性 data-role='button' 的第一个元素
const buttonElement = document.querySelector('[data-role="button"]');

document.getElementById()

  • 通过其 ID 属性选择一个元素
  • 返回具有指定 ID 的元素
  • 通过 ID 选择元素更快更有效,但通用性较差
// 选择具有 ID 'my-id' 的元素
const elementById = document.getElementById('my-id');

关键区别

  • 选择器类型document.querySelector() 使用 CSS 选择器,而 document.getElementById() 仅使用 ID 属性。
  • 返回值document.querySelector() 返回第一个匹配的元素,而 document.getElementById() 返回具有指定 ID 的元素。
  • 性能document.getElementById() 通常更快,因为它直接通过 ID 访问元素,而 document.querySelector() 必须解析 CSS 选择器。

延伸阅读

页面上的 `<iframe>` 如何通信?

主题
Web APIJavaScriptHTML

总结

页面上的 <iframe> 元素可以使用 postMessage API 进行通信。这允许父页面和 iframe 之间进行安全的跨源通信。postMessage 方法发送消息,而 message 事件侦听器接收消息。这是一个简单的例子:

// 在父页面中
const iframe = document.querySelector('iframe');
iframe.contentWindow.postMessage('Hello from parent', '*');
// 在 iframe 中
window.addEventListener('message', (event) => {
console.log(event.data); // 'Hello from parent'
});

页面上的 <iframe> 如何通信?

使用 postMessage API

postMessage API 是 iframe 相互之间或与其父页面通信的最常见和最安全的方式。此方法允许跨源通信,这对于现代 Web 应用程序至关重要。

发送消息

要将消息从父页面发送到 iframe,您可以使用 postMessage 方法。这是一个例子:

// 在父页面中
const iframe = document.querySelector('iframe');
iframe.contentWindow.postMessage('Hello from parent', '*');

在此示例中,父页面选择 iframe 并向其发送消息。第二个参数 '*' 是目标 origin。它指定目标窗口的 origin。使用 '*' 意味着任何 origin 都可以接收消息,但出于安全原因,最好指定确切的 origin。

接收消息

要在 iframe 中接收消息,您需要为 message 事件添加一个事件侦听器:

// 在 iframe 中
window.addEventListener('message', (event) => {
console.log(event.data); // 'Hello from parent'
});

event 对象包含 data 属性,该属性保存父页面发送的消息。

安全注意事项

使用 postMessage 时,考虑安全性至关重要:

  • 指定目标 origin:不要使用 '*',而是指定确切的 origin,以确保仅接收来自受信任来源的消息。
  • 验证消息:始终验证消息内容,以防止处理恶意数据。

带有目标 origin 的示例

这是一个带有指定目标来源的示例:

// In the parent page
const iframe = document.querySelector('iframe');
const targetOrigin = 'https://example.com';
iframe.contentWindow.postMessage('Hello from parent', targetOrigin);
// In the iframe
window.addEventListener('message', (event) => {
if (event.origin === 'https://parent.com') {
console.log(event.data); // 'Hello from parent'
}
});

在此示例中,父页面仅向 https://example.com 发送消息,并且 iframe 仅在消息来自 https://parent.com 时才处理该消息。

延伸阅读

如何使用 JavaScript 添加、删除和修改 HTML 元素?

主题
Web APIJavaScriptHTML

TL;DR

要使用 JavaScript 添加、删除和修改 HTML 元素,可以使用 createElementappendChildremoveChild 等方法以及 innerHTMLtextContent 等属性。例如,要添加一个元素,可以使用 document.createElement 创建它,然后使用 appendChild 将其附加到父元素。要删除一个元素,可以在其父元素上使用 removeChild。要修改一个元素,可以更改其 innerHTMLtextContent

// Adding an element
const newElement = document.createElement('div');
newElement.textContent = 'Hello, World!';
document.body.appendChild(newElement);
// Removing an element
const elementToRemove = document.getElementById('elementId');
elementToRemove.parentNode.removeChild(elementToRemove);
// Modifying an element
const elementToModify = document.getElementById('elementId');
elementToModify.innerHTML = 'New Content';

使用 JavaScript 添加、删除和修改 HTML 元素

添加元素

要添加 HTML 元素,可以使用 document.createElement 方法创建一个新元素,然后使用 appendChild 将其附加到父元素。

// Create a new div element
const newDiv = document.createElement('div');
// Set its content
newDiv.textContent = 'Hello, World!';
// Append the new element to the body
document.body.appendChild(newDiv);
// See the changed document by running the code
console.log(document.body);

您还可以使用 insertBefore 在现有子元素之前插入新元素。

const parentElement = document.getElementById('parent');
const newElement = document.createElement('p');
newElement.textContent = 'Inserted Paragraph';
const referenceElement = document.getElementById('reference');
parentElement.insertBefore(newElement, referenceElement);

删除元素

要删除 HTML 元素,可以在其父元素上使用 removeChild 方法。

// Select the element to be removed
const elementToRemove = document.getElementById('elementId');
// Remove the element
elementToRemove.parentNode.removeChild(elementToRemove);

或者,您可以在该元素上直接使用 remove 方法。

const elementToRemove = document.getElementById('elementId');
elementToRemove.remove();

修改元素

要修改 HTML 元素,您可以更改其属性,例如 innerHTMLtextContent 或属性。

const elementToModify = document.createElement('div');
// Change its inner HTML
elementToModify.innerHTML = 'New Content';
// Change its text content
elementToModify.textContent = 'New Text Content';
// Change an attribute
elementToModify.setAttribute('class', 'new-class');
console.log(elementToModify);

您还可以使用 classList.addclassList.removeclassList.toggle 等方法来修改元素的类。

const element = document.getElementById('elementId');
// Add a class
element.classList.add('new-class');
// Remove a class
element.classList.remove('old-class');
// Toggle a class
element.classList.toggle('active');

延伸阅读

event.preventDefault() 和 event.stopPropagation() 有什么区别?

主题
Web APIHTMLJavaScript

TL;DR

event.preventDefault() 用于阻止属于该事件的默认操作,例如阻止表单提交。event.stopPropagation() 用于阻止事件冒泡到父元素,从而阻止任何父事件处理程序被执行。


event.preventDefault() 和 event.stopPropagation() 有什么区别?

event.preventDefault()

event.preventDefault() 是一种方法,如果事件是可取消的,则取消该事件,这意味着属于该事件的默认操作将不会发生。例如,这可以用于阻止表单提交:

document.querySelector('form').addEventListener('submit', function (event) {
event.preventDefault();
// Form submission is prevented
});

event.stopPropagation()

event.stopPropagation() 是一种方法,可防止事件在 DOM 树中冒泡,从而阻止任何父处理程序收到该事件的通知。当您希望在特定级别处理事件并且不希望它触发父元素上的处理程序时,这很有用:

document.querySelector('.child').addEventListener('click', function (event) {
event.stopPropagation();
// Click event will not propagate to parent elements
});

关键区别

  • event.preventDefault() 停止与事件关联的默认操作。
  • event.stopPropagation() 阻止事件传播(冒泡)到父元素。

用例

  • 当您想阻止元素的默认行为时,请使用 event.preventDefault(),例如阻止链接导航或表单提交。
  • 当您想阻止事件到达父元素时,请使用 event.stopPropagation(),这在多个元素具有事件侦听器的复杂 UI 中很有用。

延伸阅读

innerHTML 和 textContent 有什么区别?

主题
Web APIHTMLJavaScript

TL;DR

innerHTMLtextContent 都是用于获取或设置 HTML 元素内容的属性,但它们的作用不同。 innerHTML 返回或设置元素中包含的 HTML 标记,这意味着它可以解析和呈现 HTML 标签。 另一方面,textContent 返回或设置元素的文本内容,忽略任何 HTML 标签并将它们呈现为纯文本。

// Example of innerHTML
element.innerHTML = '<strong>Bold Text</strong>'; // Renders as bold text
// Example of textContent
element.textContent = '<strong>Bold Text</strong>'; // Renders as plain text: <strong>Bold Text</strong>

innerHTMLtextContent 的区别

innerHTML

innerHTML 是一种属性,允许您获取或设置元素中包含的 HTML 标记。 它可以解析和呈现 HTML 标签,使其可用于动态更新网页的结构。

示例
const element = document.getElementById('example');
element.innerHTML = '<strong>Bold Text</strong>'; // This will render as bold text
用例
  • 动态添加或更新 HTML 内容
  • 渲染 HTML 标签和元素
安全注意事项

如果插入不受信任的内容,使用 innerHTML 可能会使您的应用程序暴露于跨站点脚本 (XSS) 攻击。 在将任何用户输入设置为 innerHTML 之前,请务必对其进行清理。

textContent

textContent 是一种属性,允许您获取或设置元素的文本内容。 它会忽略任何 HTML 标签并将它们呈现为纯文本,这使得它更安全地插入用户生成的内容。

示例
const element = document.getElementById('example');
element.textContent = '<strong>Bold Text</strong>'; // This will render as plain text: <strong>Bold Text</strong>
用例
  • 安全地插入用户生成的内容
  • 从字符串中删除 HTML 标签
性能注意事项

textContent 通常比 innerHTML 快,因为它不会解析和呈现 HTML 标签。 它只是更新元素的文本内容。

延伸阅读

什么是 DOM 以及它的结构?

主题
JavaScriptHTML

TL;DR

DOM(文档对象模型)是用于 Web 文档的编程接口。它表示页面,以便程序可以更改文档结构、样式和内容。DOM 结构为对象树,其中每个节点代表文档的一部分,例如元素、属性和文本。


什么是 DOM 以及它的结构?

定义

文档对象模型 (DOM) 是一个跨平台且与语言无关的接口,它将 HTML、XHTML 或 XML 文档视为树结构。此树中的每个节点代表文档的一部分。

结构

DOM 结构为节点的分层树。以下是主要类型的节点:

  1. 文档节点:文档树的根。它代表整个文档。
  2. 元素节点:这些代表 HTML 元素,并构成文档树的大部分。
  3. 属性节点:这些与元素节点相关联,代表这些元素的属性。
  4. 文本节点:这些表示元素中的文本内容。
  5. 注释节点:这些表示 HTML 中的注释。

示例

考虑以下 HTML:

<!doctype html>
<html>
<head>
<title>Document</title>
</head>
<body>
<h1>Hello, World!</h1>
<p>This is a paragraph.</p>
</body>
</html>

此文档的 DOM 树将如下所示:

Document
└── html
├── head
│ └── title
│ └── "Document"
└── body
├── h1
│ └── "Hello, World!"
└── p
└── "This is a paragraph."

访问和操作 DOM

JavaScript 可用于访问和操作 DOM。以下是一些常用方法:

  • document.getElementById(id):通过其 ID 选择一个元素。
  • document.querySelector(selector):选择与 CSS 选择器匹配的第一个元素。
  • element.appendChild(node):将新的子节点添加到元素。
  • element.removeChild(node):从元素中删除子节点。

示例:

// Create an <h1> element and add it to the DOM
const newElement = document.createElement('h1');
document.body.appendChild(newElement);
// Get the h1 element using querySelector
const heading = document.querySelector('h1');
heading.textContent = 'Hello, DOM!';
console.log(heading); // <h1>Hello, DOM!</h1>

延伸阅读

DOM 中“attribute”和“property”有什么区别?

主题
Web APIJavaScriptHTML

TL;DR

Attributes 在 HTML 中定义,为属性提供初始值。属性是 DOM 的一部分,表示元素的当前状态。例如,<input> 元素的 value 属性设置其初始值,而 value 属性反映用户与之交互时的当前值。


DOM 中“attribute”和“property”有什么区别

Attributes

Attributes 在 HTML 标记中定义,为元素提供初始值。它们是静态的,一旦页面加载后就不会改变,除非使用 JavaScript 显式修改。

示例
<input type="text" value="initial value" />

在此示例中,value="initial value" 是一个 attribute。

Properties

Properties 是 DOM 的一部分,表示元素的当前状态。它们是动态的,可以随着用户与页面交互或通过 JavaScript 而改变。

示例
const inputElement = document.querySelector('input');
console.log(inputElement.value); // 记录输入元素的当前值
inputElement.value = 'new value'; // 更改输入元素的当前值

在此示例中,valueinputElement 对象的属性。

关键区别

  • 初始化:Attributes 初始化 DOM 属性。
  • 状态:Attributes 是静态的,而属性是动态的。
  • 访问:可以使用 getAttributesetAttribute 方法访问 Attributes,而可以直接在 DOM 对象上访问属性。
示例
<input id="myInput" type="text" value="initial value" />
const inputElement = document.getElementById('myInput');
// 访问 attribute
console.log(inputElement.getAttribute('value')); // "initial value"
// 访问 property
console.log(inputElement.value); // "initial value"
// 更改 property
inputElement.value = 'new value';
console.log(inputElement.value); // "new value"
console.log(inputElement.getAttribute('value')); // "initial value"

在此示例中,更改 value 属性不会影响 value attribute。

延伸阅读

描述 `<script>`、`<script async>` 和 `<script defer>` 之间的区别

主题
HTMLJavaScript

TL;DR

所有这些方式(<script><script async><script defer>)都用于在 HTML 文档中加载和执行 JavaScript 文件,但它们在浏览器处理脚本的加载和执行方式上有所不同:

  • <script> 是包含 JavaScript 的默认方式。浏览器在下载和执行脚本时会阻止 HTML 解析。在脚本执行完毕之前,浏览器不会继续渲染页面。
  • <script async> 异步下载脚本,与解析 HTML 并行。在脚本可用后立即执行脚本,可能会中断 HTML 解析。<script async> 之间不会互相等待,并且以不特定的顺序执行。
  • <script defer> 异步下载脚本,与解析 HTML 并行。但是,脚本的执行被推迟到 HTML 解析完成后,按照它们在 HTML 中出现的顺序。

这是一个表格,总结了在 HTML 文档中加载 <script> 的 3 种方式。

特性<script><script async><script defer>
解析行为阻止 HTML 解析与解析并行运行与解析并行运行
执行顺序按照出现顺序不保证按照出现顺序
DOM 依赖是(等待 DOM)

<script> 标签的用途

<script> 标签用于在网页中包含 JavaScript。asyncdefer 属性用于更改脚本的加载和执行方式/时间。

<script>

对于没有任何 asyncdefer 的普通 <script> 标签,当遇到它们时,HTML 解析会被阻止,脚本会被立即获取和执行。HTML 解析在脚本执行完毕后恢复。如果脚本很大,这可能会阻止页面的渲染。

<script> 用于页面依赖于正确渲染的关键脚本。

<!doctype html>
<html>
<head>
<title>Regular Script</title>
</head>
<body>
<!-- Content before the script -->
<h1>Regular Script Example</h1>
<p>This content will be rendered before the script executes.</p>
<!-- Regular script -->
<script src="regular.js"></script>
<!-- Content after the script -->
<p>This content will be rendered after the script executes.</p>
</body>
</html>

<script async>

<script async> 中,浏览器异步下载脚本文件(与 HTML 解析并行),并在脚本可用后立即执行(可能在 HTML 解析完成之前)。执行不一定按照它在 HTML 文档中出现的顺序执行。这可以提高感知的性能,因为浏览器在继续渲染页面之前不必等待脚本下载。

当脚本独立于页面上的任何其他脚本时,使用 <script async>,例如分析和广告脚本。

<!doctype html>
<html>
<head>
<title>Async Script</title>
</head>
<body>
<!-- Content before the script -->
<h1>Async Script Example</h1>
<p>This content will be rendered before the async script executes.</p>
<!-- Async script -->
<script async src="async.js"></script>
<!-- Content after the script -->
<p>
This content may be rendered before or after the async script executes.
</p>
</body>
</html>

<script defer>

<script async> 类似,<script defer> 也会与 HTML 解析并行下载脚本,但脚本仅在文档被完全解析并且在触发 DOMContentLoaded 之前执行。如果有多个,则每个延迟脚本按照它们在 HTML 文档中出现的顺序执行。

如果脚本依赖于完全解析的 DOM,则 defer 属性将有助于确保在执行之前完全解析 HTML。

<!doctype html>
<html>
<head>
<title>Deferred Script</title>
</head>
<body>
<!-- Content before the script -->
<h1>Deferred Script Example</h1>
<p>This content will be rendered before the deferred script executes.</p>
<!-- Deferred script -->
<script defer src="deferred.js"></script>
<!-- Content after the script -->
<p>This content will be rendered before the deferred script executes.</p>
</body>
</html>

注意事项

  • async 属性应用于对页面初始渲染不关键且彼此不依赖的脚本,而 defer 属性应用于依赖于/被另一个脚本依赖的脚本。
  • 对于没有 src 属性的脚本,将忽略 asyncdefer 属性。
  • 包含 document.write() 的带有 deferasync<script> 将被忽略,并显示类似“从异步加载的外部脚本对 document.write() 的调用被忽略”的消息。
  • 即使 asyncdefer 有助于使脚本下载异步,但脚本最终仍在主线程上执行。如果这些脚本是计算密集型的,则可能导致 UI 滞后/冻结。Partytown 是一个库,它有助于将脚本执行重新定位到 web worker 并从 主线程 中移出,这对于您无法控制代码的第三方脚本非常有用。

延伸阅读

为什么将 CSS `<link>` 放置在 `<head></head>` 之间,而 JS `<script>` 放置在 `</body>` 之前是一个好主意?

你知道任何例外情况吗?
主题
HTML性能

简而言之,这种 CSS <link> 和 JavaScript <script> 的放置方式可以更快地渲染页面并获得更好的整体性能。

<link> 放置在 <head>

<link> 放在 <head> 中是构建优化网站的正确规范的一部分。当页面首次加载时,HTML 和 CSS 会同时被解析;HTML 创建 DOM(文档对象模型),CSS 创建 CSSOM(CSS 对象模型)。两者都需要在网站中创建视觉效果,从而实现快速的“首次有效绘制”计时。将 CSS <link> 放置在 <head> 中可确保样式表在浏览器开始渲染页面时已加载并可以使用。

这种渐进式渲染是衡量网站性能得分的指标。将样式表放在文档的底部会阻止许多浏览器中的渐进式渲染。某些浏览器会阻止渲染,以避免在样式更改时重新绘制页面元素。然后,用户会卡在查看空白的白页上。其他时候可能会出现未设置样式的闪烁内容 (FOUC),这会显示未应用任何样式的网页。

<script> 放置在 </body> 之前

<script> 标签会阻止 HTML 解析,因为它们正在被下载和执行,这会减慢页面的显示速度。将 <script> 放在底部将允许首先解析 HTML 并将其显示给用户。

<script> 放置在底部的例外情况是,当您的脚本包含 document.write() 时,但如今使用 document.write() 并不是一个好习惯。此外,将 <script> 放置在底部意味着浏览器在整个文档被解析之前无法开始下载脚本。这确保了需要操作 DOM 元素的代码不会抛出错误并停止整个脚本。如果需要在 <head> 中放置 <script>,请使用 defer 属性,这将实现与仅在 HTML 被解析后运行脚本相同的效果,但浏览器可以更早地启动网络请求以下载脚本。

请记住,将脚本放在结束 </body> 标签之前会产生页面在空缓存上加载更快的错觉(因为脚本不会阻止下载文档的其余部分)。但是,如果您有一些代码要在页面加载期间运行,它将仅在整个页面加载完成后才开始执行。如果将这些脚本放在 <head> 标签中,它们将开始更早地执行 - 因此在已准备好的缓存上,页面实际上看起来会加载得更快。

<head><body> 标签现在是可选的

根据 HTML5 规范,某些 HTML 标签(如 <head><body>)是可选的。谷歌的风格指南甚至建议删除它们以节省字节。但是,这种做法尚未得到广泛采用,性能提升可能很小,对于大多数网站来说,它可能无关紧要。

将 HTML5 视为一个开放的 Web 平台。HTML5 的构建块是什么?

主题
浏览器HTML
  • 语义: HTML 标签描述内容。
  • 样式: 自定义 HTML 标签的外观
  • 连接性: 以新的和创新的方式与服务器通信。
  • 离线和存储: 允许网页在客户端本地存储数据,并更有效地离线运行。
  • 多媒体: 使视频和音频成为开放 Web 中的一流公民。
  • 2D/3D 图形和效果: 允许更多样化的呈现选项。
  • 性能和集成: 提供更高的速度优化和更好的计算机硬件使用。
  • 设备访问: 允许使用各种输入和输出设备。

data- 属性有什么用?

主题
Web APIHTML测试

在 JavaScript 框架流行之前,开发人员使用 data- 属性在 DOM 本身中存储额外的数据,而无需其他技巧,例如非标准属性、DOM 上的额外属性。它旨在存储页面或应用程序专用的自定义数据,当没有更合适的属性或元素时。

data- 属性的另一个常见用例是存储第三方库或框架使用的信息。例如,Bootstrap 库使用数据属性使 <button> 在页面上的其他位置触发模态操作 (示例)。

<button type="button" data-bs-toggle="modal" data-bs-target="#myModal">
Launch modal
</button>
...
<div class="modal fade" id="myModal">Modal contents</div>

如今,通常不鼓励使用 data- 属性。原因之一是用户可以通过在浏览器中使用“检查元素”来轻松修改数据属性。数据模型最好存储在 JavaScript 环境中,并通过虚拟 DOM 协调或双向数据绑定(可能通过库或框架)使它们与 DOM 保持同步。

但是,数据属性的一个完全有效的用途是为 端到端 测试框架(例如 Playwright、Puppeteer、Selenium)添加一个标识符,而无需仅为主要用于其他目的的测试添加类或 ID 属性。该元素需要一种被选择的方式,而类似 data-test-id="my-element" 的方式是一种有效的方式,可以在不使语义标记复杂化的前提下实现。

什么是渐进式渲染?

主题
HTML

渐进式渲染是指用于提高网页性能(特别是提高感知加载时间)的技术,以便尽快渲染内容以供显示。

在宽带互联网普及之前的时代,它曾经非常流行,但它仍然被用于现代开发中,因为移动数据连接变得越来越受欢迎(而且不可靠)!

图片的懒加载

页面上的图片不会一次性全部加载。只有当用户滚动到显示图片的页面部分或附近时,才会加载图片。

  • <img loading="lazy"> 是一种现代方式,指示浏览器推迟加载屏幕外的图像,直到用户滚动到它们附近。
  • 使用 JavaScript 监视滚动位置,并在图像即将出现在屏幕上时加载图像(通过将图像的坐标与滚动位置进行比较)。

优先显示可见内容(或首屏渲染)

仅包含用户浏览器中将首先渲染的页面所需的最少 CSS/内容/脚本,以便尽快显示,然后您可以使用延迟脚本或监听 DOMContentLoaded/load 事件来加载其他资源和内容。

异步 HTML 片段

在后端构建页面时,将 HTML 的部分内容刷新到浏览器。有关该技术的更多详细信息,请参阅此处

其他现代技术

为什么要在图像标签中使用 `srcset` 属性?

解释浏览器在评估此属性内容时使用的过程。
主题
HTML

当您希望根据用户的设备显示宽度为用户提供不同的图像时,您可以使用 srcset 属性 - 为具有视网膜显示屏的设备提供更高质量的图像可以增强用户体验,而为低端设备提供较低分辨率的图像可以提高性能并减少数据浪费(因为提供更大的图像不会有任何可见的差异)。例如:<img srcset="small.jpg 500w, medium.jpg 1000w, large.jpg 2000w" src="..." alt=""> 告诉浏览器根据客户端的分辨率显示 small、medium 或 large .jpg 图像。第一个值是图像名称,第二个值是图像的宽度(以像素为单位)。对于 320px 的设备宽度,将进行以下计算:

  • 500 / 320 = 1.5625
  • 1000 / 320 = 3.125
  • 2000 / 320 = 6.25

如果客户端的分辨率为 1x,则 1.5625 最接近,浏览器将选择与 small.jpg 对应的 500w

如果分辨率是视网膜 (2x),浏览器将使用最接近最小值的分辨率。这意味着它不会选择 500w (1.5625),因为它大于 1,并且图像可能看起来很糟糕。然后,浏览器将选择结果比率更接近 2 的图像,即 1000w (3.125)。

srcset 解决了您希望为窄屏设备提供较小图像文件的问题,因为它们不需要像桌面显示器那样的大图像,并且还可以选择为高密度/低密度屏幕提供不同分辨率的图像。

document `load` 事件和 document `DOMContentLoaded` 事件的区别?

主题
HTMLJavaScript

TL;DR

DOMContentLoaded 事件在初始 HTML 文档完全加载和解析后触发,而无需等待样式表、图像和子框架完成加载。另一方面,load 事件在整个页面(包括所有依赖资源,如样式表和图像)完成加载后触发。

document.addEventListener('DOMContentLoaded', function () {
console.log('DOM fully loaded and parsed');
});
window.addEventListener('load', function () {
console.log('Page fully loaded');
});

document load 事件和 document DOMContentLoaded 事件的区别

DOMContentLoaded 事件

DOMContentLoaded 事件在初始 HTML 文档完全加载和解析后触发,而无需等待样式表、图像和子框架完成加载。当您希望在 DOM 准备就绪后立即执行 JavaScript 代码,而无需等待所有资源完全加载时,此事件非常有用。

document.addEventListener('DOMContentLoaded', function () {
console.log('DOM fully loaded and parsed');
});

load 事件

load 事件在整个页面(包括所有依赖资源,如样式表、图像和子框架)完成加载后触发。当您需要执行需要所有资源完全加载的操作时,此事件非常有用,例如初始化幻灯片或执行依赖于图像大小的布局计算。

window.addEventListener('load', function () {
console.log('Page fully loaded');
});

关键区别

  • 时序DOMContentLoadedload 提前触发。DOMContentLoaded 在 HTML 完全解析后发生,而 load 等待所有资源加载完毕。
  • 用例:将 DOMContentLoaded 用于仅需要 DOM 就绪的任务,例如附加事件侦听器或操作 DOM。将 load 用于依赖所有资源完全加载的任务,例如依赖于图像的布局计算。

延伸阅读

设计或开发多语言网站时,您必须注意哪些事项?

主题
HTML国际化

设计和开发多语言网站是国际化 (i18n) 的一部分。

搜索引擎优化

  • <html> 标签上使用 lang 属性。
  • 在 URL 中包含语言区域设置(例如 en_US、zh_CN 等)。
  • 网页应使用 <link rel="alternate" hreflang="other_locale" href="url_for_other_locale"> 告诉搜索引擎在指定的 href 处有另一个页面,其内容相同,但针对另一种语言/语言区域设置。
  • 为不匹配的语言使用后备页面。使用“x-default”值:<link rel="alternate" href="url_for_fallback" hreflang="x-default" />

了解语言区域设置与语言的区别

语言区域设置控制数字、日期和时间的显示方式,适用于您的地区:这可能是一个国家/地区,或一个国家/地区的一部分,甚至可能不遵守国家/地区的边界。

语言可能因国家/地区而异

某些语言,尤其是使用广泛的语言,在不同的国家/地区有不同的“风格”(语法规则、拼写、字符)。区分目标国家/地区的语言非常重要,不要假设/强制所有使用该语言的国家/地区都使用该语言的一个国家/地区的版本。示例:

  • enen-US(美式英语)、en-GB(英式英语)
  • zhzh-CN(简体中文)、zh-TW(繁体中文)

预测语言区域设置,但不要限制

服务器可以通过 HTTP Accept-Language 标头和 IP 地址的组合来确定访问者的语言区域设置/语言。有了这些,服务器可以自动为访问者选择最佳语言区域设置。但是,预测并非万无一失(尤其是当访问者使用 VPN 时),并且应允许访问者轻松更改其国家/地区/语言,而不会遇到麻烦。

考虑不同语言的文本长度差异

用另一种语言书写时,某些内容可能会更长。在设计中要注意布局或溢出问题。最好避免设计文本量会破坏设计的场景。字符计数会影响标题、标签和按钮等内容。对于自由流动的文本(如正文或评论),它们的影响较小。例如,某些语言(如德语和法语)往往比英语使用更长的单词和句子,如果不考虑这一点,可能会导致布局问题。

语言阅读方向

英语和法语等语言是从左到右、从上到下书写的。但是,希伯来语和阿拉伯语等某些语言是从右到左书写的。这会影响您网站的布局和页面上元素的位置,因此您必须小心设计您的网站,以适应不同的文本方向。

不要连接翻译后的字符串

不要做类似 "今天的日期是 " + date 的事情。它会在单词顺序不同的语言中中断。改为对每种语言使用带有参数替换的模板字符串。例如,分别查看英语和中文的以下两句话:I will travel on {% date %}我会在{% date %}出发。请注意,由于该语言的语法规则,变量的位置不同。

格式化日期和货币

日期格式有时因地区而异,例如美国习惯写作“月在前”的 "May 31, 2012",而欧洲一些国家则习惯“日在前”的 "31 May 2012"。

不要将文本放在图片中

将文本放在基于栅格的图像(例如 png、gif、jpg 等)中不是一种可扩展的方法。将文本放在图像中仍然是一种在任何计算机上显示美观的非系统字体的常用方法。但是,为了支持图像文本翻译成其他语言,需要为每种语言创建单独的图像,这对于设计师来说不是一个可扩展的工作流程。

注意颜色是如何被感知的

颜色在不同的语言和文化中被感知的方式不同。设计应适当使用颜色。

参考资料

如何使用多种语言提供页面内容?

主题
HTML国际化

假设:这个问题是关于如何提供页面内容,这些内容有多种语言版本,并且页面中的内容应该只以一种一致的语言显示。

以不同语言提供页面是国际化 (i18n) 的一个方面。

当向服务器发出 HTTP 请求时,请求用户代理通常会发送有关语言偏好的信息,例如在 Accept-Language 标头中。然后,如果存在这种备选方案,服务器可以使用此信息以适当的语言返回文档版本。返回的 HTML 文档还应在 <html> 标签中声明 lang 属性,例如 <html lang="en">...</html>

为了让搜索引擎知道相同的内容有不同的语言版本,应该使用带有 rel="alternate"hreflang="..." 属性的 <link> 标签。例如 <link rel="alternate" hreflang="de" href="http://de.example.com/page.html" />

渲染

  • 服务器端渲染: HTML 标记将包含字符串占位符,特定语言的内容将从代码配置或翻译服务中获取。然后,服务器动态生成具有该特定语言内容的 HTML 页面。
  • 客户端渲染: 适当的区域设置字符串将被提取并与基于 JavaScript 的视图相结合。

``DOCTYPE`` 是做什么的?

主题
HTML

DOCTYPEDocument Type(文档类型) 的缩写。DOCTYPE 总是与 DTD - Document Type Definition(文档类型定义) 相关联。

DTD 定义了特定类型的文档应该如何构建(例如,button 可以包含 span,但不能包含 div),而 DOCTYPE 声明了文档应该遵守的 DTD(例如,此文档遵守 HTML DTD)。

对于网页,DOCTYPE 声明是必需的。它用于告诉用户代理您的文档遵守 HTML 规范的哪个版本。一旦用户代理识别出正确的 DOCTYPE,它将触发 no-quirks mode(非怪异模式) 来读取文档。如果用户代理无法识别正确的 DOCTYPE,它将触发 quirks mode(怪异模式)

HTML5 标准的 DOCTYPE 声明是 <!DOCTYPE html>

解释什么是单页应用程序以及如何使其对 SEO 友好

主题
JavaScriptHTML

TL;DR

单页应用程序 (SPA) 是一种 Web 应用程序,它加载单个 HTML 页面,并在用户与应用程序交互时动态更新内容。这种方法提供了更流畅的用户体验,但对于 SEO 来说可能具有挑战性,因为搜索引擎可能不会执行 JavaScript 来呈现内容。要使 SPA 对 SEO 友好,您可以使用服务器端渲染 (SSR) 或静态站点生成 (SSG) 来确保搜索引擎可以索引您的内容。像 Next.js for React 或 Nuxt.js for Vue.js 这样的工具可以帮助实现这一点。


什么是单页应用程序?

定义

单页应用程序 (SPA) 是一种 Web 应用程序,它通过动态重写当前页面而不是从服务器加载整个新页面来与用户交互。这会产生更流畅的用户体验,类似于桌面应用程序。

关键特征

  • 应用程序加载单个 HTML 页面,并在用户与应用程序交互时动态更新它
  • 使用 AJAX 或 Fetch API 与服务器通信并更新页面,无需完全重新加载
  • 通常依赖客户端路由来管理应用程序内的不同视图或状态

优点

  • 初始加载后交互更快
  • 由于页面请求减少,服务器负载降低
  • 通过更流畅的过渡改善用户体验

如何使 SPA 对 SEO 友好

挑战

对于 SEO 来说,SPA 具有挑战性,因为搜索引擎可能不会执行 JavaScript 来呈现内容。这可能导致搜索引擎索引一个空的或不完整的页面。

解决方案

服务器端渲染 (SSR)

服务器端渲染涉及在将页面发送到客户端之前在服务器上呈现页面的初始 HTML。这确保了搜索引擎可以索引完全呈现的内容。

  • React:使用 Next.js,它提供对 SSR 的内置支持
  • Vue.js:使用 Nuxt.js,它也支持开箱即用的 SSR

Next.js 示例:

import React from 'react';
import { GetServerSideProps } from 'next';
const Page = ({ data }) => {
return (
<div>
<h1>{data.title}</h1>
<p>{data.content}</p>
</div>
);
};
export const getServerSideProps: GetServerSideProps = async () => {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return {
props: {
data,
},
};
};
export default Page;
静态站点生成 (SSG)

静态站点生成涉及在构建时为每个页面生成 HTML。这种方法适用于内容不经常更改的情况。

  • React:使用 Next.js 及其静态生成功能
  • Vue.js:使用 Nuxt.js 及其静态站点生成功能

Next.js 示例:

import React from 'react';
import { GetStaticProps } from 'next';
const Page = ({ data }) => {
return (
<div>
<h1>{data.title}</h1>
<p>{data.content}</p>
</div>
);
};
export const getStaticProps: GetStaticProps = async () => {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return {
props: {
data,
},
};
};
export default Page;
使用工具进行预渲染

一些工具可以预渲染您的 SPA 并将预渲染的 HTML 提供给搜索引擎。

  • Prerender.io:一项预渲染您的 JavaScript 应用程序并将静态 HTML 提供给搜索引擎的服务
  • Rendertron:一个无头 Chrome 渲染解决方案,可用于预渲染您的 SPA

延伸阅读

你会在什么时候使用 `document.write()`?

主题
Web APIJavaScriptHTML

TL;DR

document.write() 在现代 Web 开发中很少使用,因为它在页面加载后调用时会覆盖整个文档。它主要用于在初始页面加载期间编写内容等简单任务,例如用于教育目的或快速调试。但是,通常建议使用其他方法,如 innerHTMLappendChild() 或现代框架来操作 DOM。


你会在什么时候使用 document.write()

初始页面加载

document.write() 可用于在初始页面加载期间将内容直接写入文档。这是它可能适用的少数情况之一,因为它对于非常基本的任务可能更简单、更快。

<!doctype html>
<html>
<head>
<title>Document Write Example</title>
</head>
<body>
<script>
document.write('<h1>Hello, World!</h1>');
</script>
</body>
</html>

教育目的

document.write() 有时用于教育环境中,以演示基本的 JavaScript 概念。它提供了一种直接的方式来展示 JavaScript 如何操作 DOM。

快速调试

对于快速而粗略的调试,document.write() 可用于将变量或消息直接输出到文档。但是,不建议在生产代码中使用此方法。

var debugMessage = 'Debugging message';
document.write(debugMessage);

遗留代码

在一些较旧的代码库中,你可能会遇到 document.write()。虽然不建议在新项目中使用它,但了解它对于维护或重构旧代码可能很有用。

为什么不使用 document.write()

  • 覆盖文档:如果在页面加载后调用,document.write() 将覆盖整个文档,这可能导致内容丢失和糟糕的用户体验。
  • 更好的替代方案:像 innerHTMLappendChild() 和 React 或 Vue 等框架的现代方法提供了更多控制,并且使用起来更安全。
// 使用 innerHTML
document.getElementById('content').innerHTML = '<h1>Hello, World!</h1>';
// 使用 appendChild
var newElement = document.createElement('h1');
newElement.textContent = 'Hello, World!';
document.getElementById('content').appendChild(newElement);

延伸阅读

JavaScript 和浏览器中 `mouseenter` 和 `mouseover` 事件有什么区别?