<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ DOM - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ DOM - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 14 May 2026 14:35:05 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/tag/dom/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ JavaScript 中的 DOM 操作——面向初学者的全面指南 ]]>
                </title>
                <description>
                    <![CDATA[ 作为一种 Web 开发语言，JavaScript 赋予了开发者创建动态和交互式网页的能力，其中一个实现这种交互性的 JavaScript 关键特性是文档对象模型（DOM）操作。 DOM 操作允许开发者修改网页的结构、样式和内容。在本文中，我们将探讨 JavaScript 中 DOM 操作的基础知识，将复杂的概念分解成易于理解的代码片段。 什么是 DOM 文档对象模型（DOM）是一种用于 Web 文档的编程接口。它以对象树的形式表示文档的结构，其中每个对象对应于文档的一部分，例如元素、属性和文本。JavaScript 可以操作这个树状结构，允许开发者动态地改变网页的内容和外观。 如何访问 DOM 元素 为了操作 DOM，我们需要访问它的元素，这一步通常使用代表整个 HTML 文档的 document 对象来实现。让我们看一个简单的例子： // 使用一个元素的 ID 访问这个元素 const headerElement = document.getElementById('header'); // 使用类名访问元素 const paragraphs = document.getE ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/dom-manipulation-in-javascript/</link>
                <guid isPermaLink="false">65f10c90823801041102de9e</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ DOM ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tsukistar ]]>
                </dc:creator>
                <pubDate>Wed, 13 Mar 2024 02:17:02 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2024/03/Beige-Aesthetic-Neutral-Thesis-Defense-Presentation-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/dom-manipulation-in-javascript/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">DOM Manipulation in JavaScript – A Comprehensive Guide for Beginners</a>
      </p><!--kg-card-begin: markdown--><p>作为一种 Web 开发语言，JavaScript 赋予了开发者创建动态和交互式网页的能力，其中一个实现这种交互性的 JavaScript 关键特性是文档对象模型（DOM）操作。</p>
<p>DOM 操作允许开发者修改网页的结构、样式和内容。在本文中，我们将探讨 JavaScript 中 DOM 操作的基础知识，将复杂的概念分解成易于理解的代码片段。</p>
<h2 id="dom">什么是 DOM</h2>
<p>文档对象模型（DOM）是一种用于 Web 文档的编程接口。它以对象树的形式表示文档的结构，其中每个对象对应于文档的一部分，例如元素、属性和文本。JavaScript 可以操作这个树状结构，允许开发者动态地改变网页的内容和外观。</p>
<h3 id="dom">如何访问 DOM 元素</h3>
<p>为了操作 DOM，我们需要访问它的元素，这一步通常使用代表整个 HTML 文档的 document 对象来实现。让我们看一个简单的例子：</p>
<pre><code class="language-javascript">// 使用一个元素的 ID 访问这个元素
const headerElement = document.getElementById('header');

// 使用类名访问元素
const paragraphs = document.getElementsByClassName('paragraph');

// 使用标签名访问元素
const images = document.getElementsByTagName('img');
</code></pre>
<p>在上面的代码片段中，我们使用 <code>getElementById</code> , <code>getElementsByClassName</code> , 和 <code>getElementsByTagName</code> 三个函数来检索特定的元素。这三个函数的返回值可以存储在变量中，以供开发者进行进一步的操作。</p>
<h3 id="">如何修改元素内容</h3>
<p>一旦我们已经访问了一个元素，我们就可以使用这个元素的<code>innerHTML</code>属性来修改它的内容：</p>
<pre><code class="language-javascript">// 修改一个元素的内容
headerElement.innerHTML = 'New Header Text';
</code></pre>
<p>在上面的例子中，我们将 <code>headerElement</code> 的内容更改为 <code>New Header Text</code> 。这是一种简单而行之有效的方法，可以更新元素中的文本内容。</p>
<h2 id="">事件和事件处理</h2>
<p>事件是在浏览器中发生的操作或事务，例如用户点击按钮或调整窗口大小。JavaScript 允许我们处理这些事件并执行相应的代码作为对事件的响应。事件处理是创建交互式网页的关键方面。</p>
<h3 id="">如何添加事件监听器</h3>
<p>为了响应事件，我们可以使用事件监听器，它们是用于“监听”特定元素上的特定事件的函数。让我们以一个按钮点击事件为例：</p>
<pre><code class="language-javascript">// 访问一个按钮元素
const myButton = document.getElementById('myButton');

// 添加点击事件监听器
myButton.addEventListener('click', function() {
    alert('Button Clicked!');
});
</code></pre>
<p>在这个例子中，当 ID 为 <code>myButton</code> 的按钮被点击时，浏览器将弹出一个内容为 <code>Button Clicked!</code> 的提示框。事件监听器提供了一种根据用户交互执行自定义代码的方式。</p>
<h2 id="">如何修改样式</h2>
<p>DOM 操作还包括修改元素的样式，使我们能够创建视觉上吸引人的、动态的网页。</p>
<h3 id="">如何动态地修改样式</h3>
<p>我们可以使用元素的 <code>style</code> 属性来改变其外观。让我们以点击按钮时改变段落颜色的例子来说明：</p>
<pre><code class="language-javascript">// 访问一个段落元素
const myParagraph = document.getElementById('myParagraph');

// 访问一个按钮元素
const colorButton = document.getElementById('colorButton');

// 为按钮添加一个点击事件
colorButton.addEventListener('click', function() {
    // 修改段落的颜色样式
    myParagraph.style.color = 'blue';
});
</code></pre>
<p>在这个例子中，当 ID 为 <code>colorButton</code> 的按钮被点击时，ID 为 <code>myParagraph</code> 的段落的文本颜色将被更改为蓝色。</p>
<h2 id="">如何创建和修改元素</h2>
<p>除了修改现有元素之外，JavaScript 还允许我们创建新元素并将它们添加到 DOM 中。</p>
<h3 id="">如何创建新的元素</h3>
<p>使用 <code>createElement</code> 方法来创建一个新的 HTML 元素。让我们创建一个新的段落元素，并将其追加（添加）到文档的 body 中：</p>
<pre><code class="language-javascript">// 新建一个段落元素
const newParagraph = document.createElement('p');

// 为新段落设置文本内容
newParagraph.textContent = 'This is a new paragraph.';

// 将新创建的段落追加到文档的 body 中
document.body.appendChild(newParagraph);
</code></pre>
<p>在这个例子中，我们创建了一个新的 <code>p</code>（段落）元素，设置了它的文本内容，然后将其追加到文档的 body 中。</p>
<h3 id="">如何修改属性</h3>
<p>我们还可以修改现有元素的属性。让我们思考如何动态更改图像的来源：</p>
<pre><code class="language-javascript">// 访问一个图像元素
const myImage = document.getElementById('myImage');

// 修改图像的来源属性
myImage.src = 'new-image.jpg';
</code></pre>
<p>这个例子中, 我们访问了 ID 为 <code>myImage</code> 的图像元素，并将其 <code>src</code> 属性更改为 <code>new-image.jpg</code>，动态更新显示的图像。</p>
<h3 id="">如何更新表单的输入值</h3>
<p>让我们思考这样一个情景：你希望根据用户的交互来更新文本输入框的值：</p>
<pre><code class="language-javascript">// 访问一个文本输入框元素
const myInput = document.getElementById('myInput');

// 添加一个输入事件监听器
myInput.addEventListener('input', function() {
    // 使用输入值更新一个段落的内容
    document.getElementById('inputValue').textContent = myInput.value;
});
</code></pre>
<p>在这个例子中，当用户在 ID 为 <code>myInput</code> 的文本输入框中输入内容时，ID 为 <code>inputValue</code> 的段落将根据输入值动态更新段落内容。</p>
<h3 id="">如何切换可见性</h3>
<p>你可以通过使用 <code>display</code> 样式属性来切换元素的可见性。让我们创建一个按钮用于切换段落的可见性：</p>
<pre><code class="language-javascript">// 访问一个按钮元素
const toggleButton = document.getElementById('toggleButton');

// 访问一个段落元素
const toggleParagraph = document.getElementById('toggleParagraph');

// 添加一个点击事件监听器
toggleButton.addEventListener('click', function() {
    // 切换段落的可见性
    toggleParagraph.style.display = toggleParagraph.style.display === 'none' ? 'block' : 'none';
});
</code></pre>
<p>在这里，ID 为 <code>toggleParagraph</code> 的段落最初是可见的，单击 ID 为 <code>toggleButton</code> 的按钮会切换其可见性。</p>
<h2 id="dom">DOM 操作中的常见陷阱</h2>
<p>虽然 DOM 操作是创建动态网页的强大工具，但初学者经常会遇到一些常见陷阱，这些陷阱可能导致意外行为或错误。让我们探讨一些这样的陷阱，并提供如何避免它们的建议。</p>
<h3 id="dom">在 DOM 渲染完成前操作它们</h3>
<p>有时我们可能会在 DOM 尚未完全加载之前尝试操作它，这可能导致 JavaScript 尝试访问尚未呈现的元素。为了避免这种情况，最重要的是在执行任何 JavaScript 代码之前都需要等待 DOM 完全加载：</p>
<pre><code class="language-javascript">document.addEventListener('DOMContentLoaded', function() {
    // DOM 操作代码放在这里
});
</code></pre>
<p>通过将执行 DOM 操作的代码包裹在 <code>DOMContentLoaded</code> 事件监听器内部，你就可以确保它仅在 DOM 准备就绪时运行。</p>
<h3 id="">没有确认一个元素是否存在</h3>
<p>在尝试使用诸如 <code>getElementById</code> 这样的方法访问元素时，我们可能会假设元素存在并继续进行操作。但是，如果元素不存在于页面上，则可能会导致错误。</p>
<p>在操作元素之前，我们始终应该先检查该元素是否存在：</p>
<pre><code class="language-javascript">const myElement = document.getElementById('myElement');

if (myElement) {
    // 在这里对这个元素进行操作
} else {
    console.error('Element not found!');
}
</code></pre>
<p>这个简单的检查可以防止在操作不确定是否存在的元素时出现的错误。</p>
<h3 id="">忘记阻止默认行为</h3>
<p>在处理事件时，忘记阻止默认行为可能会导致意外的页面行为。例如，如果在没有阻止默认行为的情况下提交表单，页面可能会重新加载，导致数据丢失：</p>
<pre><code class="language-javascript">const myForm = document.getElementById('myForm');

myForm.addEventListener('submit', function(event) {
    // 阻止默认的表单提交
    event.preventDefault();

    // 你的表单处理代码放在这里
});
</code></pre>
<p>通过调用<code>event.preventDefault()</code>，你可以阻止与表单提交事件关联的默认行为，从而完全控制事件的处理方式。</p>
<h3 id="dom">执行效率低下的 DOM 查询</h3>
<p>在循环中执行效率低下的 DOM 查询会降低性能。每次查询都涉及遍历 DOM，不必要的查询会减慢网页的加载速度。</p>
<p>与其重复查询 DOM，不如缓存元素的引用：</p>
<pre><code class="language-javascript">// 在循环中执行的效率低下的查询
for (let i = 0; i &lt; 10; i++) {
    const myElement = document.getElementById('myElement');
    // 操作 `myElement`
}

// 循环之外的高效的查询
const myElement = document.getElementById('myElement');
for (let i = 0; i &lt; 10; i++) {
    // 操作 `myElement`
}
</code></pre>
<p>通过一次查询 DOM 并重复使用该 DOM 的引用，可以优化你的代码。</p>
<h3 id="">无法处理跨浏览器的兼容性</h3>
<p>不同的浏览器可能会略有不同地解释 JavaScript 和 DOM 操作。如果没有考虑跨浏览器兼容性，可能会导致代码的行为不一致。</p>
<p>使用特性检测，并考虑使用像 jQuery 或现代框架这样的库来处理跨浏览器的不一致性：</p>
<pre><code class="language-javascript">// 使用特性检测检查浏览器对 `addEventListener` 方法的支持：
if (document.addEventListener) {
    // 使用 addEventListener
} else {
    // 回退到另一种受支持的方法
}
</code></pre>
<p>通过在使用一些功能特性之前检查它们，你可以确保您的代码在各种浏览器上正常工作。</p>
<h2 id="dom">如何在框架中使用 DOM 操作</h2>
<p>虽然 JavaScript 允许直接操作 DOM，但现代 Web 开发通常涉及使用诸如 React 或 Vue.js 等框架。这些框架提供了一种更加结构化的方式来构建和管理用户界面。</p>
<h3 id="react">React 示例</h3>
<pre><code class="language-javascript">// React 组件渲染一个按钮并处理其点击事件
class MyButton extends React.Component {
    handleClick() {
        alert('React Button Clicked!');
    }

    render() {
        return (
            &lt;button onClick={() =&gt; this.handleClick()}&gt;Click me&lt;/button&gt;
        );
    }
}

// 将 React 组件渲染到 DOM 中
ReactDOM.render(&lt;MyButton /&gt;, document.getElementById('reactRoot'));
</code></pre>
<p>这个 React 示例创建了一个组件来处理按钮点击事件，它演示了一种更加声明式的 UI 开发方法。</p>
<h3 id="vuejs">Vue.js 示例</h3>
<pre><code class="language-javascript">// 具有一个数据属性和一个方法的 Vue.js 实例
new Vue({
    el: '#vueRoot',
    data: {
        message: 'Vue.js Message'
    },
    methods: {
        showMessage: function () {
            alert(this.message);
        }
    }
});
</code></pre>
<p>这个例子创建了一个 Vue.js 实例来管理数据和方法，展示了 Vue.js 的响应性和基于组件的结构。</p>
<h2 id="">总结</h2>
<p>在这个全面的指南中，我们深入探讨了 JavaScript 中的 DOM 操作。从访问元素到处理事件，从修改样式到创建新元素，我们以简单直接的方式涵盖了 DOM 操作的基本概念。</p>
<p>请记住，DOM 操作是创建动态和交互式网页的核心。通过掌握这些基本技术，你将能够构建引人入胜且用户友好的 Web 应用程序。随着你继续进行你的 JavaScript 学习之旅，更多的练习与实践将加深你对这些概念的理解，为你成为一名成功的 Web 开发者铺平道路。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 理解 DOM 事件和 JavaScript 事件监听器 ]]>
                </title>
                <description>
                    <![CDATA[ 浏览器中的 JavaScript 代码使用事件驱动编程模式。这意味着当浏览器中发生特定的 DOM 事件时，将执行一段代码作为对该操作的响应。 在本文中，我将帮助你理解如何使用 JavaScript 监听和响应 DOM 事件。 如果你需要复习一下 DOM，我已经写了一篇文章来解释什么是 DOM 以及 JavaScript 如何与之交互 [https://www.freecodecamp.org/news/introduction-to-the-dom/]。 什么是 DOM 事件，它们为什么有用 DOM 事件是浏览器暴露的信号，你可以利用这些信号运行一段 JavaScript 代码。 这些 DOM 事件会在用户与我们创建的应用程序进行交互时发生，例如点击按钮或在输入框中输入字母。 作为 web 开发人员，你可以指示 JavaScript 监听特定事件，并对该事件做出响应。 例如：  * 点击按钮时，更改段落文本。  * 提交表单时，使用 Fetch API 发送 POST 请求。 如何监听 DOM 事件 要监听事件，需要使用 addEventListener() 方法将事件 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/dom-events-and-javascript-event-listeners/</link>
                <guid isPermaLink="false">65ae29b3c8b4c103e5521044</guid>
                
                    <category>
                        <![CDATA[ DOM ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Miya Liu ]]>
                </dc:creator>
                <pubDate>Mon, 22 Jan 2024 04:19:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2024/01/DOM-events-feature-image.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/dom-events-and-javascript-event-listeners/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Understanding DOM Events and JavaScript Event Listeners</a>
      </p><p>浏览器中的 JavaScript 代码使用事件驱动编程模式。这意味着当浏览器中发生特定的 DOM 事件时，将执行一段代码作为对该操作的响应。</p><p>在本文中，我将帮助你理解如何使用 JavaScript 监听和响应 DOM 事件。</p><p>如果你需要复习一下 DOM，我已经写了一篇文章来解释<a href="https://www.freecodecamp.org/news/introduction-to-the-dom/">什么是 DOM 以及 JavaScript 如何与之交互</a>。</p><h2 id="-dom-">什么是 DOM 事件，它们为什么有用</h2><p>DOM 事件是浏览器暴露的信号，你可以利用这些信号运行一段 JavaScript 代码。</p><p>这些 DOM 事件会在用户与我们创建的应用程序进行交互时发生，例如点击按钮或在输入框中输入字母。</p><p>作为 web 开发人员，你可以指示 JavaScript 监听特定事件，并对该事件做出响应。</p><p>例如：</p><ul><li>点击按钮时，更改段落文本。</li><li>提交表单时，使用 Fetch API 发送 POST 请求。</li></ul><h2 id="-dom--1">如何监听 DOM 事件</h2><p>要监听事件，需要使用 <code>addEventListener()</code> 方法将事件监听器附加到元素上。</p><p><code>addEventListener()</code> 方法接受两个参数：</p><ul><li>要监听的事件 <code>type</code></li><li>事件触发时要运行的函数</li></ul><pre><code class="language-js">Element.addEventListener(type, function);</code></pre><p>回到示例，假设你想在点击按钮元素时更改段落文本，你可以这样做：</p><pre><code class="language-html">&lt;body&gt;
  &lt;p id="myParagraph"&gt;This is an example paragraph&lt;/p&gt;
  &lt;button id="changeText"&gt;Change Text&lt;/button&gt;
  &lt;script&gt;
    const button = document.querySelector('#changeText');

    function newText(event) {
      const p = document.querySelector('#myParagraph');
      p.innerText = 'The text has been changed';
    }

    button.addEventListener('click', newText);
  &lt;/script&gt;
&lt;/body&gt;</code></pre><p>要在 HTML 文档中插入 JavaScript 代码，我们需要使用 <code>script</code> 标签，如上图所示。</p><p>使用 <code>document.querySelector()</code> 方法选择按钮元素，然后在该元素上调用 <code>addEventListener()</code> 方法，为按钮附加一个事件监听器。</p><p>首先，指定要监听的事件类型 <code>type</code>，在本例中为 <code>click</code> 事件。然后，指定该事件发生时要运行的函数。</p><p>在上面的代码中，当 <code>click</code> 事件被触发时，<code>newText</code> 函数将被执行。</p><p>事件监听器还将发送一个 <code>event</code> 对象，其中包含触发事件的相关信息。这就是为什么在上面的 <code>newText</code> 函数中有一个 <code>event</code> 参数。</p><p>你可以将事件记录到控制台，查看其详细信息：</p><pre><code class="language-js">function newText(event) {
  console.log(event);
}</code></pre><p>如果你再次点击按钮，将得到以下输出结果：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2024/01/event-log-example-1.png" class="kg-image" alt="Event object log" width="600" height="400" loading="lazy"></figure><p>根据事件触发时要做的事情，你可能需要使用 <code>event</code> 对象中包含的信息。</p><p>在这里，我们要做的只是更改段落文本，因此不需要 <code>event</code> 对象。稍后，我们将在处理键盘事件时看到使用 <code>event</code> 对象的示例。</p><p>在浏览器中可以监听的事件有很多。以下是开发 web 应用时可能需要的一些最常见事件：</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>事件</th>
<th>触发事件的情况</th>
</tr>
</thead>
<tbody>
<tr>
<td>click</td>
<td>当你按下并释放鼠标的主按钮时，用于追踪按钮和可点击的元素</td>
</tr>
<tr>
<td>mousemove</td>
<td>当你移动鼠标光标时</td>
</tr>
<tr>
<td>mouseover</td>
<td>当你将鼠标光标移动到某个元素上方时，类似于 CSS 的 hover 状态</td>
</tr>
<tr>
<td>mouseout</td>
<td>当你的鼠标光标移动出元素的边界时</td>
</tr>
<tr>
<td>dblclick</td>
<td>当你点击两次时</td>
</tr>
<tr>
<td>DOMContentLoaded</td>
<td>当 DOM 内容完全加载时</td>
</tr>
<tr>
<td>keydown</td>
<td>当你在键盘上按下一个键时</td>
</tr>
<tr>
<td>keyup</td>
<td>当你在键盘上释放一个键时</td>
</tr>
<tr>
<td>submit</td>
<td>当一个表单被提交时</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>如果你想阅读 DOM 事件类型的完整列表，可以访问<a href="https://en.wikipedia.org/wiki/DOM_event">此页面</a>。</p><p>DOM 事件分为多个类别。在此，我们将只介绍两个在项目中最常用的事件：键盘事件和鼠标事件。</p><h2 id="-">键盘事件</h2><p>对于键盘，你可以跟踪按下和松开键时分别运行的 <code>keydown</code> 和 <code>keyup</code> 事件。</p><p>举例说明，请在控制台运行以下代码：</p><pre><code class="language-js">document.addEventListener('keydown', event =&gt; {
  console.log(`A key is pressed: ${event.key}`);
});

document.addEventListener('keyup', event =&gt; {
  console.log(`A key is released: ${event.key}`);
});</code></pre><p>运行上述代码后，慢慢按下键盘上的一个键，然后慢慢松开。</p><p>你应该会看到如下日志输出：</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/news/content/images/2024/01/keydown-event-1.png" class="kg-image" alt="Keyboard events log" width="600" height="400" loading="lazy"></figure><p>请注意 “keydown” 日志是如何在你按下按键时立即显示的，而 “keyup” 日志只有在你松开按键时才会显示。</p><p>键盘事件通常会附加到 <code>document</code> 对象而不是特定元素上，因为整个网站都应能监听到该事件。</p><h2 id="--1">鼠标事件</h2><p>除了键盘事件，DOM 还提供了一种跟踪鼠标事件的方法。</p><p>最常见的鼠标事件有：</p><ul><li><code>mousedown</code> - 鼠标按钮被按下</li><li><code>mouseup</code> - 鼠标按钮被释放</li><li><code>click</code> - 点击事件</li><li><code>dblclick</code> - 双击事件</li><li><code>mousemove</code> - 当鼠标移动到元素上时</li><li><code>contextmenu</code> - 打开上下文菜单时，例如单击鼠标右键时</li></ul><p>同样，你可以直接在 <code>document</code> 对象中添加事件监听器来测试这些事件：</p><pre><code class="language-js">document.addEventListener('mousedown', event =&gt; {
  console.log(`The mouse is pressed`);
});

document.addEventListener('mouseup', event =&gt; {
  console.log(`The mouse is released`);
});</code></pre><p>运行上面的代码，然后点击浏览器中的任意位置。你应该会看到 <code>mousedown</code> 和 <code>mouseup</code> 事件分别被记录下来。</p><h2 id="--2">如何移除事件监听器</h2><p>要移除附加到元素上的事件监听器，需要调用 <code>removeEventListener()</code> 方法，并传递事件的 <code>type</code> 和你传递给 <code>addEventListener()</code> 方法的 <code>function</code>，如下所示：</p><pre><code class="language-js">button.removeEventListener('click', newText);</code></pre><p>上述代码可以从 <code>button</code> 元素中移除 “click” 事件监听器。请注意，你需要在元素上调用 <code>removeEventListener()</code> 方法，同时将函数 <code>newText</code> 传递给该方法。</p><p>要正确地移除事件监听器，需要为事件附加函数引用。如果向 <code>addEventListener()</code> 方法传递一个无名函数，就无法移除该事件：</p><pre><code class="language-js">button.addEventListener('click', function (event) {
  alert('Button save is clicked');
});</code></pre><p>如果没有上面例子中的函数名，就无法移除事件监听器。</p><h2 id="-html-">如何使用 HTML 属性监听事件</h2><p>除了使用 <code>addEventListener()</code> 方法外，你还可以通过在 HTML 元素中添加 <code>on[eventname]</code> 属性来监听事件。</p><p>例如，假设你想监听按钮点击，你可以为按钮添加 <code>onclick</code> 属性，如下所示：</p><pre><code class="language-html">&lt;body&gt;
  &lt;button onclick="handleClick()"&gt;Click Me!&lt;/button&gt;
  &lt;script&gt;

    function handleClick(event) {
      alert('The button is clicked!');
    }
  &lt;/script&gt;
&lt;/body&gt;</code></pre><p>在上面的按钮元素中，我们添加了 <code>onclick</code> 属性，并将 <code>handleClick()</code> 函数传递给它。</p><p>当我们点击按钮时，`handleClick()` 函数将被执行。</p><p>你也可以使用 JavaScript 添加 <code>onclick</code> 属性，如下所示：</p><pre><code class="language-html">&lt;body&gt;
  &lt;button id="myBtn"&gt;Click Me!&lt;/button&gt;
  &lt;script&gt;
    const myBtn = document.querySelector('#myBtn');
    myBtn.onclick = handleClick;

    function handleClick(event) {
      alert('The button is clicked!');
    }
  &lt;/script&gt;
&lt;/body&gt;</code></pre><p>在这里，我们使用 JavaScript 将 <code>handleClick</code> 函数的引用赋值给 <code>onclick</code> 属性。</p><p>要删除 <code>onclick</code> 属性，可以将该属性赋值为空：</p><pre><code class="language-js">const myBtn = document.querySelector('#myBtn');
myBtn.onclick = null;</code></pre><h2 id="--3">你应该使用哪一种</h2><p>如你所见，有两种方法可以监听 DOM 事件：<code>addEventListener()</code> 方法和 <code>on[eventname]</code> HTML 属性。你应该使用哪一种呢？</p><p>答案是，当你需要更多扩展性时，可以使用 <code>addEventListener()</code> 方法，而当你希望事情简单化时，可以使用 <code>on[eventname]</code> 方法。</p><p>在开发 web 应用程序时，<code>.html</code> 文件只应作为页面的结构，而 <code>.js</code> 文件则应定义 web 应用程序的任何行为。</p><p>为了使应用程序更易于维护和扩展，JavaScript 应能访问 HTML 元素，但 HTML 元素不能执行 JavaScript 函数。这就是推荐使用 <code>addEventListener()</code> 方法的原因。</p><p>但是，<code>addEventListener()</code> 并不是没有代价的，这使得代码的阅读相当繁琐。</p><p>使用 <code>on[eventname]</code> 属性时，你只需在 HTML 元素中指定函数名称：</p><pre><code class="language-html">&lt;body&gt;
  &lt;button onclick="handleClick()"&gt;Click Me!&lt;/button&gt;
  &lt;script&gt;

    function handleClick(event) {
      alert('The button is clicked!');
    }
  &lt;/script&gt;
&lt;/body&gt;</code></pre><p>但使用 <code>addEventListener()</code> 方法时，需要查询所需的元素，调用该方法，然后指定要运行的事件和回调函数：</p><pre><code class="language-html">&lt;body&gt;
  &lt;button id="myBtn"&gt;Click Me!&lt;/button&gt;
  &lt;script&gt;
    const myBtn = document.querySelector('#myBtn');
    myBtn.addEventListener('click', handleClick);

    function handleClick(event) {
      alert('The button is clicked!');
    }
  &lt;/script&gt;
&lt;/body&gt;</code></pre><p>如上所示，在使用 <code>on[eventname]</code> 属性时，可以少写一些代码。</p><p>虽然这看起来无关紧要，但当你在大型应用程序中使用大量 HTML 和 JS 文件时，这可以是一件重要的事情。</p><p>此外，<code>addEventListener()</code> 方法还允许你为同一元素附加多个监听器，如下所示：</p><pre><code class="language-html">&lt;body&gt;
  &lt;button id="myBtn"&gt;Click Me!&lt;/button&gt;
  &lt;script&gt;
    const myBtn = document.querySelector('#myBtn');

    myBtn.addEventListener('click', handleClick);

    myBtn.addEventListener('click', handleClickTwo);

    function handleClick() {
      console.log('Run from handleClick function');
    }

    function handleClickTwo() {
      console.log('Run from handleClickTwo function');
    }
  &lt;/script&gt;
&lt;/body&gt;</code></pre><p>点击上面的按钮时，JavaScript 将同时执行两个事件监听器。</p><p>而使用 <code>onclick</code> 属性则无法做到这一点，因为每次只能指定一个函数作为引用：</p><pre><code class="language-html">&lt;body&gt;
  &lt;button id="myBtn"&gt;Click Me!&lt;/button&gt;
  &lt;script&gt;
    const myBtn = document.querySelector('#myBtn');

    myBtn.onclick = handleClick;
      
    // 当你把一个新的函数赋给 onclick，
    // 旧的函数被覆盖

    myBtn.onclick = handleClickTwo;

    function handleClick() {
      console.log('Run from handleClick function');
    }

    function handleClickTwo() {
      console.log('Run from handleClickTwo function');
    }
  &lt;/script&gt;
&lt;/body&gt;</code></pre><p>但我从未遇到过需要两次监听同一事件的情况，因此这一优势可能根本没有用处。</p><h2 id="--4">结语</h2><p>通过浏览器暴露的 DOM 事件，你可以以适当的方式响应用户操作。</p><p>这种使用事件监听器来完成特定任务的模式被称为事件驱动编程，在使用 JavaScript 开发 web 应用程序时会经常用到这种模式。</p><p>有两种方法可以监听事件：使用 <code>addEventListener()</code> JavaScript 方法和 <code>on[eventname]</code> HTML 属性。这两种方法各有利弊，因此你最好都能熟悉。</p><p>如果你喜欢这篇文章，并希望将自己的 JavaScript 技能提高到一个新的水平，我建议你点击<a href="https://codewithnathan.com/beginning-modern-javascript">这里</a>查看我的新书<em>《Beginning Modern JavaScript》</em>。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2024/01/beginning-js-cover.png" class="kg-image" alt="beginning-js-cover" width="600" height="400" loading="lazy"></figure><p>本书旨在让初学者轻松学习 JavaScript，让所有希望学习 JavaScript 的人都能轻松掌握。它以循序渐进的指南帮助你了解如何使用 JavaScript 创建动态 web 应用程序。</p><p>我在此承诺：你将真正感觉到自己了解自己在用 JavaScript 做什么。</p><p>下次见！</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ DOM 幕后揭秘 ]]>
                </title>
                <description>
                    <![CDATA[ 对于一名高效工作的前端开发者来说，了解 JavaScript 中的 DOM 和事件是如何运行非常重要。 本文将讲解 DOM 是什么以及如何运行的。 DOM 是什么 DOM 的全称是文档对象模型，是连接 JavaScript 和 web 浏览器之间的接口。 借助 DOM，你可以编写 JavaScript 来创建、修改、删除 HTML 元素，设置样式、类别和属性，监听以及响应事件。 HTML 文档生成 DOM 树之后，你就可以与之交互。DOM 是一个复杂的 API，包含与 DOM 树交互的方法和属性。 DOM 图解你可以在这个网站 [https://fritscher.ch/dom-css/]视觉化 DOM。 DOM 是如何运行的 – 幕后揭秘 DOM 的组织形式非常巧妙。顶层元素被称作EventTarget。你可以借助下图更好地了解工作原理。 EventTarget接口由可以接收事件、并且可以创建监听器的对象实现。换句话说，任何事件目标都会实现与该接口有关的这三个方法。虽然 Element  及其子项、Document  和 Window  是最常见的事件目标（Even ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/what-is-dom-in-javascript/</link>
                <guid isPermaLink="false">63e9aefdcf34b8063af88b0f</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ DOM ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ PapayaHUANG ]]>
                </dc:creator>
                <pubDate>Fri, 10 Feb 2023 09:30:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/02/What-is-DOM-and-Events--in-JavaScipt--2--1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/what-is-dom-in-javascript/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">What is the DOM? A Behind-the-Scenes Guide</a>
      </p><!--kg-card-begin: markdown--><p>对于一名高效工作的前端开发者来说，了解 JavaScript 中的 DOM 和事件是如何运行非常重要。</p>
<p>本文将讲解 DOM 是什么以及如何运行的。</p>
<h2 id="dom">DOM 是什么</h2>
<p>DOM 的全称是文档对象模型，是连接 JavaScript 和 web 浏览器之间的接口。</p>
<p>借助 DOM，你可以编写 JavaScript 来创建、修改、删除 HTML 元素，设置样式、类别和属性，监听以及响应事件。</p>
<p>HTML 文档生成 DOM 树之后，你就可以与之交互。DOM 是一个复杂的 API，包含与 DOM 树交互的方法和属性。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/09/Frame-70-1.png" alt="DOM 图解" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>DOM 图解</figcaption>
</figure>
<p>你可以在<a href="https://fritscher.ch/dom-css/">这个网站</a>视觉化 DOM。</p>
<h2 id="dom">DOM 是如何运行的 – 幕后揭秘</h2>
<p>DOM 的组织形式非常巧妙。顶层元素被称作<code>EventTarget</code>。你可以借助下图更好地了解工作原理。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/DOM-behind-the-scene-1.png" alt="DOM-behind-the-scene-1" width="600" height="400" loading="lazy"></p>
<p><code>EventTarget</code>接口由可以接收事件、并且可以创建监听器的对象实现。换句话说，任何事件目标都会实现与该接口有关的这三个方法。虽然 <strong>Element</strong> 及其子项、<strong>Document</strong> 和 <strong>Window</strong> 是最常见的事件目标（EventTarget），但其他对象也可以是。</p>
<p>Window 代表了浏览器窗口。所有全局的 JavaScript 对象、函数和变量都自动归属于 window 对象。全局变量是 window 对象的属性，全局函数是 window 对象的方法。即便是文档对象（HTML DOM）也是 window 对象的属性。</p>
<pre><code class="language-js">window.document.getElementById("header");

//两者相同

document.getElementById("header");
</code></pre>
<p>Nodes 归属于 DOM（即文档对象模型）。在 DOM 中，如元素、属性、文本等都被组织在一个分层级的树状结构中，相互为子或父元素。这些个体就被称为 node（节点）。</p>
<p>上图中的 Node 可以被 JavaScript 对象表示。我们通常使用<code>document.querySelector()</code>, <code>document.getElementById()</code>等方法来调用这些节点。</p>
<p>现在让我们看看文档。</p>
<h2 id="dom">如何使用 DOM 来选择、创建和删除元素</h2>
<p>借助 DOM，我们可以使用 JavaScript 来选择、删除和创建元素。</p>
<h3 id="">如何选择元素</h3>
<p>JavaScript 中有各种各样的方式来选择 HTML 元素：</p>
<ul>
<li>document.getElementById();</li>
<li>document.getElementByClassName();</li>
<li>document.getElementByTagName();</li>
<li>document.querySelector();</li>
<li>document.querySelectorAll();</li>
</ul>
<h4 id="documentgetelementbyid">如何使用 <code>document.getElementById()</code> 方法</h4>
<p><code>getElementById()</code>方法返回的元素 id 与传入的字符串匹配。因为 HTML 元素的 id 是唯一值，所以这种方式的速度最快。</p>
<p>例子:</p>
<pre><code class="language-js">const ele = document.getElementById("IDName");
console.log(ele); // 打印对应id名称的元素
</code></pre>
<h4 id="documentgetelementbyclassname">如何使用<code>document.getElementByClassName()</code>方法</h4>
<p><code>document.getElementByClassName()</code>返回一个<code>HTMLCollection</code>，这个集合中的元素类名与传入的字符串匹配。我们可以同时搜索多个类别名称，传入的时候用空格隔开，它会实时返回的 HTMLCollection。</p>
<p>“实时”是什么意思？就是说，一旦我们给传入的类名添加一个元素，HTMLCollection 会自动获取这个新的元素。</p>
<p>例子:</p>
<pre><code class="language-js">const ele = document.getElementByClassName("ClassName");
console.log(ele); // 打印实时HTMLCollection
</code></pre>
<h4 id="documentgetelementbytagname">如何使用<code>document.getElementByTagName();</code>方法</h4>
<p><code>document.getElementByTagName()</code>返回一个<code>HTMLCollection</code>，这个集合中的元素标签与传入的字符串匹配。可以对任何 HTML 元素调用这个方法，它会返回实时的 HTMLCollection。</p>
<p>例子:</p>
<pre><code class="language-js">const paragraph = document.getElementByTagName("p");
const heading = document.getElementByTagName("h1");

console.log(paragraph); // p 元素 HTMLCollection
console.log(heading); // h1 元素 HTMLCollection
</code></pre>
<h4 id="documentqueryselector">如何使用 document.querySelector() 方法</h4>
<p><code>document.querySelector()</code>返回满足传入条件的第一个 HTML 元素。在这个方法中我们可以传入类名、id和标签名。参见例子：</p>
<pre><code class="language-js">const id = document.querySelector("#idname"); // 使用id
const classname = document.querySelector(".classname"); // 使用类名
const tag = document.querySelector("p"); // 使用标签名
</code></pre>
<p>选择规则：</p>
<ul>
<li>若使用类别名，在开头要添加(.)，如 (".classname")</li>
<li>若使用 id， 在开头要添加(#)，如("#id")</li>
<li>若使用标签，可以直接使用标签名，如("p")</li>
</ul>
<h4 id="documentqueryselectorall">如何使用 document.querySelectorAll() 方法</h4>
<p><code>document.querySelectorAll()</code>是<code>querySelector</code>方法的延伸。这个方法返回<strong>所有</strong>匹配传入值的元素。该方法返回一个不会实时更新的 NodeList。</p>
<pre><code class="language-js">const ele = document.querySelectorAll("p");
console.log(ele); // 返回 p 标签的 NodeList
</code></pre>
<p><strong>注意：</strong> HTMLCollection 是实时更新的，但是 NodeList 并不是。</p>
<h3 id="">如何创建元素</h3>
<p>你可以在 JavaScript 中创建 HTML 元素，并且动态地添加到 HTML 中。可以使用<code>document.createElement()</code>来添加元素，在括号内传入元素的标签名。</p>
<p>元素创建成功之后，可以添加类名，属性以及该元素的文本内容。</p>
<p><strong>例子：</strong></p>
<pre><code class="language-js">const ele = document.createElement("a");
ele.innerText = "Click Me";
ele.classList.add("text-left");
ele.setAttribute("href", "www.google.com");

// 类似于以下标记
// &lt;a href="www.google.com"&gt;Click Me&lt;/a&gt;

// 在 HTML 中更新已有的元素
document.querySelector(".links").prepend(ele);
document.querySelector(".links").append(ele);
document.querySelector(".links").before(ele);
document.querySelector(".links").after(ele);
</code></pre>
<p>在上述例子中，我们在 JavaScript 中创建了一个 anchor 标签，并且添加了类名和属性。在 HTML 中更新这个元素有以下四种方法：</p>
<ul>
<li><code>prepend()</code>：在父节点的第一个子节点之前插入数据</li>
<li><code>append()</code>：对位于最后一个索引的元素之后插入数据或内容</li>
<li><code>before()</code>：在选定的元素前插入数据</li>
<li><code>after()</code>：在选定的元素后插入数据。也可以说是在匹配的其父节点的子节点列表之后插入。（可以不作为元素插入，作为相邻的文本内容插入。）</li>
</ul>
<h3 id="">如何删除元素</h3>
<p>我们已经了解了如何使用 JavaScript 创建元素以及如何将它添加到 HTML 中。但是如果我们想要删除一个 HTML 元素呢？很简单！只需要对这个元素调用<code>remove()</code>方法。</p>
<p><strong>例子：</strong></p>
<pre><code class="language-js">const ele = document.querySelector("p");

// ele被点击后就会被删除
ele.addEventListener('click', function(){
	ele.remove();
})
</code></pre>
<h2 id="javascriptcss">如何通过 JavaScript 来控制 CSS</h2>
<p>我们已经学习了如何通过 JavaScript 来控制 HTML，现在我们来学习如何通过 JavaScript 来控制 CSS，帮助你动态地更改页面。</p>
<p>如果想要通过点击一个元素，就更改这个元素的背景颜色的话，可以通过 JavaScript 来实现。</p>
<p><strong>语法如下：</strong></p>
<pre><code class="language-js">const ele = document.querySelector("desired element");

ele.style.propertyName = value;
// e.g.
//ele.style.color = red;
</code></pre>
<p>当使用 JavaScript 来修改 CSS 属性的时候，需要注意在 CSS 中 <code>-</code> 用来连接单词，如 <code>background-color</code>但在 JavaScript 中我们使用 <code>backgroundColor</code>（驼峰样式）。</p>
<p><strong>例子：</strong></p>
<pre><code class="language-js">const ele = document.querySelector("div");
ele.style.backgroundColor = "red";
</code></pre>
<p>假设你已经给项目编写好了 CSS，你希望通过 JavaScript 来添加类名，可以使用<code>classList</code>。</p>
<p><strong>例子：</strong></p>
<pre><code class="language-js">const ele = document.querySelector(".link");
ele.classList.add("bg-red"); // 为已经存在的列表添加 bg-red 类名
ele.classList.remove("pb-4");// 为已经存在的列表删除 pb-4 类名
ele.classList.toggle("bg-green"); // 将 bg-red 类切换到现有的类列表中，这意味着如果它已经存在，那么它将被删除，如果它不存在，它将被添加。
</code></pre>
<p>当我们使用<code>classList</code>时，直接对元素进行添加、删除或切换类。这就像在更新现有的类。</p>
<p><code>element.className</code>的特别之处在于，它删除了所有现有的类别，然后添加一个新的类别。</p>
<p><strong>例子：</strong></p>
<pre><code class="language-js">const ele = document.querySelector(".link");
ele.classList.add("bg-red"); // 在现有的类别中添加 bg-red 类别
ele.classList.remove("pb-4");//从现有的类别中删除 pb-4 类别

ele.className = "p-10"; // 删除所有现有类别，添加 p-10
</code></pre>
<h2 id="">如何使用事件处理器</h2>
<p>对象状态的改变被称作为<strong>事件</strong>。对事件做出反应的过程被称为<strong>事件处理</strong>。</p>
<p>当用户点击鼠标、让鼠标在元素上滑过、按键等时，事件就发生了。所以当你想在事件发生时做一些事情的话，就可以使用事件处理器来引发这个事件。</p>
<p>我们使用事件处理器在事件发生的时候来执行一些 JavaScript 代码。在 JavaScript 中有许多不同的事件处理器（这里是一份简单的<a href="https://way2tutorial.com/html/html5_events_handler_list.php">列表</a>），但将事件处理器添加到元素的方法相同。</p>
<p><strong>语法如下：</strong></p>
<pre><code class="language-js">const ele = document.querySelector("a");

ele.addEventListener("event", function(){
	// 回调函数
});
</code></pre>
<p>你可以使用的事件</p>
<ul>
<li>click（点击鼠标）</li>
<li>mouseover（鼠标移动到某个元素）</li>
<li>mouseout（鼠标离开某个元素）</li>
<li>keypress（按下键盘）</li>
<li>keydown（按下键盘）</li>
</ul>
<p><strong>这里是使用 “click” 事件的例子：</strong></p>
<pre><code class="language-js">const ele = document.querySelector("a");

ele.addEventListener("click", function(){
	ele.style.backgroundColor = "pink";
});
</code></pre>
<h2 id="">事件传播：冒泡和捕获</h2>
<p>事件传播决定了元素接收事件的顺序。有两种处理 DOM 中事件传播顺序的方式：事件冒泡和事件捕获。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/Frame-71-1.png" alt="Frame-71-1" width="600" height="400" loading="lazy"></p>
<h3 id="">什么是事件冒泡</h3>
<p>当一个事件发生在一个组件时，首先会在该组件上触发事件处理器，然后传播到它的父组件，一直向上到其他的父组件。</p>
<p>事件处理器的默认情况时从中心组件一路向外到最外层的组件传播。</p>
<h3 id="">什么是事件捕获</h3>
<p>这个过程和冒泡刚好相反。事件处理器首先作用在父组件，然后再作用到触发处理器的组件。</p>
<p>简言之，事件首先由最外层的元素捕获，然后传递到最内层的元素。这个过程也被称作涓涓细流。</p>
<p><strong>让我们看一下下面的例子：</strong></p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;Example&lt;/title&gt;
    &lt;style&gt;
        nav{
            display: flex;
            justify-content: center;
            padding: 30px;
        }

        nav li{
            list-style: none;
            padding: 5px;
        }

        nav li a{
            text-decoration: none;
            padding: 20px;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div&gt;
        &lt;nav&gt;
            &lt;li&gt;&lt;a href="#"&gt;Home&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href="#"&gt;About&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href="#"&gt;Contact&lt;/a&gt;&lt;/li&gt;
        &lt;/nav&gt;
    &lt;/div&gt;

    &lt;script&gt;
        const navbar = document.querySelector("nav");
        navbar.addEventListener('click', function(){
            navbar.style.backgroundColor="green"
        });

        const anchor = document.querySelector("a");
        anchor.addEventListener("click", function(){
            anchor.style.backgroundColor="pink";
        })
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>代码效果如图：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/Screenshot-2022-09-26-142920.png" alt="Screenshot-2022-09-26-142920" width="600" height="400" loading="lazy"></p>
<p>仔细看上面的代码示例，我分别在<code>nav</code>标签和<code>anchor</code>标签添加了事件处理器：当你点击<code>nav</code>的时候背景色会变成绿色，而当点击<code>anchor</code>标签的时候，背景色会看成粉色。</p>
<p>但是当你点击<code>anchor</code>标签的时候，anchor 和 nav 的背景色都发生了变化。这是因为<strong>事件冒泡</strong>。</p>
<p><strong>当你只点击 nav 元素的时候发生的事情：</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/Frame-72--1-.png" alt="Frame-72--1-" width="600" height="400" loading="lazy"></p>
<p><strong>当你只点击 anchor 元素的时候发生的事情：</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/Frame-73--1-.png" alt="Frame-73--1-" width="600" height="400" loading="lazy"></p>
<p>停止事件传播的方式是在事件发起的元素的事件处理器上使用 <code>stopPropagation()</code>。这样就可以阻止 nav 元素因为上面的原因被激活。</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;Example&lt;/title&gt;
    &lt;style&gt;
        nav{
            display: flex;
            justify-content: center;
            padding: 30px;
        }

        nav li{
            list-style: none;
            padding: 5px;
        }

        nav li a{
            text-decoration: none;
            padding: 20px;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div&gt;
        &lt;nav&gt;
            &lt;li&gt;&lt;a href="#"&gt;Home&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href="#"&gt;About&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href="#"&gt;Contact&lt;/a&gt;&lt;/li&gt;
        &lt;/nav&gt;
    &lt;/div&gt;

    &lt;script&gt;
        const navbar = document.querySelector("nav");
        navbar.addEventListener('click', function(){
            navbar.style.backgroundColor="green"
        });

        const anchor = document.querySelector("a");
        anchor.addEventListener("click", function(e){
            e.stopPropagation();
            anchor.style.backgroundColor="pink";
        })
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<h2 id="dom">如何遍历 DOM</h2>
<p>“优秀的 JavaScript 开发者应该知道如何遍历 DOM — 即<strong>从一个元素选择到另一个元素的行为</strong> ” – <a href="https://zellwk.com/blog/dom-traversals/">Zell Liew</a></p>
<p>现在我们来看看为什么遍历会比<code>document.querySelector()</code>方法好用，以及如何像专家一样遍历。</p>
<p>有三种遍历 DOM 的方式：</p>
<ul>
<li>向上</li>
<li>向下</li>
<li>相临</li>
</ul>
<h3 id="dom">如何向上遍历 DOM</h3>
<p>有两种向上遍历 DOM 的方式：</p>
<ul>
<li>parentElement</li>
<li>closest</li>
</ul>
<p><code>parentElement</code>是选取父元素的属性，如下：</p>
<pre><code class="language-js">const ele = document.querySelector("a");
console.log(ele.parentElement); // &lt;div&gt;
</code></pre>
<p><code>parentElement</code>可以完美地选择上一层的元素，而<code>closest</code>可以让你选择上几层的元素。 <code>closest</code>可以选择符合选择器最近的祖先元素。</p>
<p>使用<code>closest</code>的例子：</p>
<pre><code class="language-html">&lt;article id="target"&gt;
  &lt;h1 id="heading"&gt;This is the main heading&lt;/h1&gt;
  &lt;div id="outer-div"&gt;
    This is the outer div
    &lt;div id="inner-div"&gt;This is the inner div&lt;/div&gt;
  &lt;/div&gt;
&lt;/article&gt;
</code></pre>
<pre><code class="language-js">const innerDiv = document.querySelector("#inner-div");
console.log(innerDiv.closest("#target")); // article#target
</code></pre>
<p>在上述代码中我们选择离<code>#inner-div</code>最近并且 id 为<code>#target</code>的元素。</p>
<h3 id="dom">如何向下遍历 DOM</h3>
<p>我们可以在选择器中使用<code>children</code>方法来向下遍历，就可以选择到直属子元素。</p>
<p><strong>例子：</strong></p>
<pre><code class="language-html">&lt;div&gt;
    &lt;a href="#"&gt;Link-1&lt;/a&gt;
    &lt;a href="#"&gt;Link-2&lt;/a&gt;
    &lt;a href="#"&gt;Link-3&lt;/a&gt;
    &lt;a href="#"&gt;Link-4&lt;/a&gt;
&lt;/div&gt;
</code></pre>
<pre><code class="language-js">const ele = document.querySelector("div");
const child = ele.children;

console.log(child); // 返回 HTMLCollection
// div 中的 4 个元素
</code></pre>
<h3 id="dom">如何遍历相邻的 DOM</h3>
<p>遍历相邻的 DOM 很有趣，有两种主要的方法：</p>
<ul>
<li>previousElementSibling</li>
<li>nextElementSibling</li>
</ul>
<p>使用<code>previousElementSibling</code>方法，我们可以选择 HTML 中的前一个元素：</p>
<pre><code class="language-html">&lt;div&gt;
    &lt;a href="#"&gt;Link-1&lt;/a&gt;
    &lt;h1&gt;Heading&lt;/h1&gt;
&lt;/div&gt;
</code></pre>
<pre><code class="language-js">const ele = document.querySelector("h1");
console.log(ele.previousElementSibling); // &lt;a href="#"&gt;Link-1&lt;/a&gt;
</code></pre>
<p>使用<code>nextElementSibling</code>，我们可以选择 HTML 中的后一个元素：</p>
<pre><code class="language-html">&lt;div&gt;
    &lt;a href="#"&gt;Link-1&lt;/a&gt;
    &lt;h1&gt;Heading&lt;/h1&gt;
&lt;/div&gt;
</code></pre>
<pre><code class="language-js">const ele = document.querySelector("a");
console.log(ele.nextElementSibling); // &lt;h1&gt;Heading&lt;/h1&gt;
</code></pre>
<h2 id=""><strong>总结</strong></h2>
<p>希望你已经了解 JavaScript 中的 DOM 是如何工作的，感谢阅读！</p>
<p>你可以在以下地方关注我：</p>
<ul>
<li><a href="https://twitter.com/Kedar__98">Twitter</a></li>
<li><a href="https://www.linkedin.com/in/kedar-makode-9833321ab/?originalSubdomain=in">LinkedIn</a></li>
<li><a href="https://www.instagram.com/kedar_98/">Instagram</a></li>
</ul>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 学习 JavaScript DOM ]]>
                </title>
                <description>
                    <![CDATA[ 如果你刚开始学习 JavaScript，那么你可能听说过 DOM。但它到底是什么？ 在本文中，我将解释 DOM 是什么，并提供一些 JavaScript 代码示例。 我们将了解如何从 HTML 文档中选择元素、如何创建元素、如何更改内联 CSS 样式以及如何监听事件。 DOM 是什么 DOM 代表文档对象模型。它是一个编程接口，允许我们从文档中创建、更改或删除元素。我们还可以为这些元素添加事件以使我们的页面更​​加动态。 DOM 将 HTML 文档视为节点树。一个节点代表一个 HTML 元素。 让我们看一下这段 HTML 代码，以更好地理解 DOM 树结构。 <!DOCTYPE html> <html lang="en">   <head>     <meta charset="UTF-8">     <meta name="viewport" content="width=device-width, initial-scale=1.0">  ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/what-is-the-dom-document-object-model-meaning-in-javascript/</link>
                <guid isPermaLink="false">615fe96121a1350622df50d7</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ DOM ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Miya Liu ]]>
                </dc:creator>
                <pubDate>Mon, 08 Aug 2022 06:20:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/08/joan-gamell-ZS67i1HLllo-unsplash-1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/what-is-the-dom-document-object-model-meaning-in-javascript/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">What is the DOM? Document Object Model Meaning in JavaScript</a>
      </p><p>如果你刚开始学习 JavaScript，那么你可能听说过 DOM。但它到底是什么？</p><p>在本文中，我将解释 DOM 是什么，并提供一些 JavaScript 代码示例。</p><p>我们将了解如何从 HTML 文档中选择元素、如何创建元素、如何更改内联 CSS 样式以及如何监听事件。</p><h2 id="dom-"><strong>DOM 是什么</strong></h2><p>DOM 代表文档对象模型。它是一个编程接口，允许我们从文档中创建、更改或删除元素。我们还可以为这些元素添加事件以使我们的页面更​​加动态。</p><p>DOM 将 HTML 文档视为节点树。一个节点代表一个 HTML 元素。</p><p>让我们看一下这段 HTML 代码，以更好地理解 DOM 树结构。</p><pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="ie=edge"&gt;
    &lt;title&gt;DOM tree structure&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;DOM tree structure&lt;/h1&gt;
	&lt;h2&gt;Learn about the DOM&lt;/h2&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre><p>我们的文档称为根节点，其中包含一个子节点，即 <code>&lt;html&gt;</code> 元素。 <code>&lt;head&gt;</code> 元素包含两个子元素，即 <code>&lt;head&gt;</code> 和 <code>&lt;body&gt;</code> 元素。</p><p><code>&lt;head&gt;</code> 和 <code>&lt;body&gt;</code> 元素都有自己的子元素。 </p><p>这是可视化此节点树的另一种方法。</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/Document.jpg" class="kg-image" alt="Document" width="600" height="400" loading="lazy"></figure><p>我们可以访问文档中的这些元素并使用 JavaScript 对它们进行更改。</p><p>让我们看几个示例，了解如何使用 JavaScript 处理 DOM。</p><h2 id="-">如何选择文档中的元素</h2><p>在 HTML 文档中选择元素有几种不同的方法。</p><p>在本文中，我们将重点介绍其中三种方法：</p><ul><li><code>getElementById()</code></li><li><code>querySelector()</code></li><li><code>querySelectorAll()</code></li></ul><h3 id="getelementbyid-"><strong><code>getElementById()</code></strong></h3><p>在 HTML 中，<code>id</code> 被用作 HTML 元素的唯一标识符。这意味着你不能为两个不同的元素使用相同的 <code>id</code> 名称。</p><p>这是不正确的：</p><pre><code class="language-html">&lt;p id="para"&gt;This is my first paragraph.&lt;/p&gt;
&lt;p id="para"&gt;This is my second paragraph.&lt;/p&gt;</code></pre><p>你必须确保这些 id 是唯一的，如下所示：</p><pre><code class="language-html">&lt;p id="para1"&gt;This is my first paragraph.&lt;/p&gt;
&lt;p id="para2"&gt;This is my second paragraph.&lt;/p&gt;</code></pre><p>在 JavaScript 中，我们可以通过引用 <code>id</code> 名称来获取 HTML 标签。</p><pre><code class="language-js">document.getElementById("id name goes here")</code></pre><p>此代码告诉计算机获取 <code>id</code> 为 <code>para1</code> 的 <code>&lt;p&gt;</code> 元素并将该元素打印到控制台。</p><pre><code class="language-js">const paragraph1 = document.getElementById("para1");
console.log(paragraph1);</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/Screen-Shot-2021-09-26-at-2.25.49-PM.png" class="kg-image" alt="Screen-Shot-2021-09-26-at-2.25.49-PM" width="600" height="400" loading="lazy"></figure><p>如果我们只想读取段落的内容，那么我们可以使用 <code>console.log()</code> 中的 <code>textContent</code> 属性。</p><pre><code class="language-js">const paragraph1 = document.getElementById("para1");
console.log(paragraph1.textContent);</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/Screen-Shot-2021-09-26-at-2.35.31-PM.png" class="kg-image" alt="Screen-Shot-2021-09-26-at-2.35.31-PM" width="600" height="400" loading="lazy"></figure><h3 id="queryselector-"><strong><code>querySelector()</code></strong></h3><p>你可以使用此方法查找具有一个或多个 CSS 选择器的元素。</p><p>我已经为我最喜欢的电视节目创建了这个 HTML 示例：</p><pre><code class="language-html">&lt;h1&gt;Favorite TV shows&lt;/h1&gt;
&lt;ul class="list"&gt;
  &lt;li&gt;Golden Girls&lt;/li&gt;
  &lt;li&gt;Archer&lt;/li&gt;
  &lt;li&gt;Rick and Morty&lt;/li&gt;
  &lt;li&gt;The Crown&lt;/li&gt;
&lt;/ul&gt;</code></pre><p>如果我想找到 <code>h1</code> 元素并将其打印到控制台，那么我可以在 <code>querySelector()</code> 中使用该标签名称。</p><pre><code class="language-js">const h1Element = document.querySelector("h1");
console.log(h1Element);</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/Screen-Shot-2021-09-26-at-3.15.59-PM.png" class="kg-image" alt="Screen-Shot-2021-09-26-at-3.15.59-PM" width="600" height="400" loading="lazy"></figure><p>如果我想以 <code>class="list"</code> 为目标以将无序列表打印到控制台，那么我将在 <code>querySelector()</code> 中使用 <code>.list</code>。</p><p>在 <code>list</code> 前面的 <code>.</code> 告诉计算机以一个类名为目标。如果你想定位一个 <code>id</code>，那么你可以在名称前使用 <code>#</code> 符号。</p><pre><code class="language-js">const list = document.querySelector(".list");
console.log(list);</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/Screen-Shot-2021-09-26-at-3.22.45-PM.png" class="kg-image" alt="Screen-Shot-2021-09-26-at-3.22.45-PM" width="600" height="400" loading="lazy"></figure><h3 id="queryselectorall-"><strong><code>querySelectorAll()</code></strong></h3><p>此方法查找与 CSS 选择器匹配的所有元素并返回所有这些节点的列表。</p><p>如果我想在我们的列表中查找所有 <code>&lt;li&gt;</code> 项目，我将使用 <code>&gt;</code> 查找 <code>&lt;ul&gt;</code> 的所有子级。</p><pre><code class="language-js">const listItems = document.querySelectorAll("ul &gt; li");
console.log(listItems); </code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/Screen-Shot-2021-09-26-at-3.30.46-PM.png" class="kg-image" alt="Screen-Shot-2021-09-26-at-3.30.46-PM" width="600" height="400" loading="lazy"></figure><p>If we wanted to print out the actual如果我们想打印出所有电视节目 <code>&lt;li&gt;</code> 项目，我们可以使用 <code>forEach()</code> 遍历 NodeList 并打印出每个项目。</p><pre><code class="language-js">const listItems = document.querySelectorAll("ul &gt; li");

listItems.forEach((item) =&gt; {
  console.log(item);
});</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/Screen-Shot-2021-09-26-at-3.42.13-PM.png" class="kg-image" alt="Screen-Shot-2021-09-26-at-3.42.13-PM" width="600" height="400" loading="lazy"></figure><h2 id="--1">如何向文档添加新元素</h2><p>我们可以使用 <code>document.createElement()</code> 将新元素添加到 DOM 树中。</p><p>让我们看一下这个例子：</p><pre><code class="language-html">&lt;h1&gt;Reasons why I love freeCodeCamp:&lt;/h1&gt;</code></pre><p>现在，我在页面上只有一个 <code>&lt;h1&gt;</code> 标签，但我想使用 JavaScript 在 <code>&lt;h1&gt;</code> 标签下添加我喜欢 freeCodeCamp 的原因列表。</p><p>我们可以首先使用 <code>document.createElement()</code> 创建一个 <code>&lt;ul&gt;</code> 元素。我会把它赋值给变量 <code>unorderedList</code>。</p><pre><code class="language-js">let unorderedList = document.createElement("ul");
</code></pre><p>然后我们需要使用 <code>appendChild()</code> 方法将该 <code>&lt;ul&gt;</code> 元素添加到文档中。</p><pre><code class="language-js">document.body.appendChild(unorderedList);</code></pre><p>下一部分是使用 <code>createElement()</code> 方法在 <code>&lt;ul&gt;</code> 元素内添加几个 <code>&lt;li&gt;</code> 元素。</p><pre><code class="language-js">let listItem1 = document.createElement("li");

let listItem2 = document.createElement("li");</code></pre><p>然后我们可以使用 <code>textContent</code> 属性为我们的列表项添加文本。</p><pre><code class="language-js">let listItem1 = document.createElement("li");
listItem1.textContent = "It's free";

let listItem2 = document.createElement("li");
listItem2.textContent = "It's awesome";
</code></pre><p>最后一部分是使用 <code>appendChild()</code> 方法，以便可以将列表项添加到无序列表中。</p><pre><code class="language-js">let listItem1 = document.createElement("li");
listItem1.textContent = "It's free";
unorderedList.appendChild(listItem1);

let listItem2 = document.createElement("li");
listItem2.textContent = "It's awesome";
unorderedList.appendChild(listItem2);</code></pre><p>这就是所有代码：</p><pre><code class="language-js">let unorderedList = document.createElement("ul");
document.body.appendChild(unorderedList);

let listItem1 = document.createElement("li");
listItem1.textContent = "It's free";
unorderedList.appendChild(listItem1);

let listItem2 = document.createElement("li");
listItem2.textContent = "It's awesome";
unorderedList.appendChild(listItem2);</code></pre><p>这是页面上输出的样子：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/Screen-Shot-2021-09-26-at-4.21.55-PM.png" class="kg-image" alt="Screen-Shot-2021-09-26-at-4.21.55-PM" width="600" height="400" loading="lazy"></figure><h2 id="-style-css-">如何使用 Style 属性更改内联 CSS 样式</h2><p><code>style</code> 属性使你能够更改 HTML 文档中的 CSS。</p><p>在此示例中，我们将使用 <code>style</code> 属性将 <code>h1</code> 文本从黑色更改为蓝色。</p><p>这是我们的 HTML。</p><pre><code class="language-html">&lt;h1&gt;I was changed to blue using JavaScript&lt;/h1&gt;
</code></pre><p>我们首先需要使用 <code>querySelector()</code> 方法获取 <code>h1</code> 标签。</p><pre><code class="language-js">const h1 = document.querySelector("h1");</code></pre><p>然后我们使用 <code>h1.style.color</code> 将 <code>h1</code> 文本从黑色更改为蓝色。</p><pre><code class="language-js">const h1 = document.querySelector("h1");
h1.style.color = "blue";</code></pre><p>这是浏览器中的结果：</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/09/Screen-Shot-2021-09-26-at-4.33.44-PM.png" class="kg-image" alt="Screen-Shot-2021-09-26-at-4.33.44-PM" width="600" height="400" loading="lazy"></figure><p>你可以使用此 <code>style</code> 属性更改许多 CSS 内联样式，包括 <code>background-color</code>、<code>border-style</code>、<code>font-size</code> 等。</p><h2 id="-addeventlistener-">如何使用 addEventListener() 监听页面上的事件</h2><p>此方法允许你将事件附加到 HTML 元素（如按钮）。</p><p>在此示例中，当用户单击按钮时，将弹出一条警告消息。</p><p>在我们的 HTML 中，我们有一个 <code>id</code> 为 <code>btn</code> 的按钮元素。</p><pre><code class="language-html">  &lt;button id="btn"&gt;Show alert&lt;/button&gt;
</code></pre><p>我们可以使用 <code>getElementById()</code> 方法在 JavaScript 中定位该元素，并将其分配给名为 <code>button</code> 的变量。</p><pre><code class="language-js">const button = document.getElementById("btn");
</code></pre><p><code>addEventListener()</code> 接受一个事件类型和一个函数。事件类型将是 <code>click</code> 事件，该函数将触发警报消息。</p><p>这是将事件侦听器添加到 <code>button</code> 变量的代码。</p><pre><code class="language-js">button.addEventListener("click", () =&gt; {
  alert("Thank you for clicking me");
});</code></pre><p>这是完整的代码，你可以在其中单击按钮并弹出警报消息：</p><figure class="kg-card kg-embed-card"><iframe id="cp_embed_abwQwPb" src="https://codepen.io/jessica-wilkins/embed/preview/abwQwPb?default-tabs=css%2Cresult&amp;height=300&amp;host=https%3A%2F%2Fcodepen.io&amp;slug-hash=abwQwPb" title="Using addEventListener" scrolling="no" frameborder="0" height="300" allowtransparency="true" class="cp_embed_iframe" style="width: 100%; overflow: hidden;" loading="lazy"></iframe></figure><h2 id="-dom">如何在实际项目中使用 DOM</h2><p>以上对你可以使用的一些 DOM 方法的简要介绍，还有很多我们没有在本文中介绍的示例。</p><p>如果你想开始构建初学者 JavaScript 项目并使用 DOM，那么我建议您查看我的 <a href="https://www.freecodecamp.org/news/javascript-projects-for-beginners/">40 JavaScript Projects for Beginners</a> 文章。</p><h2 id="--2"><strong>总结</strong></h2><p>DOM 代表文档对象模型，是一种编程接口，允许我们从文档中创建、更改或删除元素。我们还可以为这些元素添加事件以使我们的页面更​​加动态。</p><p>你可以使用 <code>getElementById()</code>、<code>querySelector()</code> 和 <code>querySelectorAll()</code> 等方法在 JavaScript 中选择元素。</p><p>如果要向文档添加新元素，可以使用 <code>document.createElement()</code>。</p><p>你还可以使用 <code>style</code> 属性更改元素的内联 CSS 样式。</p><p>如果你想向按钮等元素添加事件，则可以使用 <code>addEventListener()</code>。</p><p>我希望你喜欢这篇文章，并祝你在 JavaScript 之旅中好运。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ DOM 完全指南 ]]>
                </title>
                <description>
                    <![CDATA[ 想必你已经听说过全能的 DOM 了——所以你才会在这里看这篇文章，对吧？如果你觉得它有点难，我可以保证，你在读完这篇文章之后就能够掌握操作 DOM 的相关知识。 开始之前，请允许我分享一个关于我是如何了解到 DOM 的趣事。 我是如何了解到 DOM 的 做了几个月的 web 开发之后，我还在学习古老的 HTML 和 CSS。我偶然地发现了 w3schools 的一个 DOM 课程，其中第一个示例是关于一个灯泡和两个按钮。 点击其中一个按钮会“打开”灯泡，点击另一个按钮则会“关闭”灯泡。我真的被搞晕了。 网站上的按钮为什么可以打开灯泡？怎么回事！？ 我甚至为此发了个 Twitter。然后我发现他们不过是改变了图片元素的 src 属性，我心都碎了。不过无论如何，这事让我爱上了 DOM，它激发了我的求知欲。 如果你耐心读完本文，并动手实践其中涉及的示例，我保证 DOM 操作再也难不倒你。准备好了吗？开始吧！ > 为了便于理解，我把相关知识归纳为以下几个章节。  * DOM 的定义及基本概念  * 如何选中 DOM 中的元素  * 如何遍历及移动 DOM 中的元素  * 如何 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-manipulate-the-dom-beginners-guide/</link>
                <guid isPermaLink="false">601e8c5d6183a705401563d4</guid>
                
                    <category>
                        <![CDATA[ DOM ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Humilitas ]]>
                </dc:creator>
                <pubDate>Sat, 17 Apr 2021 07:21:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/02/photo-1581291518857-4e27b48ff24e.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>想必你已经听说过全能的 DOM 了——所以你才会在这里看这篇文章，对吧？如果你觉得它有点难，我可以保证，你在读完这篇文章之后就能够掌握操作 DOM 的相关知识。</p>
<p>开始之前，请允许我分享一个关于我是如何了解到 DOM 的趣事。</p>
<h2 id="dom">我是如何了解到 DOM 的</h2>
<p>做了几个月的 web 开发之后，我还在学习古老的 HTML 和 CSS。我偶然地发现了 w3schools 的一个 DOM 课程，其中第一个示例是关于一个灯泡和两个按钮。</p>
<p>点击其中一个按钮会“打开”灯泡，点击另一个按钮则会“关闭”灯泡。我真的被搞晕了。</p>
<p>网站上的按钮为什么可以打开灯泡？怎么回事！？</p>
<p>我甚至为此发了个 Twitter。然后我发现他们不过是改变了图片元素的 src 属性，我心都碎了。不过无论如何，这事让我爱上了 DOM，它激发了我的求知欲。</p>
<p>如果你耐心读完本文，并动手实践其中涉及的示例，我保证 DOM 操作再也难不倒你。准备好了吗？开始吧！</p>
<blockquote>
<p>为了便于理解，我把相关知识归纳为以下几个章节。</p>
</blockquote>
<ul>
<li>DOM 的定义及基本概念</li>
<li>如何选中 DOM 中的元素</li>
<li>如何遍历及移动 DOM 中的元素</li>
<li>如何编辑 DOM 中的元素</li>
<li>指定样式</li>
<li>DOM 的事件处理</li>
</ul>
<p>喝杯咖啡放松一下，跟着我一起学习下面的章节。</p>
<p><img src="https://media.giphy.com/media/ceeFbVxiZzMBi/source.gif" alt="vvv-1" width="600" height="400" loading="lazy"></p>
<h2 id="dom">DOM 的定义及基本概念</h2>
<h3 id="dom">什么是 DOM？</h3>
<p>DOM（Document Object Model——文档对象模型），可以简单理解为浏览器创建的节点树，每个节点有自己的属性和方法，可以通过 JavaScript 来操作这些属性、调用这些方法。</p>
<p>操作 DOM 的能力是 JavaScript 最独特和有用的特性之一。</p>
<p>下图是 DOM 树的视觉表示。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/images.png" alt="images" width="600" height="400" loading="lazy"></p>
<p>document 对象是 DOM 的核心/基础，执行任何形式的 DOM 操作都要访问 document 对象。</p>
<p>根元素 <code>html</code> 是 document 对象的子节点。</p>
<p>下一行是 <code>body</code> 和 <code>head</code> 元素，它们互为兄弟节点，并且都是 <code>html</code> 的子节点。</p>
<p>head 元素下面有一个 title 元素，它是 head 元素的子节点，同时也是文本节点“my text”的父节点。</p>
<p>body 元素下面有两个元素（<code>a</code> 元素和 <code>h1</code> 元素），它们互为兄弟节点，并且都是 <code>body</code> 元素的子节点。</p>
<p>最后，<code>href</code> 属性和文本节点“my link”都是 <code>a</code> 元素的子节点，同样的，文本节点“My header”是 <code>h1</code> 元素的子节点。</p>
<p>这些对于新手来说可能看起来有些复杂，不过请相信我——熟能生巧。</p>
<h2 id="dom">如何选中 DOM 中的元素</h2>
<p>为了操作 DOM 中的元素，需要先选中指定的元素，幸好，我们有四种常用方式可以用来选中元素。</p>
<h3 id="getelementbyid">getElementById() 方法</h3>
<p>最常见的方式是通过 id 属性来选中元素。</p>
<p>下面的示例中，<code>getElementById()</code> 方法通过 id="master" 来查找元素。</p>
<pre><code class="language-javascript">&lt;p id="master"&gt;i love javascript&lt;/p&gt;

&lt;script&gt;
	const masterEl = document.getElementById('master')
	console.log(masterEl) //&lt;p id="master"&gt;i love javascript&lt;/p&gt; 
&lt;/script&gt;
</code></pre>
<p>id 是区分大小写的，比如：“master”和“Master”就是两个完全不同的 id。</p>
<p>选中了一个元素之后，就可以给它添加一些样式、编辑它的属性、遍历它的父元素或子元素。</p>
<h3 id="getelementsbyclassname">getElementsByClassName() 方法</h3>
<p>这个方法返回文档中所有类名（class）包含指定值的元素的集合。</p>
<p>例如，下面的 HTML 页面中有三个 class="master2" 的元素，我通过“btn”这个 id 选中了其中的按钮。</p>
<p>点击这个按钮将会选中所有类名包含“master2”的元素，并改变其中第三个元素的 <code>innerHTML</code> 属性值。</p>
<pre><code class="language-javascript">&lt;p class="master2"&gt;i love javascript&lt;/p&gt;
&lt;p class="master2"&gt;i love react&lt;/p&gt;
&lt;h1 class="master2"&gt;i want a job&lt;/h1&gt;

&lt;button id="btn"&gt;click me&lt;/button&gt;

&lt;script&gt;
	const btn = document.getElementById('btn')

	btn.addEventListener('click', function master(){
	   var master = document.getElementsByClassName("master2");
	   master[2].innerHTML = 'i need a job';
	})
&lt;/script&gt;
</code></pre>
<p>点击按钮前：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/22.png" alt="22" width="600" height="400" loading="lazy"></p>
<p>点击按钮后：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/444.png" alt="444" width="600" height="400" loading="lazy"></p>
<blockquote>
<p>这里用到了 <code>addEventListener()</code> 方法，稍后会介绍。</p>
</blockquote>
<h3 id="getelementsbytagname">getElementsByTagName() 方法</h3>
<p>这个方法接收 html 标签名作为参数，根据在文档中出现的先后顺序返回这个标签的所有元素。</p>
<p>下面的代码展示了 <code>getElementsByTagName()</code> 的语法，获取页面中的所有 <code>p</code> 元素、改变第二个 <code>p</code> 元素的内容。</p>
<pre><code class="language-javascript">&lt;p&gt;VsCode&lt;/p&gt;
&lt;p&gt;Atom&lt;/p&gt;
&lt;p&gt;Sublime text&lt;/p&gt;
&lt;button id="btn"&gt;click me&lt;/button&gt;

&lt;script&gt;
	const btn = document.getElementById('btn')

	btn.addEventListener('click', function master(){
		let master = document.getElementsByTagName('p');
		let masterEl = master[1].innerHTML = 'Code editors';
		console.log(masterEl) //Code editors
	})

	//&lt;p&gt;Atom&lt;/p&gt; changes to &lt;p&gt;Code editors&lt;/p&gt;
&lt;/script&gt;
</code></pre>
<h3 id="css">使用 CSS 选择器</h3>
<h4 id="queryselector">.querySelector()</h4>
<p>这个方法返回第一个匹配指定选择器的元素，它可以接收所有 CSS 样式选择器作为参数，它可以通过标签名、类名或 ID 来选择元素。</p>
<pre><code class="language-javascript">&lt;div id=master&gt;i am a frontend developer&lt;/div&gt;

&lt;script&gt;
	const master = document.querySelector("#master") 
&lt;/script&gt;
</code></pre>
<p>上面的方法接收一个 CSS 选择器作为参数，返回匹配这个选择器的第一个元素。</p>
<h4 id="queryselectorall">.querySelectorAll()</h4>
<p>这个方法跟上一个方法类似，区别在于它返回的是包含所有匹配元素的节点集合。</p>
<pre><code class="language-javascript">&lt;p class="master"&gt;React&lt;/p&gt;
&lt;p class="master"&gt;Vue&lt;/p&gt;
&lt;p class="master"&gt;Angular&lt;/p&gt;

&lt;script&gt;
	const master = document.querySelectorAll(".master") 
	console.log(master[1])  //&lt;p class="master"&gt;Vue&lt;/p&gt;
&lt;/script&gt;
</code></pre>
<h3 id="dom">选择 DOM 元素的方法总结</h3>
<p>在需要选择 DOM 元素的时候，你有四种不同的方式可以选择，这四种不同方式做的都是同一件事（选择一个或多个元素）。</p>
<p>如果不记得第一种方式，可以用第二种，如果碰巧前两种都忘了，还有第三种、第四种方式可以用。是不是我或者 JavaScript 让我们的生活变得更简单了？:)</p>
<p>我个人建议尽量使用第一种方式或第四种方式中的第一个方法（queryselector）。你之前学习 HTML 的时候应该知道，文档中元素的 id 是不允许重复的，也就是说 id 是文档中元素的唯一标识符。</p>
<p>考虑到这一点，通过 id 来选择元素是“安全”的做法，因为这样你就不会把同样的操作应用在多个不同元素上了（除非你刻意要这样做——当然了，只要你想的话，也可以选择其他方式）。</p>
<h2 id="document">如何遍历 Document</h2>
<p>现在你应该会认同我的说法，即 HTML 文档中的一切都是节点。就连 HTML 元素中的文字也是文本节点。</p>
<p>在 DOM 中，可以利用前面讲到的节点关系（父节点、子节点、兄弟节点等）来遍历节点树、访问节点。</p>
<blockquote>
<p>可以创建新节点，并且所有节点都可以被修改或删除。</p>
</blockquote>
<h3 id="">回顾一下</h3>
<ul>
<li>每个节点都有且只有一个父节点，根节点除外（它没有父节点）。</li>
<li>一个节点可以有多个子节点。</li>
<li>同一个父节点下的子节点互为兄弟节点。</li>
</ul>
<p>在这个章节中，我们要学习如何获取元素的父节点、兄弟节点以及子节点。为此，我会用到下面这些节点属性。</p>
<ul>
<li>parentNode</li>
<li>childrenNodes</li>
<li>firstElementChild</li>
<li>lastElementChild</li>
<li>nextElementSibling</li>
<li>previousElementSibling</li>
</ul>
<p>我会使用下面的 HTML 页面来展示这些节点属性的用法。在第四章节中，我会演示如何操作 DOM。</p>
<p>这就是本文的目的——了解如何操作 DOM。如果不懂如何操作 DOM 的话，了解了选择元素和遍历 DOM 的方法也没什么用处。了解如何为元素添加 CSS 样式、如何创建并追加元素、如何设置 innerHTML 以及如何做事件处理至关重要。</p>
<p>这是本文的重点，请跟着我的脚步，继续前进。</p>
<pre><code class="language-javascript">&lt;div id="parent"&gt;
	&lt;div id="firstchild"&gt;i am a first child&lt;/div&gt;
	&lt;p id="secondchild"&gt;i am the second child&lt;/p&gt;
	&lt;h4&gt;i am alive&lt;/h4&gt;
	&lt;h1&gt;hello world&lt;/h1&gt;
	&lt;p&gt;i am the last child&lt;/p&gt;
&lt;/div&gt;  

const parent = document.getElementById('parent').lastElementChild
console.log(parent) //&lt;p&gt;i am the last child&lt;/p&gt;

const parent2 = document.getElementById('parent').children[3]
console.log(parent2) //&lt;h1&gt;hello world&lt;/h1&gt;

const secondchild = document.getElementById('secondchild')
console.log(secondchild) //&lt;p id="secondchild"&gt;i am the second child&lt;/p&gt;

console.log(secondchild.parentNode) //&lt;div id="parent"&gt;...&lt;/div&gt;

console.log(secondchild.nextElementSibling) //&lt;h4&gt;i am alive&lt;/h4&gt;

console.log(secondchild.previousElementSibling) //&lt;div id="firstchild"&gt;i am a first child&lt;/div&gt;
</code></pre>
<h3 id="">创建元素</h3>
<p>上面代码中的 HTML 片段中有一个包含 5 个子元素的父元素，如果想使用 JavaScript 往其中添加一个 <code>div</code> 元素，首先需要调用 <code>createElement()</code> 方法来创建一个新的元素：</p>
<pre><code class="language-javascript">const createEl = document.createElement('div')
console.log(createEl) //&lt;div&gt;&lt;/div&gt;
</code></pre>
<h3 id="innerhtml">设置 innerHTML</h3>
<p>我们成功创建了一个 <code>div</code> 标签，不过现在其中还没有文本节点，调用 <code>.innerHTML()</code> 方法来为它添加文本节点。</p>
<pre><code class="language-javascript">const innerhtml = createEl.innerHTML = 'i am a frontend developer'
console.log(createEl) //&lt;div&gt;​i am a frontend developer​&lt;/div&gt;​
</code></pre>
<h3 id="">在页面中追加元素</h3>
<p>目前已经完成了创建元素以及往其中插入文本节点的操作，但是创建出的新元素还没有插入到 DOM 树中。</p>
<p>现在演示如何将创建出的新元素追加到 HTML 页面中，代码如下：</p>
<pre><code class="language-javascript">const createEl = document.createElement('div')
const innerhtml = createEl.innerHTML = 'i am a frontend developer'
const parentEl = document.getElementById('parent')
parentEl.appendChild(createEl)
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/Document---Google-Chrome-16_01_2021-11_50_14-PM--2-.png" alt="Document---Google-Chrome-16_01_2021-11_50_14-PM--2-" width="600" height="400" loading="lazy"></p>
<h3 id="">在指定元素之前插入元素</h3>
<p>注意上面的控制台打印信息截图，追加的 <code>div</code> 标签子元素自动地插入到了父元素的最下面。</p>
<p>如果出于某种原因想要把元素追加到其它位置该怎么办？也许是第一个元素之前或者是第四个元素之前的位置。这些情况都是很有可能出现的。下方代码把元素插入到当前第一个子元素之前。</p>
<p>我们会用到 <code>insertBefore()</code> 方法，它接收两个参数：<code>document.insertBefore(newNode, existingNode)</code>。</p>
<pre><code class="language-javascript">const parentEl = document.getElementById('parent')
const firstchildEl = document.getElementById('firstchild')

const createEl = document.createElement('div')

const innerhtml = createEl.innerHTML = 'i am a frontend developer'

parentEl.insertBefore(createEl, firstchildEl)
console.log(parentEl)
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/mmm.png" alt="mmm" width="600" height="400" loading="lazy"></p>
<h3 id="">替换子元素</h3>
<p>我们使用 <code>replaceChild()</code> 方法将第一个子元素替换为新创建的元素，它接收两个参数：<code>document.replaceChild(newNode, existingNode)</code>。</p>
<pre><code class="language-javascript">const firstchildEl = document.getElementById('firstchild')
const parentEl = document.getElementById('parent')

const createEl = document.createElement('div')
const innerhtml = createEl.innerHTML = 'i am a frontend developer'

parentEl.replaceChild(createEl, firstchildEl)

console.log(parentEl)                
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/kkk.png" alt="kkk" width="600" height="400" loading="lazy"></p>
<h3 id="">移除子元素</h3>
<p>我们使用 <code>removeChild()</code> 方法来移除指定元素（这里指的是第一个子元素），它接收一个参数：<code>document.removeChild(element)</code>。</p>
<pre><code class="language-javascript">const firstchildEl = document.getElementById('firstchild')
const parentEl = document.getElementById('parent')

parentEl.removeChild(firstchildEl)

console.log(parentEl)
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/vvv.png" alt="vvv" width="600" height="400" loading="lazy"></p>
<h2 id="css">为元素添加 CSS 样式</h2>
<p>前面的示例中演示了如何创建元素，并将其追加到指定的父元素。</p>
<p>为了设定元素样式，我们需要为它添加 CSS 类，这里使用 JavaScript 来操作。</p>
<p>我将为你演示如何添加、移除以及切换 CSS 类。</p>
<p>别担心，这并不难，我会为你详细介绍。</p>
<h3 id="css">添加 CSS 类</h3>
<p>现在有一个普通的 HTML 按钮，它的 id 为“master”、没有应用额外样式，如下图：</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/ttt.png" alt="ttt" width="600" height="400" loading="lazy"></p>
<p>第一步，为这个按钮创建一个 CSS 样式。</p>
<p>接着，在 JavaScript 代码部分为这个按钮添加一个事件监听器：点击按钮时为这个按钮元素添加“button”类样式。</p>
<pre><code class="language-javascript">&lt;style&gt;
	body{
		 background-color: hotpink;
		 display: flex;
		 align-items: center;
	}

	.button{
		 background-color: blueviolet;
		 width: 200px;
		 border: none;
		 font-size: 2rem;
		 padding: 0.5rem;
		 border-radius: 5px;
		 cursor: pointer;
	}
&lt;/style&gt;


&lt;button id="master"&gt;Click me&lt;/button&gt;


const buttonEl = document.getElementById('master')
buttonEl.addEventListener('click', addFunction)

function addFunction(){
	buttonEl.classList.add('button')
}
</code></pre>
<p>点击按钮之后，可以看到效果如下，好看吧？</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/jjj.png" alt="jjj" width="600" height="400" loading="lazy"></p>
<h3 id="css">移除 CSS 类</h3>
<p>还是使用上面的示例，我们要移除 CSS 类，这次会使用 <code>classList.remove()</code> 方法。也许你已经猜到结果了，对吧？</p>
<p>没错，这个按钮会变回默认样式。</p>
<pre><code class="language-javascript">const buttonEl = document.getElementById('master')
buttonEl.addEventListener('click', addFunction)

function addFunction(){
	buttonEl.classList.remove('button')
}
</code></pre>
<h3 id="css">切换 CSS 类</h3>
<p>如果不想完全移除这个 CSS 样式，你可能希望能有一种方式让按钮的样式在默认样式和指定样式之间来回切换。</p>
<p><code>classList.toggle()</code> 方法为这种操作提供了支持。</p>
<p>像 Twitter 之类的社交媒体平台上通常会用到 <code>classList.toggle()</code> 方法，它使得用户可以通过点击按钮为一个帖子点赞，也可以随时点击这个按钮来取消点赞。</p>
<p>可以通过 JavaScript 代码来检查按钮是否包含指定的 CSS 类。</p>
<p>如果按钮包含这个类，点击按钮会将这个类移除，反之亦然。</p>
<pre><code class="language-javascript">const buttonEl = document.getElementById('master')
buttonEl.addEventListener('click', addFunction)

function addFunction(){
    buttonEl.classList.toggle('button')
}
</code></pre>
<h1 id="">事件处理</h1>
<h3 id="html">什么是 HTML 事件？</h3>
<p>它是发生在 HTML 元素上的一些“事件”，比如按钮点击、文本输入等等。当事件触发时，就会执行对应的称为事件处理程序的 JavaScript 代码。</p>
<p>这些事件处理程序都是 JavaScript 函数，当元素触发某个事件时，事件处理器函数就会执行。</p>
<h3 id="">事件监听器</h3>
<p>到目前为止，我们基本上在之前的每个示例中都用到了事件监听器，可见它在操作 DOM 的过程中是多么重要。</p>
<p>为了给一个元素或者 DOM 对象添加事件监听器，需要用到 <code>addEventListener()</code> 方法。使用这个方法要优于以前在 html 标签中绑定事件监听器的做法。</p>
<p>这样能让 JavaScript 和 html 分离，使得代码更简洁、可读性更强。</p>
<p>我很认同将各类代码分离的做法，如果你的想法和我一样，那你应该会更喜欢这种绑定事件监听器的方式。</p>
<p>事件监听器接收三个参数。</p>
<ul>
<li>第一个参数是事件类型，如“click”。</li>
<li>第二个参数是将要执行的函数。</li>
<li>第三个参数是一个布尔值，指定使用事件冒泡还是事件捕获。 <strong>此参数是可选的</strong></li>
</ul>
<blockquote>
<p>可以为一个元素添加多种事件的处理程序。</p>
</blockquote>
<blockquote>
<p>也可以为一个元素的一种事件添加多个事件处理程序，比如给“click”事件添加两个事件处理程序。</p>
</blockquote>
<h2 id="">总结</h2>
<p>学会如何使用 JavaScript 操作 DOM 是非常重要的，这是必备的知识。</p>
<p>如果你理解了文中的示例和插图，可以自己构建一个小项目试试。如果想要成为优秀的开发者，动手实践至关重要。</p>
<p><img src="https://media.giphy.com/media/mVJ5xyiYkC3Vm/source.gif" alt="kkk-1" width="600" height="400" loading="lazy"></p>
<!--kg-card-end: markdown--><p>原文：<a href="https://www.freecodecamp.org/news/how-to-manipulate-the-dom-beginners-guide/">How to Manipulate the DOM - the Ultimate Beginner's Guide</a>，作者：<a href="https://www.freecodecamp.org/news/author/chibuike-okere/">Chibuike Okere</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 浏览器是如何渲染网页的 ]]>
                </title>
                <description>
                    <![CDATA[ 今天我们讨论的话题将专注于网页渲染以及它在Web开发中至关重要的作用。其实网上已经有许多谈论这个主题的文章了，但大多数文章提供的都是比较碎片化的信息，我需要查阅相当多的资料，才能完整地了解网页渲染。所以我决定写下这篇有一定综合性的文章。相信一方面能够帮助初学者了解网页渲染的原理，另一方面也能帮助有经验的同学细化巩固相关的知识结构。 不同的浏览器引擎运行起来会有些许差异，针对特定浏览器的具体内容会更加复杂。本文并不会涉及某个浏览器的底层原理，而是讨论一些通共的原则。 浏览器如何渲染网页 我们先来了解一下浏览器是如何对网页进行渲染的：  1. 浏览器将从服务器获取的HTML文档构建成文档对象模型DOM(Document Object Model).  2. 样式将被载入和解析，构成层叠样式表模型CSSOM(CSS Object Model).  3. 在DOM和CSSOM之上，渲染树(rendering     tree)将会被创建，代表一系列将被渲染的对象（这在Webkit内核中被称为renderer或者渲染对象render     object，在Gecko内核中被称为框架fra ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/webpage-rendering/</link>
                <guid isPermaLink="false">5fa2b3d05f583f0565090d9e</guid>
                
                    <category>
                        <![CDATA[ 浏览器 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ DOM ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web开发 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ 余博伦 ]]>
                </dc:creator>
                <pubDate>Thu, 12 Nov 2020 09:20:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/04/photo-1605123308459-abb648aa4f66.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>今天我们讨论的话题将专注于网页渲染以及它在Web开发中至关重要的作用。其实网上已经有许多谈论这个主题的文章了，但大多数文章提供的都是比较碎片化的信息，我需要查阅相当多的资料，才能完整地了解网页渲染。所以我决定写下这篇有一定综合性的文章。相信一方面能够帮助初学者了解网页渲染的原理，另一方面也能帮助有经验的同学细化巩固相关的知识结构。</p><p>不同的浏览器引擎运行起来会有些许差异，针对特定浏览器的具体内容会更加复杂。本文并不会涉及某个浏览器的底层原理，而是讨论一些通共的原则。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/11/image-15.png" class="kg-image" alt="image-15" width="1150" height="537" loading="lazy"></figure><h2 id="-">浏览器如何渲染网页</h2><p>我们先来了解一下浏览器是如何对网页进行渲染的：</p><ol><li>浏览器将从服务器获取的HTML文档构建成文档对象模型DOM(Document Object Model).</li><li>样式将被载入和解析，构成层叠样式表模型CSSOM(CSS Object Model).</li><li>在DOM和CSSOM之上，渲染树(rendering tree)将会被创建，代表一系列将被渲染的对象（这在Webkit内核中被称为renderer或者渲染对象render object，在Gecko内核中被称为框架frame）。渲染树映射除了不可见元素（例如<strong>&lt;head&gt;</strong>或者含有<strong>display:none;</strong>的标签）外的所有DOM结构。每一段文本字符串都将划分在不同的渲染对象中，每一个渲染对象都包含了它相应的DOM对象以及计算后的样式。换句话讲，渲染树是DOM的直观表示。</li><li>渲染树的每个元素包含的内容都是计算过的，它被称之为布局layout.浏览器使用一种流式处理的方法，只需要一次pass绘制操作就可以布局所有的元素（<strong>tables</strong>需要多次pass绘制，pass表示像素处理和顶点处理）。</li><li>最后布局完成，渲染树将转化为屏幕上的实际内容，这一步被称为绘制painting.</li></ol><h2 id="-repaint">重绘Repaint</h2><p>当页面元素样式的改变不影响元素在文档流中的位置时（例如<strong>background-color, border-color,visibility</strong>）,浏览器只会将新样式赋予元素并进行重绘操作。</p><h2 id="-reflow">回流Reflow</h2><p>当改变影响文档内容或者结构，或者元素位置时，回流操作就会被触发，一般有以下几种情况：</p><ul><li>DOM操作（对元素的增删改，顺序变化等）；</li><li>内容变化，包括表单区域内的文本改变；</li><li>CSS属性的更改或重新计算；</li><li>增删样式表内容；</li><li>修改class属性；</li><li>浏览器窗口变化（滚动或缩放）；</li><li>伪类样式激活（<strong>:hover</strong>等）。</li></ul><h2 id="--1">浏览器如何优化渲染</h2><p>浏览器本身会尽可能地减少其重绘或回流的次数，只更改必要的元素。例如一个<strong>position</strong>设置为<strong>absolute/fixed</strong>的元素的更改只会影响其本身和其子元素，而<strong>static</strong>的元素变化则会影响其之后的所有页面元素。</p><p>另外一项优化的技术则是在JavaScript代码运行时，浏览器会缓存所有的变化，然后只通过一次pass绘制操作来应用这些更改。例如下面这段代码只会触发一次重绘和回流：</p><pre><code class="language-js">var $body = $('body');
$body.css('padding', '1px'); // 触发重绘与回流
$body.css('color', 'red'); // 触发重绘
$body.css('margin', '2px'); // 触发重绘与回流
// 最终只有一次重绘和回流被触发

</code></pre><p>然而，根据我们之前提到过的，获取某个元素的属性将会触发强制回流。比如我们在刚才的代码中加上一句读取元素属性的操作：</p><pre><code class="language-js">var $body = $('body');
$body.css('padding', '1px');
$body.css('padding'); // 此处触发强制回流
$body.css('color', 'red');
$body.css('margin', '2px');

</code></pre><p>结果就会有两次回流发生。因此，我们应该尽量合并读取元素属性的操作来优化性能。</p><p>当然也有我们不得不触发强制回流的情况。比如说对同一个元素的<strong>margin-left</strong>属性进行两次操作——开始的时候赋值<strong>100px</strong>的距离，之后为了实现动画效果，再加上<strong>transition</strong>属性将距离改变到<strong>50px</strong>.</p><p>我们先定义一个CSS类：</p><pre><code class="language-css">.has-transition {
   -webkit-transition: margin-left 1s ease-out;
      -moz-transition: margin-left 1s ease-out;
        -o-transition: margin-left 1s ease-out;
           transition: margin-left 1s ease-out;
}

</code></pre><p>之后再对页面元素进行操作：</p><pre><code class="language-js">// 我们的元素开始默认含有 "has-transition" 的class属性
var $targetElem = $('#targetElemId');

// 移除默认的 "has-transition"
$targetElem.removeClass('has-transition');

// 此处的属性改变没有动画效果
$targetElem.css('margin-left', 100);

// 再加上原来的属性名
$targetElem.addClass('has-transition');

// 这次改变有动画效果
$targetElem.css('margin-left', 50);

</code></pre><p>但事实上这段代码并不会像注释描述的那样运作，每条语句的操作将被缓存，只有结果会在页面上显示，所以我们就需要手动进行一次强制回流：</p><pre><code class="language-js">// 移除默认的 "has-transition"
$(this).removeClass('has-transition');

// 此处的属性改变没有动画效果
$(this).css('margin-left', 100);

// 触发强制回流，上述两条语句的效果会马上在页面中显示
$(this)[0].offsetHeight; // 只是举个例子，别的触发方法也可以

// 再加上原来的属性名
$(this).addClass('has-transition');

// 这次改变有动画效果
$(this).css('margin-left', 50);

</code></pre><p>你可以<a href="https://link.zhihu.com/?target=http%3A//output.jsbin.com/qutev/1/" rel="nofollow noreferrer">在JSBin</a>预览这个例子。</p><h2 id="--2">优化渲染效率的几条最佳实践</h2><p>根据我查阅的一些资料，总结出以下几条优化建议：</p><ul><li>合法地书写HTML和CSS，不要忘了文档编码类型。样式文件应当在<strong> &lt;head&gt; </strong>标签中，脚本文件在<strong> &lt;body&gt;</strong> 结束前。</li><li>简化并优化你的CSS选择器（有些人可能CSS预处理器用习惯了从来不关注这一点）。将嵌套层减少到最小。CSS选择器根据其优先级具有不同的运行效率（从快到慢）：</li><li>ID选择器： <strong>#id</strong></li><li>类选择器： <strong>.class</strong></li><li>标签选择器： <strong>div</strong></li><li>相邻选择器： <strong>a + i</strong></li><li>子元素选择器： <strong>ul &gt; li</strong></li><li>通用选择器： <strong>*</strong></li><li>属性选择器： <strong>input[type="text"]</strong></li><li>伪类选择器： <strong>a:hover</strong></li></ul><p>浏览器中CSS选择器是从右到左进行匹配的（<a href="https://link.zhihu.com/?target=http%3A//stackoverflow.com/questions/5797014/why-do-browsers-match-css-selectors-from-right-to-left" rel="nofollow noreferrer">为什么浏览器要从右到左匹配样式选择器</a>），这也是为什么越短的选择器运行越快的原因（别提通用选择器，它会遍历所有元素）：</p><pre><code class="language-css">div * {...} // ×
.list li {...} // ×
.list-item {...} // √
#list .list-item {...} // √

</code></pre><ul><li>在你的脚本代码中，尽量减少DOM操作。缓存所有的内容，包括属性和对象（如果他们需要被复用的话）。尽量将元素缓存到本地之后再进行操作，最后再添加到DOM当中。</li><li>如果你使用jQuery进行DOM操作的话，最好遵循<a href="https://link.zhihu.com/?target=http%3A//www.ruanyifeng.com/blog/2011/08/jquery_best_practices.html" rel="nofollow noreferrer">jQuery最佳实践</a>。</li><li>修改元素样式时，更改其<strong>class</strong>属性是性能最高的方法。你的选择器越有针对性越好（这同样也有助于分离页面样式和逻辑）。</li><li>尽量只对 <strong>position</strong> 为 <strong>absolute/fixed</strong> 的元素设置动画。</li><li>在页面滚动时禁用 <strong>:hover</strong> 样式效果：</li></ul><pre><code class="language-css">.disable-hover {
  pointer-events: none;
}

</code></pre><pre><code class="language-js">var body = document.body,
    timer;

window.addEventListener('scroll', function() {
  clearTimeout(timer);
  if(!body.classList.contains('disable-hover')) {
    body.classList.add('disable-hover')
  }
  
  timer = setTimeout(function(){
    body.classList.remove('disable-hover')
  },500);
}, false);

</code></pre><p>如果你想对此话题进行更深入的了解，可以查阅：</p><ol><li><a href="https://link.zhihu.com/?target=http%3A//www.codeweblog.com/how-browsers-work-%25E4%25B8%25AD%25E6%2596%2587%25E7%2589%2588/" rel="nofollow noreferrer">How browsers work 中文版</a></li><li><a href="https://link.zhihu.com/?target=https%3A//segmentfault.com/a/1190000006917754" rel="nofollow noreferrer">【译】浏览器渲染：repaint,reflow/relayout,restyle</a></li></ol><p>原文：<a href="http://frontendbabel.info/articles/webpage-rendering-101/">What Every Frontend Developer Should Know About Webpage Rendering</a>，作者：Alexander Skutin</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
