<?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[ Mingxun Liu - 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[ Mingxun Liu - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 23 May 2026 08:28:37 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/author/mingxun/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 目标是 CLI 大师——CLI 入门 ]]>
                </title>
                <description>
                    <![CDATA[ CLI 是 Command-Line Interface 的简称，也就是 命令行界面 ，通俗来讲就是用输入命令的方式与计算机交互。可能大部分人都看到命令行很头疼，感觉这些东西复杂又难记，不过其实还好啦，大部分选项参数设计的都蛮合理的，只要稍微理解它，你也可以成为  CLI 大师。 参数和选项（arguments & options） 一条命令通常由三个部分组成：命令、选项和参数。命令很好理解，比如：whoami、curl 或是 node  等等这些都是命令。在使用命令时可能需要告诉它各种执行细节，这就是选项和参数的作用，它们根据命令不同具有不同的可选性，某些命令可能不需要参数，某些命令可能没有选项（虽然不常见，但这是允许的）。 选项 选项通常用于修改命令的行为或提供额外的信息。选项使用一个或两个短线（- ）开头，许多选项需要一个额外的值跟着它们。单短线的短选项格式，可以使用一个空格在选项和值之间隔开或是不隔开，不过更推荐使用空格隔开；如果是双短线的长选项格式，则必须使用一个空格在选项和值之间隔开（有时也用  =，具体使用请查阅使用命令的相关文档）。此外不需要任何额外值的短选项可以彼 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/getting-started-with-the-cli/</link>
                <guid isPermaLink="false">668f46c7dd1680043183e13e</guid>
                
                    <category>
                        <![CDATA[ 命令行 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mingxun Liu ]]>
                </dc:creator>
                <pubDate>Thu, 11 Jul 2024 06:54:03 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2024/07/nathan-da-silva-k-rKfqSm4L4-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><code>CLI</code> 是 <code>Command-Line Interface</code> 的简称，也就是 <code>命令行界面</code>，通俗来讲就是用输入命令的方式与计算机交互。可能大部分人都看到命令行很头疼，感觉这些东西复杂又难记，不过其实还好啦，大部分选项参数设计的都蛮合理的，只要稍微理解它，你也可以成为 <code>CLI</code> 大师。</p>
<h2 id="argumentsoptions">参数和选项（arguments &amp; options）</h2>
<p>一条命令通常由三个部分组成：命令、选项和参数。命令很好理解，比如：<code>whoami</code>、<code>curl</code> 或是 <code>node</code> 等等这些都是命令。在使用命令时可能需要告诉它各种执行细节，这就是选项和参数的作用，它们根据命令不同具有不同的可选性，某些命令可能不需要参数，某些命令可能没有选项（虽然不常见，但这是允许的）。</p>
<h3 id="">选项</h3>
<p>选项通常用于修改命令的行为或提供额外的信息。选项使用一个或两个短线（<code>-</code>）开头，许多选项需要一个额外的值跟着它们。单短线的短选项格式，可以使用一个空格在选项和值之间隔开或是不隔开，不过更推荐使用空格隔开；如果是双短线的长选项格式，则必须使用一个空格在选项和值之间隔开（有时也用 <code>=</code>，具体使用请查阅使用命令的相关文档）。此外不需要任何额外值的短选项可以彼此紧挨着使用。</p>
<h3 id="">参数</h3>
<p>一条命令可以有多个参数，用于标识信息的来源或目的地，对位置比较敏感，比如：<code>mv source.txt destination.txt</code>。参数一般是需要操作的内容，可能是一个文件路径、一段文本或是标准输入流。</p>
<p>Tips：在类 Unix 操作系统中，有些命令可以使用一个短线代替文件名，这将会使其处理来自标准输入的数据或将数据发送到标准输出。</p>
<h3 id="">使用语法说明</h3>
<p>当你第一次接触某个命令，想快速获取它的用法时，通常可以使用 <code>--help</code> 或 <code>-h</code> 此类选项查看一些简短的用法说明。下面这些语法说明会帮助你快速理解选项和参数的可选性：</p>
<ul>
<li>尖括号（<code>&lt;key&gt;</code>）代表<strong>必需</strong>参数，例如：<code>ping &lt;destination&gt;</code></li>
<li>方括号（<code>[key]</code>）代表<strong>可选</strong>参数，例如：<code>mkdir [OPTION] &lt;dirname&gt;</code></li>
<li>省略号（<code>...</code>）代表<strong>可重复</strong>参数，例如：<code>cp &lt;source1&gt; [source2...] &lt;dest&gt;</code></li>
<li>竖线（<code>|</code>）代表<strong>可选择</strong>参数，例如：<code>ip [OPTION] OBJECT { SUBCOMMAND | help }</code></li>
</ul>
<p>上述这些符号只是辅助理解如何使用命令的，在终端中实际使用这些命令时不需要再次按照参数的可选性增加这些符号，尖括号所包裹的参数在不容易混淆的情况下可能不会额外标记。</p>
<h2 id="redirectpipe">重定向和管道（redirect &amp; pipe）</h2>
<p>标准输入输出流（Standard Input/Output Streams，简称标准 I/O 流）是程序与外界交互的基础。标准 I/O 流主要包括标准输入流（<code>stdin</code>）、标准输出流（<code>stdout</code>）和标准错误流（<code>stderr</code>），分别可以用文件描述符 <code>0</code>、<code>1</code> 和 <code>2</code> 来指代。在 Unix 命令行中，我们经常使用重定向和管道来管理这些输入输出流，以便更高效地处理数据和信息。</p>
<h3 id="">重定向</h3>
<p>重定向的语法非常简单，使用 <code>&lt;</code> 代表重定向输入，使用 <code>&gt;</code> 代表重定向输出。通过重定向输入可以将文件内容作为标准输入流传递给程序，重定向输出则是将程序的标准输出流写入到文件，如果执行一个命令同时需要重定向输入和输出的话，看起来会是这样：<code>command &lt; input &gt; output</code>。重定向输入实际上等价于 <code>0&lt; input</code>，同样的重定向输出实际上等价于 <code>1&gt; output</code>。使用 <code>&gt;</code> 将会将标准输出流重定向到一个文件，如果文件存在则会覆盖原内容，也可以使用 <code>&gt;&gt;</code> 将标准输出流重定向并追加到指定文件。</p>
<p>某些情况下，我们可能需要把标准错误、标准输出一并记录并处理，那这时就需要重定向这些标准流。例如，我需要将标准错误重定向至标准输出，就需要这样：<code>command 2&gt;&amp;1</code>，如果是将标准输出重定向到标准错误，则是这样：<code>command 1&gt;&amp;2</code>。</p>
<h3 id="">管道</h3>
<p>在处理数据时，管道也是非常强大和常用的工具，使用 <code>|</code> 将左边命令的标准输出连接到右边命令的标准输入，可以将多个命令组合在一起。多条指令使用管道依次传递看起来会使这样：<code>command1 | command2 | command3</code>。管道允许实时处理数据流，命令可以即时地处理输入流的部分内容，而不必等到整个输入流完全生成。默认情况下，标准错误将不会被通过管道传递，它们将会合并并直接输出到终端上，你可以使用重定向标准错误到标准输出来改变这一行为。</p>
<h2 id="exitcode">返回值（exit code）</h2>
<p>与常见的 <code>True</code> <code>False</code> 布尔值返回结果不一样，在命令行中成功都应该返回 <code>0</code>，而其他的返回代码都可能代表着错误或失败，通常来说每个命令都会定义自己的错误码，如果你执行的命令出现了错误可以使用 <code>echo $?</code> 获取到错误码，这在写自动化脚本检测失败时会很有用。</p>
<p>结合 <code>&amp;&amp;</code> 和 <code>||</code> 运算符就可以根据命令的执行结果决定后续的操作，例如在 <code>command1 &amp;&amp; command2</code> 命令中，如果 <code>command1</code> 执行成功（exit code 等于 0）则执行 <code>command2</code>；反之在 <code>command1 || command2</code> 中，如果 <code>command1</code> 执行失败（exit code 不等于 0）则执行 <code>command2</code>。</p>
<h2 id="config">配置文件（config）</h2>
<p>通常这些命令的全局配置文件都在 <code>/etc</code> 或是 <code>/usr/local/etc</code> 目录中，用户自定义的配置文件通常在 <code>/home/&lt;username&gt;/.&lt;something&gt;rc</code> 此类的文件中。至于 rc 后缀的起源众说纷纭，我们约定成俗，见到 rc 一般当作配置文件都没什么问题。当然，每条命令也可以有自己的配置文件格式，例如：<code>curl</code> 的配置文件通常是按行写各种选项，而 <code>wget</code> 的配置文件则像是一个个的键值对。</p>
<h2 id="environmentvariable">环境变量（environment variable）</h2>
<p>环境变量是一种在操作系统中用来存储信息的机制，它们包含了系统运行时所需的各种信息，如路径、默认参数、语言设置等。每个环境变量都有一个名称和一个对应的值。通常来说定义环境变量应当全大写，设置环境变量可以采用多种方法，可以根据需要在命令行中临时设置或者在系统中永久生效。</p>
<p>某些命令行程序也会读取项目中会的 <code>.env</code> 或是其他环境变量文件，也会有像是 <code>NODE_ENV=development vite build</code> 此类直接在命令前定义的环境变量的情况，这种情况环境变量不会被记录仅在当前命令中有效。</p>
<p>我们还可以在终端中直接 <code>export</code> 一个环境变量，这样定义的环境变量将仅在当前会话中可用，重启终端或退出登录等操作都会使其失效。比如这样：</p>
<pre><code class="language-bash">export NODE_ENV=development
vite build
</code></pre>
<p>如果你想持久化的定义一个环境变量可以将它在 <code>.zshrc</code> 或是 <code>.bashrc</code> 等终端配置文件中 <code>export</code>，在启动该终端时自动添加这些环境变量。例如：</p>
<pre><code class="language-bash">export PATH="/usr/local/go/bin:$PATH"
</code></pre>
<h2 id="">小测</h2>
<details>
  <summary>指出 <code>ls -lah --time=ctime --color auto .</code> 命令中各个成分</summary>
    在 <code>ls -lah --time=ctime --color auto .</code> 中，<code>-lah</code> 是一组短选项，<code>--time=ctime</code> 是一个长选项并指定了一个值，<code>--color auto</code> 也是一个长选项它指定了一个值并使用空格分割，<code>.</code> 是参数指代当前目录，并且在这个例子中参数和选项都是可以省略的。
</details>
<details>
  <summary>重定向标准输出和标准错误到一个文件</summary>
    <ul>
        <li>正确的做法：<code>myprogram &gt; all_output.txt 2&gt;&amp;1</code>，这个命令表示先将标准输出（文件描述符1）重定向到 <code>all_output.txt</code>，然后将标准错误（文件描述符2）重定向到标准输出（文件描述符1），确保两个流都指向同一个文件。</li>
        <li>错误的做法：<code>myprogram 2&gt;&amp;1 &gt; all_output.txt</code>，这个命令的意思是将标准错误（文件描述符2）重定向到标准输出（文件描述符1），然后再将标准输出重定向到<code>all_output.txt</code>。由于标准输出在这时已经被重定向，所以标准错误仍然会输出到终端而不是文件中。</li>
    </ul>
</details>
<details>
  <summary>说出 <code>seq 1 5 | tr 1-9 a-z | sort -r</code> 的输出</summary>
<table style="undefined;table-layout: fixed; width: 219px"><colgroup>
<col style="width: 73px">
<col style="width: 73px">
<col style="width: 73px">
</colgroup>
<thead>
  <tr>
    <td>1<br>2<br>3<br>4<br>5</td>
    <td>a<br>b<br>c<br>d<br>e</td>
    <td>e<br>d<br>c<br>b<br>a</td>
  </tr></thead>
</table>
</details>
<h2 id="">结语</h2>
<p>相信你读完这篇文章对命令行再也没有那么恐惧了，大多数参数与选项设计得都很合理且便于记忆，稍加使用一段时间你将会发现命令实在是太酷了！</p>
<h2 id="">参考文档</h2>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Command-line_interface">https://en.wikipedia.org/wiki/Command-line_interface</a></li>
<li><a href="https://en.wikipedia.org/wiki/Redirection_(computing)">https://en.wikipedia.org/wiki/Redirection_(computing)</a></li>
<li><a href="https://en.wikipedia.org/wiki/Pipeline_(Unix)">https://en.wikipedia.org/wiki/Pipeline_(Unix)</a></li>
<li><a href="https://everything.curl.dev/cmdline/configfile.html">https://everything.curl.dev/cmdline/configfile.html</a></li>
<li><a href="https://www.gnu.org/software/wget/manual/wget.html#Sample-Wgetrc">https://www.gnu.org/software/wget/manual/wget.html#Sample-Wgetrc</a></li>
<li><a href="https://cn.vitejs.dev/guide/env-and-mode.html#env-variables">https://cn.vitejs.dev/guide/env-and-mode.html#env-variables</a></li>
</ul>
<!--kg-card-end: markdown--><p>Photo by <a href="https://unsplash.com/@silvawebdesigns?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash">Nathan da Silva</a> on <a href="https://unsplash.com/photos/macbook-pro-beside-white-ceramic-mug-on-brown-wooden-table-k-rKfqSm4L4?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash">Unsplash</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ CSS 过渡与动画手册——如何用 CSS 为元素制作动画 ]]>
                </title>
                <description>
                    <![CDATA[ CSS 过渡（Transition）和动画（Animation）为更改元素的样式提供了平滑、渐进的方式，但是它们的工作方式有些不同。 这是它们之间的主要区别： CSS 过渡 CSS 动画  * 创建一个从一个 CSS 值到另一个值平滑的过渡。   * 你需要触发器去运行 CSS 过渡。例如，你可以使用 `:hover` 伪类    [https://codesweetly.com/css-pseudo-selectors] 在用户指针悬停在元素上时运行过渡。   * 过渡只有两个状态：初始态和最终态。你不能创建中间步骤。   * 只运行一次。  * 最适合用在基础样式的更改。  * 从一个 CSS 关键帧到另一个关键帧的样式变更动画。   * CSS 动画不需要触发器。  * 动画允许你创建多种状态。  ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/css-transition-vs-css-animation-handbook/</link>
                <guid isPermaLink="false">6674044f5c4dc803d18ad2e7</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mingxun Liu ]]>
                </dc:creator>
                <pubDate>Thu, 20 Jun 2024 10:31:22 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2024/06/How-to-Animate-Elements-in-CSS-Cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/css-transition-vs-css-animation-handbook/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">CSS Transition vs Animation Handbook – How to Animate Elements in CSS</a>
      </p><!--kg-card-begin: markdown--><p>CSS 过渡（Transition）和动画（Animation）为更改元素的样式提供了平滑、渐进的方式，但是它们的工作方式有些不同。</p>
<p>这是它们之间的主要区别：</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><table>
    <thead>
        <tr>
            <th>CSS 过渡</th>
            <th>CSS 动画</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <ul>
                    <li>
                        创建一个从一个 CSS 值到另一个值平滑的过渡。
                    </li>
                    <li>
                        你需要触发器去运行 CSS 过渡。例如，你可以使用 `:hover` <a href="https://codesweetly.com/css-pseudo-selectors">伪类</a> 在用户指针悬停在元素上时运行过渡。
                    </li>
                    <li>
                        过渡只有两个状态：初始态和最终态。你不能创建中间步骤。
                    </li>
                    <li>只运行一次。</li>
                    <li>最适合用在基础样式的更改。</li>
                </ul>
            </td>
            <td>
                <ul>
                    <li>
                        从一个 CSS 关键帧到另一个关键帧的样式变更动画。
                    </li>
                    <li>CSS 动画不需要触发器。</li>
                    <li>动画允许你创建多种状态。</li>
                    <li>
                        你可以运行多次动画迭代——甚至是无数次。
                    </li>
                    <li>最适合使用在动态样式的更改。</li>
                </ul>
            </td>
        </tr>
    </tbody>
</table><!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>本手册通过一些例子去解释两种动画技术，以便你理解它们之间的异同。</p>
<h2 id="">目录</h2>
<ol>
<li><a href="#what-are-css-transitions">什么是 CSS 过渡？</a></li>
<li><a href="#categories-of-css-transition-properties">CSS 过渡属性的种类</a>
<ul>
<li><a href="#what-are-the-required-css-transition-properties">什么是必需 CSS 过渡属性？</a></li>
<li><a href="#what-is-the-css-transition-property">什么是 <code>transition-property</code>？</a></li>
<li><a href="#what-is-the-css-transition-duration-property">什么是 <code>transition-duration</code>？</a></li>
<li><a href="#examples-of-the-required-css-transition-properties">必需 CSS 过渡属性的示例</a></li>
<li><a href="#what-are-the-optional-css-transition-properties">什么是可选 CSS 过渡属性？</a></li>
<li><a href="#what-is-the-css-transition-timing-function-property">什么是 <code>transition-timing-function</code>？</a></li>
<li><a href="#what-is-a-css-transition-delay-property">什么是 <code>transition-delay</code>？</a></li>
<li><a href="#examples-of-the-optional-css-transition-properties">可选 CSS 过渡属性的示例</a></li>
</ul>
</li>
<li><a href="#shorthand-for-defining-the-css-transition-properties">CSS 过渡属性的缩写</a></li>
<li><a href="#what-is-css-animation">什么是 CSS 动画？</a>
<ul>
<li><a href="#components-of-css-animations">CSS 动画的组成</a></li>
<li><a href="#what-are-css-keyframes">什么是 CSS <code>@keyframes</code></a></li>
</ul>
</li>
<li><a href="#what-are-css-animation-properties">什么是 CSS animation 属性？</a>
<ul>
<li><a href="#what-is-the-css-animation-name-property">什么是 CSS <code>animation-name</code> 属性？</a></li>
<li><a href="#what-is-the-css-animation-duration-property">什么是 CSS <code>animation-duration</code> 属性？</a></li>
<li><a href="#what-is-the-css-animation-timing-function-property">什么是 CSS <code>animation-timing-function</code> 属性？</a></li>
<li><a href="#what-is-the-css-animation-delay-property">什么是 CSS <code>animation-delay</code> 属性？</a></li>
<li><a href="#what-is-the-css-animation-iteration-count-property">什么是 CSS <code>animation-iteration-count</code> 属性？</a></li>
<li><a href="#what-is-the-css-animation-direction-property">什么是 CSS <code>animation-direction</code> 属性？</a></li>
<li><a href="#what-is-the-css-animation-play-state-property">什么是 CSS <code>animation-play-state</code> 属性？</a></li>
<li><a href="#what-is-the-css-animation-fill-mode-property">什么是 CSS <code>animation-fill-mode</code> 属性？</a></li>
</ul>
</li>
<li><a href="#what-is-a-css-animation-property">什么是 CSS <code>animation</code> 属性？</a></li>
<li><a href="#important-stuff-to-know-about-css-transitions-and-animations">CSS 过渡和动画的重要知识要点</a></li>
<li><a href="#wrapping-up">总结</a></li>
</ol>
<p>话不多说，我们讨论一下 CSS 过渡。</p>
<h2 id="what-are-css-transitions">什么是 CSS 过渡？</h2>
<p><strong>CSS 过渡</strong>提供了一种平滑、渐进的方式去更改指定的 CSS 属性值。</p>
<p>这样，CSS 过渡会使变化在指定时间段内平滑地发生，而不是让浏览器去立即更改某个属性值。</p>
<p>例如，假设你希望当悬停时更改一个元素的尺寸。在这个例子中，你有两个选项：</p>
<ol>
<li>不使用 CSS 过渡实现更改。</li>
<li>使用 CSS 过渡从元素的初始尺寸平滑地过渡到它的新状态。</li>
</ol>
<p>让我们看些这两个选项的例子。</p>
<h3 id="css">不使用 CSS 过渡如何在鼠标悬停时更改图片的尺寸</h3>
<pre><code class="language-css">img {
  width: 40%;
}

img:hover {
  width: 100%;
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-transitions/js-dsymqf"><strong>试着编辑它</strong></a></p>
<p>因为我们没有使用 CSS 过渡，上面的代码立即将图片的尺寸从初始宽度（<code>40%</code>）更改为新的宽度（<code>100%</code>）。</p>
<p>使用 CSS 过渡，你可以获得更愉悦的体验。让我们看看下面的示例。</p>
<h3 id="css">使用 CSS 过渡如何在鼠标悬停时更改图片的尺寸</h3>
<pre><code class="language-css">img {
  width: 40%;
  transition: width 3s ease-out 0.4s;
}

img:hover {
  width: 100%;
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-transitions/js-ufwgbu"><strong>试着编辑它</strong></a></p>
<p>从 <code>width: 40%</code> 到 <code>width: 100%</code>，<code>transition</code> 属性在图片上应用了一个平滑、渐进的过渡。</p>
<h2 id="categories-of-css-transition-properties">CSS 过渡属性的种类</h2>
<p>我们有两种 CSS 过渡属性：</p>
<ul>
<li><em>必需</em>过渡属性</li>
<li><em>可选</em>过渡属性</li>
</ul>
<p>让我们来讨论一下这两者。</p>
<h3 id="what-are-the-required-css-transition-properties">什么是必需 CSS 过渡属性</h3>
<p>创建 CSS 过渡的两个必需属性：</p>
<ul>
<li><code>transition-property</code></li>
<li><code>transition-duration</code></li>
</ul>
<h4 id="what-is-the-css-transition-property">什么是 <code>transition-property</code>？</h4>
<p>CSS 的 <code>transition-property</code> 指定你希望从初始态过渡到新状态的 CSS 属性。</p>
<h4 id="what-is-the-css-transition-duration-property">什么是 <code>transition-duration</code> ？</h4>
<p>CSS 的 <code>transition-duration</code> 属性定义了浏览器完成所选择元素过渡的时间长度。换句话说，<code>transition-duration</code> 设置从开始到结束所需的全部时间。</p>
<p><strong>注意以下几点：</strong></p>
<ul>
<li><code>transition-duration</code> 属性只接受毫秒（ms）或秒（s）的时间形式。</li>
<li>0 秒（<code>0s</code>）是 <code>transition-duration</code> 的默认值。因此，如果你不定义 <code>transition-duration</code> 属性，将不会有<a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/transitionend_event">过渡事件</a>发生。</li>
<li><code>transition-duration</code> 只接受 0 或正数值。如果你提供其他任何值浏览器将忽略它。</li>
</ul>
<h3 id="examples-of-the-required-css-transition-properties">必需 CSS 过渡属性的示例</h3>
<p>下面是两个必需 CSS 过渡属性的一些示例。</p>
<h4 id="3">如何在 3 秒内完成元素宽度的过渡</h4>
<pre><code class="language-css">img {
  width: 40%;
  transition-property: width;
  transition-duration: 3s;
}

img:hover {
  width: 100%;
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-transitions/js-cq27rd"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>transition-property</code> 告诉浏览器把 <code>img</code> 的 <code>width</code> 从它的初始值（<code>40%</code>）过渡到它的新状态（<code>100%</code>）。</li>
<li>使用 <code>transition-duration</code> 属性定义过渡从开始到结束持续 3 秒（<code>3s</code>）的时间。</li>
</ol>
<p>因此，浏览器将在 3 秒（<code>3s</code>）内从老值到新值间平滑地过渡，而不是立即把图片从初始宽度（<code>40%</code>）更改到新尺寸（<code>100%</code>）。</p>
<h4 id="5">如何在 5 秒内完成字体尺寸的过渡</h4>
<pre><code class="language-css">p {
  font-size: 1rem;
  transition-property: font-size;
  transition-duration: 5s;
}

p:hover {
  font-size: 7rem;
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-transitions/js-huvkzp"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>transition-property</code> 告知浏览器过渡 <code>p</code> 元素的 <code>font-size</code>。</li>
<li>使用 <code>transition-duration</code> 属性定义过渡从开始到结束持续 5 秒（<code>5s</code>）的时间。</li>
</ol>
<p>因此，浏览器将在 5 秒（<code>5s</code>）内从老值到新值间平滑地过渡，而不是立即把 <code>p</code> 的初始字体尺寸（<code>1rem</code>）更改到新尺寸（<code>7rem</code>）。</p>
<p>现在让我们讨论一下可选 CSS 过渡属性。</p>
<h3 id="what-are-the-optional-css-transition-properties">什么是可选 CSS 过渡属性</h3>
<p>这是两个可选 CSS 过渡属性：</p>
<ul>
<li><code>transition-timing-function</code></li>
<li><code>transition-delay</code></li>
</ul>
<h4 id="what-is-the-css-transition-timing-function-property">什么是 <code>transition-timing-function</code>？</h4>
<p>CSS 的 <code>transition-timing-function</code> 属性定义了所选元素实现过渡的时机（速度）。</p>
<p>换句话说，<code>transition-timing-function</code> 指定了在持续时间内的不同时间间隔实现过渡的速度。</p>
<p><code>transition-timing-function</code> 属性接受的值如下：</p>
<ul>
<li><code>ease</code>：开始时过渡缓慢，然后加快速度，并缓慢结束。<code>ease</code> 是属性 <code>transition-timing-function</code> 的默认值。它等价于 <code>cubic-bezier(0.25,0.1,0.25,1)</code>。</li>
<li><code>ease-in</code>：开始时过渡很慢，随后逐渐增加速度。它等价于 <code>cubic-bezier(0.42,0,1,1)</code>。</li>
<li><code>ease-out</code>：开始很快，结束时很慢。它等价于 <code>cubic-bezier(0,0,0.58,1)</code>。</li>
<li><code>ease-in-out</code>：开始和结束很慢。它等价于 <code>cubic-bezier(0.42,0,0.58,1)</code>。</li>
<li><code>linear</code>：在整个过渡的持续时间内使用一致的速度开始和结束过渡。它等价于 <code>cubic-bezier(0,0,1,1)</code>。</li>
<li><code>cubic-bezier(p1, p2, p3, p4)</code>：允许你定义<a href="https://www.cssportal.com/css-cubic-bezier-generator/">三次贝塞尔曲线</a>的值。</li>
</ul>
<h4 id="what-is-a-css-transition-delay-property">什么是 <code>transition-delay</code>？</h4>
<p>CSS 的 <code>transition-delay</code> 属性定义了浏览器在开始过渡前需要等待多长时间。</p>
<p><strong>注意以下几点：</strong></p>
<ul>
<li><code>transition-delay</code> 必须使用毫秒（ms）或秒（s）的形式。</li>
<li><code>0s</code> 是 <code>transition-delay</code> 的默认值，这会使过渡在浏览器将其应用到 HTML 元素时立即开始。</li>
<li>一个负值会使过渡从指定时间立即开始。举例来说，假设一个元素的 <code>transition-delay</code> 值设为 <code>-3s</code>，过渡将在 3 秒时立即执行。</li>
<li>一个正值会使过渡等待指定的延迟时间后开始。举例来说，假设一个元素的 <code>transition-delay</code> 值设为 <code>3s</code>，这次过渡将延迟 3 秒后开始。</li>
</ul>
<h3 id="examples-of-the-optional-css-transition-properties">可选 CSS 过渡属性的示例</h3>
<p>下面是两个可选 CSS 过渡属性的一些示例。</p>
<h4 id="easeout">如何使用 ease-out 速度完成元素宽度的过渡</h4>
<pre><code class="language-css">img {
  width: 40%;
  transition-property: width;
  transition-duration: 3s;
  transition-timing-function: ease-out;
}

img:hover {
  width: 100%;
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-transitions/js-tqzgmf"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>transition-property</code> 告知浏览器过渡 <code>img</code> 元素的宽度。</li>
<li>我们使用 <code>transition-duration</code> 属性定义过渡从开始到结束 3 秒（<code>3s</code>）的持续时间。</li>
<li>我们使用 <code>transition-timing-function</code> 属性快速地开始过渡然后缓慢地结束。</li>
</ol>
<h4 id="linear">如何使用 linear 速度完成元素宽度的过渡</h4>
<pre><code class="language-css">img {
  width: 40%;
  transition-property: width;
  transition-duration: 3s;
  transition-timing-function: linear;
}

img:hover {
  width: 100%;
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-transitions/js-1gqwai"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>transition-property</code> 告知浏览器过渡 <code>img</code> 元素的宽度。</li>
<li>我们使用 <code>transition-duration</code> 属性定义过渡从开始到结束 3 秒（<code>3s</code>）的持续时间。</li>
<li><code>transition-timing-function</code> 属性告诉浏览器将这个元素从初始宽度过渡到新尺寸时，采用恒定的过渡速度。</li>
</ol>
<h4 id="2">如何使用 2 秒的延迟完成元素宽度的过渡</h4>
<pre><code class="language-css">img {
  width: 40%;
  transition-property: width;
  transition-duration: 3s;
  transition-timing-function: linear;
  transition-delay: 2s;
}

img:hover {
  width: 100%;
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-transitions/js-ejjufi"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>transition-property</code> 告知浏览器过渡 <code>img</code> 元素的宽度。</li>
<li>我们使用 <code>transition-duration</code> 属性定义过渡从开始到结束 3 秒（<code>3s</code>）的持续时间。</li>
<li><code>transition-timing-function</code> 使用一个恒定的速度完成 <code>img</code> 元素宽度的过渡。</li>
<li>我们使用 <code>transition-delay</code> 属性在过渡的开始添加 2 秒（<code>2s</code>）的延迟。</li>
</ol>
<p>现在，我们了解了 CSS 过渡的属性，我们可以讨论一下它的缩写语法。</p>
<h2 id="shorthand-for-defining-the-css-transition-properties">CSS 过渡属性的缩写</h2>
<p>我们使用 <code>transition</code> 属性作为 <code>transition-property</code>、<code>transition-duration</code>、<code>transition-timing-function</code> 和 <code>transition-delay</code> 属性的缩写。</p>
<p>换句话说，我们不再这样写：</p>
<pre><code class="language-css">img {
  transition-property: width;
  transition-duration: 3s;
  transition-timing-function: linear;
  transition-delay: 2s;
}
</code></pre>
<p>你可以使用 <code>transition</code> 属性替换让你的代码更短，像这样：</p>
<pre><code class="language-css">img {
  transition: width 3s linear 2s;
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-transitions/js-vtvbpo"><strong>试着编辑它</strong></a></p>
<p>这是 <code>transition</code> 属性的语法：</p>
<pre><code class="language-css">transition: &lt;property-name&gt; &lt;duration&gt; &lt;timing-function&gt; &lt;delay&gt;;
</code></pre>
<p>注意你可以使用 <code>transition</code> 去定义多个 CSS 过渡属性的状态。</p>
<p><strong>看这个例子：</strong></p>
<pre><code class="language-css">img {
  width: 40%;
  opacity: 0.4;
  transition: width 3s linear, opacity 4s ease-out, transform 5s;
}

img:hover {
  width: 100%;
  opacity: 1;
  transform: rotate(45deg);
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-transitions/js-ajygzm"><strong>试着编辑它</strong></a></p>
<p>上面的片段使用逗号（<code>,</code>）去分隔我们应用在 <code>img</code> 元素上的每个过渡属性。</p>
<p>那么，现在你了解了 CSS 过渡是什么、它如何工作，我们来探讨一些 CSS 动画。</p>
<h2 id="what-is-css-animation">什么是 CSS 动画？</h2>
<p><strong>CSS 动画</strong> 提供了一个平滑、渐进的方式从一个关键帧到另一个关键帧为元素的样式增加动画。</p>
<h3 id="components-of-css-animations">CSS 动画的组成</h3>
<p>CSS 动画由两部分组成：</p>
<ol>
<li>关键帧</li>
<li>动画属性</li>
</ol>
<h3 id="what-are-css-keyframes">什么是 CSS <code>@keyframes</code>？</h3>
<p><strong>@keyframes</strong> 定义了你想要浏览器某关键时刻（帧）平滑地应用在元素上的样式。</p>
<h3 id="csskeyframes">CSS <code>@keyframes</code> 的语法</h3>
<p>CSS <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule">at 规则</a>中 @keyframes  由以下组成：</p>
<ol>
<li>一个 <code>@keyframes</code> 关键字</li>
<li>一个 <code>@keyframes</code> 名字</li>
<li>零个或多个关键帧块</li>
<li>关键帧选择器</li>
<li>关键帧样式声明</li>
</ol>
<p><strong>看这个插图：</strong></p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/09/css-animation-keyframes-illustration-codesweetly.png" alt="剖析 CSS @keyframes" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>剖析 CSS @keyframes</figcaption>
</figure>
<p>一个 CSS <code>@keyframes</code> 由一个关键字、一个名字、一个关键帧块组成。</p>
<h3 id="csskeyframes">CSS @keyframes 的示例</h3>
<p>下面是 CSS @keyframes 的示例</p>
<h4 id="changecolor">如何定义 <code>change-color</code> 的关键帧</h4>
<pre><code class="language-css">@keyframes change-color {
  /* 最初的关键帧 */
  0% {background-color: purple;}
  /* 最后的关键帧 */
  100% {background-color: yellow;}
}
</code></pre>
<p>在上面片段中我们做了什么：</p>
<ol>
<li>我们创建了一个 <code>@keyframes</code> 命名为 <code>change-color</code>。</li>
<li>我们定义了一个关键帧，供浏览器在关联元素的动画处于 <code>0%</code> 持续时间时应用。</li>
<li>我们定义了一个关键帧，供浏览器在关联元素的动画处于 <code>100%</code> 持续时间时应用。</li>
</ol>
<p><strong>注意：</strong></p>
<ul>
<li>你可以把你的 <code>@keyframes</code> 按照你希望的随意命名。</li>
<li><code>0%</code> 等价于关键字 <code>from</code>，<code>100%</code> 等价于关键字 <code>to</code>。也就是说，上面的代码片段等价于下面的：</li>
</ul>
<pre><code class="language-css">@keyframes change-color {
  /* 最初的关键帧 */
  from {background-color: purple;} 
  /* 最后的关键帧 */
  to {background-color: yellow;} 
}
</code></pre>
<ul>
<li>一个动画的开始和结束状态（<code>from</code> 和 <code>to</code>）是可选的。</li>
<li>假设你省略了 <code>@keyframes</code> 的开始或结束状态，这种情况下，浏览器将使用元素的现有样式作为起始/结束状态。</li>
<li><code>!important</code> 在关键帧中无效，浏览器会忽略任何带有 <code>!important</code> 的关键帧声明。</li>
</ul>
<p>让我们看另一个例子。</p>
<h4 id="shapeimage">如何定义 <code>shape-image</code> 的关键帧</h4>
<pre><code class="language-css">@keyframes shape-image {
  0% { width: 40%; border: 5px solid blue;}
  40% { width: 70%; border: 1px solid red; border-radius: 50%;}
  75% { width: 50%; border: 30px solid green;}
  100% { width: 100%; border: 7px solid purple;}
}
</code></pre>
<p>在上面片段中我们做了什么：</p>
<ol>
<li>我们创建了 <code>@keyframes</code> <a href="https://codesweetly.com/css-ruleset">规则集</a> 命名为 <code>shape-image</code>。</li>
<li>我们定义了 4 个关键帧，供浏览器在关联元素的动画处于指定关键时刻时应用。</li>
</ol>
<p><strong>提示：</strong> 在 JavaScript 中使用 <a href="https://developer.mozilla.org/en-US/docs/Web/API/CSSKeyframesRule">CSSKeyframesRule</a> 接口访问 CSS <code>@keyframes</code>。</p>
<p>那么，现在我们知道了 CSS @keyframes 规则集，我们可以讨论另一个 CSS 动画的组成部分了—— <em>animation 属性</em>。</p>
<h2 id="what-are-css-animation-properties">什么是 CSS animation 属性？</h2>
<p><strong>CSS animation 属性</strong> 告知浏览器你希望应用在指定元素上的动画。</p>
<p>换句话说，CSS <code>animation</code> 描述了动画的属性，例如它的名字、持续时间、方向、迭代次数。</p>
<p>下面是 CSS 动画的 9 种属性：</p>
<ul>
<li><code>animation-name</code></li>
<li><code>animation-duration</code></li>
<li><code>animation-timing-function</code></li>
<li><code>animation-delay</code></li>
<li><code>animation-iteration-count</code></li>
<li><code>animation-direction</code></li>
<li><code>animation-play-state</code></li>
<li><code>animation-fill-mode</code></li>
<li><code>animation</code></li>
</ul>
<p>让我们逐个讨论一下。</p>
<h3 id="what-is-the-css-animation-name-property">什么是 CSS <code>animation-name</code> 属性？</h3>
<p>CSS <code>animation-name</code> 属性定义了你希望应用在指定元素上的 <code>@keyframes</code> 包含的样式的名字。</p>
<p><strong>看这个例子：</strong></p>
<pre><code class="language-css">div {
  width: 150px;
  height: 150px;
  animation-name: change-color;
}

@keyframes change-color {
  from {background-color: purple;}
  to {background-color: yellow;}
}
</code></pre>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 指定了我们希望应用到 <code>div</code> 元素上的 <code>@keyframes</code>。</li>
<li>我们创建了一个名为 <code>change-color</code> 的 <code>@keyframes</code> 规则集。</li>
<li>我们定义了 <code>div</code> 元素的动画在 <code>0%</code> 和 <code>100%</code> 时两个关键帧给浏览器使用。</li>
</ol>
<p><strong>提示：</strong> 你可以使用关键字 <code>none</code> 不激活动画。</p>
<h3 id="what-is-the-css-animation-duration-property">什么是 CSS <code>animation-duration</code> 属性？</h3>
<p>CSS <code>animation-duration</code> 属性定义了完成一个动画循环的事件。</p>
<p><strong>注意以下几点：</strong></p>
<ul>
<li><code>animation-duration</code> 属性只接受毫秒（ms）或秒（s）的时间形式。</li>
<li><code>animation-duration</code> 值只接受 0 或是整数。否则，浏览器将忽略这条声明。</li>
<li>0 秒（<code>0s</code>）是 <code>animation-duration</code> 的默认值。</li>
<li>假设 <code>0s</code> 是 <code>animation-duration</code> 的值。在这种情况下，浏览器仍将通过触发 <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/animationstart_event"><code>animationStart</code></a> 和 <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/animationend_event"><code>animationEnd</code></a> 事件来执行动画。但是由 <a href="https://codesweetly.com/css-animations-explained#what-is-an-animation-fill-mode-property-in-css"><code>animation-fill-mode</code></a> 值决定动画是否可见。比如，你设置 <code>animation-fill-mode</code> 为 <code>none</code>，动画则是不可见的。</li>
</ul>
<p>让我们看一些 <code>animation-duration</code> 属性的例子。</p>
<h4 id="3">如何在 3 秒内完成更改元素的颜色的动画</h4>
<pre><code class="language-css">div {
  width: 150px;
  height: 150px;
  animation-name: change-color;
  animation-duration: 3s;
}

@keyframes change-color {
  from {background-color: purple;}
  to {background-color: yellow;}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-h6mb4k"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 属性指定了我们希望用在 <code>div</code> 元素上的 <code>@keyframes</code>。</li>
<li><code>animation-duration</code> 属性设置了动画一个循环的运行时间在 3 秒（<code>3s</code>）内。</li>
<li>我们创建了 <code>change-color</code> 关键帧<a href="https://codesweetly.com/css-ruleset">规则集</a>。</li>
<li>我们定义了 <code>div</code> 元素的动画在 <code>0%</code> 和 <code>100%</code> 时两个关键帧给浏览器使用。</li>
</ol>
<p>因此，浏览器将创建一个从第一帧到最后持续 3 秒的平滑的 <code>change-color</code> 动画。</p>
<h4 id="7">如何在 7 秒内完成更改图片边框和宽度的动画</h4>
<pre><code class="language-css">img {
  animation-name: shape-image;
  animation-duration: 7s;
}

@keyframes shape-image {
  0% { width: 40%; border: 5px solid blue;}
  40% { width: 70%; border: 1px solid red; border-radius: 50%;}
  75% { width: 50%; border: 30px solid green;}
  100% { width: 100%; border: 7px solid purple;}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-prumgo"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 属性指定了我们希望应用到 <code>img</code> 元素的 <code>@keyframes</code>。</li>
<li><code>animation-duration</code> 属性设置动画一个循环的运行时长为 7 秒（<code>7s</code>）。</li>
<li>我们创建了 <code>shape-image</code> 的关键帧规则集。</li>
<li>我们定义了四个关键帧，以便浏览器在图像动画达到指定关键时刻时应用。</li>
</ol>
<p>因此，浏览器将从 <code>shape-image</code> 的第一帧到最后一帧创建一个流畅的 7 秒的动画。</p>
<h3 id="what-is-the-css-animation-timing-function-property">什么是 CSS <code>animation-timing-function</code> 属性？</h3>
<p>CSS 的 <code>animation-timing-function</code> 属性定义了动画在整个持续时间内的实现时机（速度）。</p>
<p>换句话说，<code>animation-timing-function</code> 属性指定了动画在其持续时间内各个间隔的实现速度。</p>
<p><code>animation-timing-function</code> 属性接受的值如下：</p>
<ul>
<li><code>ease</code>：慢慢地开始动画，然后加速，并慢慢地结束动画。<code>ease</code> 是 <code>animation-timing-function</code> 属性的默认值。它等价于 <code>cubic-bezier(0.25, 0.1, 0.25, 1)</code>。</li>
<li><code>ease-in</code>：用一个递增的速度慢慢地开始动画。它等价于 <code>cubic-bezier(0.42, 0, 1, 1)</code>。</li>
<li><code>ease-out</code>：动画开始很快，然后慢慢的结束动画。它等价于 <code>cubic-bezier(0, 0, 0.58, 1)</code>。</li>
<li><code>ease-in-out</code>：慢慢地开始并慢慢地结束动画。它等价于 <code>cubic-bezier(0.42, 0, 0.58, 1)</code>。</li>
<li><code>linear</code>：在整个动画持续时间内使用一致的速度开始、结束动画。它等价于 <code>cubic-bezier(0, 0, 1, 1)</code>。</li>
<li><code>cubic-bezier(p1, p2, p3, p4)</code>：允许你定义<a href="https://www.cssportal.com/css-cubic-bezier-generator/">三次贝塞尔曲线</a>的值。</li>
</ul>
<p>然我们看一些 <code>animation-timing-function</code> 属性的例子。</p>
<h4 id="">如何使用线性速度完成更改元素宽度的动画</h4>
<pre><code class="language-css">div {
  width: 150px;
  height: 150px;
  background-color: purple;
  animation-name: change-width;
  animation-duration: 7s;
  animation-timing-function: linear;
}

@keyframes change-width {
  from {width: 50px;}
  to {width: 100%;}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-tzwrdc"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 属性制定了我们希望应用到 <code>div</code> 元素的 <code>@keyframes</code>。</li>
<li><code>animation-duration</code> 属性设置动画一个循环的运行时长为 7 秒（<code>7s</code>）。</li>
<li><code>linear</code> 函数应用一个一致的速度到 <code>div</code> 元素的动画上。</li>
<li>我们创建一个名为 <code>change-width</code> 关键帧规则集。</li>
<li>我们为浏览器定义了两个关键帧，分别在 <code>div</code> 动画的持续时间为百分之零（<code>0%</code>）和百分之一百（<code>100%</code>）时应用。</li>
</ol>
<p>因此，浏览器将从 <code>change-width</code> 的第一帧到最后一帧创建一个流畅的 7 秒的动画。</p>
<p>让我们看另一个例子。</p>
<h4 id="easeinout">如何使用 ease-in-out 和 线性速度完成更改元素宽度的动画</h4>
<pre><code class="language-css">div {
  width: 150px;
  height: 150px;
  color: white;
  animation-name: change-width;
  animation-duration: 7s;
}

.first-div {
  background-color: purple;
  animation-timing-function: ease-in-out;
}

.second-div {
  background-color: orange;
  animation-timing-function: linear;
}

@keyframes change-width {
  from {width: 50px;}
  to {width: 100%;}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-janmqa"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 属性制定了我们希望应用到 <code>div</code> 元素的 <code>@keyframes</code>。</li>
<li><code>animation-duration</code> 属性设置动画一个循环的运行时长为 7 秒（<code>7s</code>）。</li>
<li>我们使用 <code>ease-in-out</code> 函数在 <code>first-div</code> 上应用一个慢慢开始并慢慢结束的动画。</li>
<li><code>linear</code> 函数在 <code>second-div</code> 上用一个一致的速度的动画。</li>
<li>我们创建了名为 <code>change-width</code> 的关键帧规则集。</li>
<li>我们为浏览器定义了两个关键帧，分别在 <code>div</code> 动画的持续时间为百分之零（<code>0%</code>）和百分之一百（<code>100%</code>）时应用。</li>
</ol>
<p>因此，浏览器将从 <code>change-width</code> 的第一帧到最后一帧创建一个流畅的 7 秒的动画。</p>
<h3 id="what-is-the-css-animation-delay-property">什么是 CSS <code>animation-delay</code> 属性？</h3>
<p>CSS 的 <code>animation-delay</code> 属性定义了浏览器在开始动画之前需要等待多长时间。</p>
<p>换句话说，使用 <code>animation-delay</code> 去指定动画是应该立即开始、从指定时间开始、还是稍后（一段延迟之后）开始。</p>
<p><strong>注意以下几点：</strong></p>
<ul>
<li><code>animation-delay</code> 属性必须是毫秒（<code>ms</code>）或是秒（<code>s</code>）作为单位。</li>
<li><code>0s</code> 是 <code>animation-delay</code> 的默认值，这会使动画在浏览器将其应用到 HTML 元素时立即开始。</li>
<li>一个负值会使动画从指定时间立即开始。举例来说，假设一个元素的 <code>animation-delay</code> 值设为 <code>-3s</code>，动画将在 3 秒时立即开始。</li>
<li>一个正值会使动画等待指定时间过后开始。举例来说，假设一个元素的 <code>animation-delay</code> 值设为 <code>3s</code>，这次动画将延迟 3 秒后开始。</li>
</ul>
<p>让我们看一些 <code>animation-delay</code> 属性的例子。</p>
<h4 id="4">如何 4 秒延迟后完成更改元素宽度的动画</h4>
<pre><code class="language-css">div {
  width: 150px;
  height: 150px;
  color: white;
  animation-name: change-width;
  animation-duration: 7s;
}

.first-div {
  background-color: purple;
  animation-timing-function: ease-in-out;
}

.second-div {
  background-color: orange;
  animation-timing-function: linear;
  animation-delay: 4s;
}

@keyframes change-width {
  from {width: 50px;}
  to {width: 100%;}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-iidpmk"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 属性制定了我们希望应用到 <code>div</code> 元素的 <code>@keyframes</code>。</li>
<li><code>animation-duration</code> 属性设置动画一个循环的运行时长为 7 秒（<code>7s</code>）。</li>
<li>我们使用 <code>ease-in-out</code> 函数在 <code>first-div</code> 上应用一个慢慢开始并慢慢结束的动画。</li>
<li><code>linear</code> 函数在 <code>second-div</code> 上用一个一致的速度的动画。</li>
<li><code>animation-delay</code> 属性在开始 <code>second-div</code> 的动画时应用了一个 4 秒（<code>4s</code>）的延迟。</li>
<li>我们创建了名为 <code>change-width</code> 的关键帧规则集。</li>
<li>我们为浏览器定义了两个关键帧，分别在 <code>div</code> 动画的持续时间为百分之零（<code>0%</code>）和百分之一百（<code>100%</code>）时应用。</li>
</ol>
<p>因此，浏览器会延迟 <code>second-div</code> 的动画四秒，而立即开始 <code>first-div</code> 的动画。</p>
<p>下面是 <code>animation-delay</code> 属性的另一个例子。</p>
<h4 id="">如何从动画序列的第四秒开始完成更改元素宽度的动画</h4>
<pre><code class="language-css">div {
  width: 150px;
  height: 150px;
  color: white;
  animation-name: change-width;
  animation-duration: 7s;
}

.first-div {
  background-color: purple;
  animation-timing-function: ease-in-out;
}

.second-div {
  background-color: orange;
  animation-timing-function: linear;
  animation-delay: -4s;
}

@keyframes change-width {
  from {width: 50px;}
  to {width: 100%;}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-6xga4t"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 属性制定了我们希望应用到 <code>div</code> 元素的 <code>@keyframes</code>。</li>
<li><code>animation-duration</code> 属性设置动画一个循环的运行时长为 7 秒（<code>7s</code>）。</li>
<li>我们使用 <code>ease-in-out</code> 函数在 <code>first-div</code> 上应用一个慢慢开始并慢慢结束的动画。</li>
<li><code>linear</code> 函数在 <code>second-div</code> 上用一个一致的速度的动画。</li>
<li><code>animation-delay</code> 属性使 <code>second-div</code> 的动画从动画序列的第四秒开始。</li>
<li>我们创建了名为 <code>change-width</code> 的关键帧规则集。</li>
<li>我们为浏览器定义了两个关键帧，分别在 <code>div</code> 动画的持续时间为百分之零（<code>0%</code>）和百分之一百（<code>100%</code>）时应用。</li>
</ol>
<p>因此，浏览器会在第四秒立即开始 <code>second-div</code> 的动画。</p>
<h3 id="what-is-the-css-animation-iteration-count-property">什么是 CSS <code>animation-iteration-count</code> 属性？</h3>
<p>CSS 的 <code>animation-iteration-count</code> 属性定义了浏览器应重复动画的次数。</p>
<p><strong>注意以下几点：</strong></p>
<ul>
<li><code>1</code> 是 <code>animation-iteration-count</code> 的默认值。</li>
<li><code>animation-iteration-count</code> 属性接受非整数值，<code>0.5</code> 告诉浏览器播放单个动画循环的一半。</li>
<li><code>animation-iteration-count</code> <em>不接受</em>赋值。</li>
<li><code>infinite</code> 意味着浏览器将一直重复这个动画。</li>
</ul>
<p>下面是一些例子。</p>
<h4 id="">如何完成两次更改元素宽度的动画</h4>
<pre><code class="language-css">div {
  width: 70px;
  height: 50px;
  background-color: purple;
  animation-name: change-width;
  animation-duration: 5s;
  animation-timing-function: ease-in-out;
  animation-iteration-count: 2;
}

@keyframes change-width {
  from {width: 70px;}
  to {width: 100%;}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-vbcswe"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 属性制定了我们希望应用到 <code>div</code> 元素的 <code>@keyframes</code>。</li>
<li><code>animation-duration</code> 属性设置动画一个循环的运行时长为 5 秒（<code>5s</code>）。</li>
<li>我们使用 <code>ease-in-out</code> 函数在 <code>div</code> 上应用一个慢慢开始并慢慢结束的动画。</li>
<li><code>animation-iteration-count</code> 属性告诉浏览器运行这个动画两次。</li>
<li>我们创建了名为 <code>change-width</code> 的关键帧规则集。</li>
<li>我们为浏览器定义了两个关键帧，分别在 <code>div</code> 动画的持续时间为百分之零（<code>0%</code>）和百分之一百（<code>100%</code>）时应用。</li>
</ol>
<p>因此，浏览器将运行 <code>div</code> 的动画两次。</p>
<p>下面是另一个 <code>animation-iteration-count</code> 属性的例子。</p>
<h4 id="">如何无限重复更改元素宽度的动画</h4>
<pre><code class="language-css">div {
  width: 70px;
  height: 50px;
  animation-name: change-width;
  animation-duration: 5s;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}

@keyframes change-width {
  from {width: 70px; background-color: purple;}
  to {width: 100%; background-color: orange;}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-p1zwk5"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 属性制定了我们希望应用到 <code>div</code> 元素的 <code>@keyframes</code>。</li>
<li><code>animation-duration</code> 属性设置动画一个循环的运行时长为 5 秒（<code>5s</code>）。</li>
<li>我们使用 <code>ease-in-out</code> 函数在 <code>div</code> 上应用一个慢慢开始并慢慢结束的动画。</li>
<li><code>animation-iteration-count</code> 属性告诉浏览器无限次地运行这个动画。</li>
<li>我们创建了名为 <code>change-width</code> 的关键帧规则集。</li>
<li>我们为浏览器定义了两个关键帧，分别在 <code>div</code> 动画的持续时间为百分之零（<code>0%</code>）和百分之一百（<code>100%</code>）时应用。</li>
</ol>
<p>因此，浏览器将无限次地运行 <code>div</code> 的动画。</p>
<h3 id="cssanimationdirection">什么是 CSS <code>animation-direction</code> 属性？</h3>
<p><code>animation-direction</code> 属性指定动画的第一次迭代是正向还是反向。它还定义浏览器是否应该改变后续迭代的方向。</p>
<p><code>animation-direction</code> 可接受的值：</p>
<ul>
<li><code>normal</code>：正常的方向播放动画（也就是正向），<code>normal</code> 是 <code>animation-direction</code> 的默认值。</li>
<li><code>reverse</code>：相反的方向播放动画（反向）。</li>
<li><code>alternate</code>：第一次动画循环正向播放，后续迭代在方向与正向之间交替。</li>
<li><code>alternate-reverse</code>：第一次动画循环反向播放，后续迭代在正向与反向之间交替。</li>
</ul>
<p>下面是一些例子。</p>
<h4 id="">如何每次都以反向开始完成改变元素宽度的动画</h4>
<pre><code class="language-css">div {
  width: 70px;
  height: 50px;
  animation-name: change-width;
  animation-duration: 5s;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: reverse;
}

@keyframes change-width {
  from {width: 70px; background-color: purple;}
  to {width: 100%; background-color: orange;}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-d2n3zt"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 属性制定了我们希望应用到 <code>div</code> 元素的 <code>@keyframes</code>。</li>
<li><code>animation-duration</code> 属性设置动画一个循环的运行时长为 5 秒（<code>5s</code>）。</li>
<li>我们使用 <code>ease-in-out</code> 函数在 <code>div</code> 上应用一个慢慢开始并慢慢结束的动画。</li>
<li><code>animation-iteration-count</code> 属性告诉浏览器无限次地运行这个动画。</li>
<li><code>animation-direction</code> 属性让每次动画反向开始。</li>
<li>我们创建了名为 <code>change-width</code> 的关键帧规则集。</li>
<li>我们为浏览器定义了两个关键帧，分别在 <code>div</code> 动画的持续时间为百分之零（<code>0%</code>）和百分之一百（<code>100%</code>）时应用。</li>
</ol>
<p>下面是 <code>animation-direction</code> 属性的另一个例子。</p>
<h4 id="">如何交替动画方向地完成更改元素宽度的动画</h4>
<pre><code class="language-css">div {
  width: 70px;
  height: 50px;
  animation-name: change-width;
  animation-duration: 5s;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

@keyframes change-width {
  from {width: 70px; background-color: purple;}
  to {width: 100%; background-color: orange;}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-ld9kms"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 属性制定了我们希望应用到 <code>div</code> 元素的 <code>@keyframes</code>。</li>
<li><code>animation-duration</code> 属性设置动画一个循环的运行时长为 5 秒（<code>5s</code>）。</li>
<li>我们使用 <code>ease-in-out</code> 函数在 <code>div</code> 上应用一个慢慢开始并慢慢结束的动画。</li>
<li><code>animation-iteration-count</code> 属性告诉浏览器无限次地运行这个动画。</li>
<li><code>animation-direction</code> 属性让每次动画都交替方向。</li>
<li>我们创建了名为 <code>change-width</code> 的关键帧规则集。</li>
<li>我们为浏览器定义了两个关键帧，分别在 <code>div</code> 动画的持续时间为百分之零（<code>0%</code>）和百分之一百（<code>100%</code>）时应用。</li>
</ol>
<h3 id="cssanimationplaystate">什么是 CSS <code>animation-play-state</code> 属性？</h3>
<p>CSS 的 <code>animation-play-state</code> 属性定义了浏览器是在运行还是暂停特定动画。</p>
<p><code>animation-play-state</code> 属性接受的值：</p>
<ul>
<li><code>running</code>：指定浏览器运行动画。<code>running</code> 是 <code>animation-play-state</code> 的默认值。</li>
<li><code>paused</code>：指定浏览器暂停动画。</li>
</ul>
<p><strong>看这个例子：</strong></p>
<pre><code class="language-css">div {
  width: 70px;
  height: 50px;
  animation-name: change-width;
  animation-duration: 5s;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

div:hover {
  animation-play-state: paused;
}

@keyframes change-width {
  from {width: 70px; background-color: purple;}
  to {width: 100%; background-color: orange;}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-kbommn"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 属性制定了我们希望应用到 <code>div</code> 元素的 <code>@keyframes</code>。</li>
<li><code>animation-duration</code> 属性设置动画一个循环的运行时长为 5 秒（<code>5s</code>）。</li>
<li>我们使用 <code>ease-in-out</code> 函数在 <code>div</code> 上应用一个慢慢开始并慢慢结束的动画。</li>
<li><code>animation-iteration-count</code> 属性告诉浏览器无限次地运行这个动画。</li>
<li><code>animation-direction</code> 属性让每次动画都交替方向。</li>
<li>我们在 <code>div</code> 的 hover <a href="https://codesweetly.com/css-pseudo-selectors">伪类</a>上使用了 <code>animation-play-state</code>，以便在用户将鼠标移到 <code>div</code> 元素上时暂停动画。</li>
<li>我们创建了名为 <code>change-width</code> 的关键帧规则集。</li>
<li>我们为浏览器定义了两个关键帧，分别在 <code>div</code> 动画的持续时间为百分之零（<code>0%</code>）和百分之一百（<code>100%</code>）时应用。</li>
</ol>
<h3 id="cssanimationfillmode">什么是 CSS <code>animation-fill-mode</code> 属性？</h3>
<p>CSS 的 <code>animation-fill-mode</code> 属性定义了浏览器在动画运行之前（或之后）应该应用于元素的样式。</p>
<p><code>animation-fill-mode</code> 可接受的值：</p>
<ul>
<li><code>none</code>：浏览器将<em>不会</em>在动画运行之后或之前应用样式。<code>none</code> 是 <code>animation-fill-mode</code> 的默认值。</li>
<li><code>forwards</code>：当动画结束，元素将保留最后动画帧的样式声明。（注意：<code>animation-direction</code> 和 <code>animation-iteration-count</code> 属性将决定最后一帧。）</li>
<li><code>backwards</code>：在 <code>animation-delay</code> 期间，动画将保留最初动画帧的样式声明。（注意：<code>animation-direction</code> 属性决定第一帧。）</li>
<li><code>both</code>：浏览器将同时应用 <code>forwards</code> 和 <code>backwards</code> 规则。因此，动画属性将在两个方向上扩展。</li>
</ul>
<p>下面是一些例子：</p>
<h4 id="">如何在动画结束为元素增加样式</h4>
<pre><code class="language-css">div {
  width: 70px;
  height: 50px;
  background-color: green;
  animation-name: change-width;
  animation-duration: 5s;
  animation-timing-function: ease-in-out;
  animation-fill-mode: forwards;
}

@keyframes change-width {
  from {width: 70px; background-color: purple;}
  to {width: 100%; background-color: orange;}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-lkc7mw"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 属性制定了我们希望应用到 <code>div</code> 元素的 <code>@keyframes</code>。</li>
<li><code>animation-duration</code> 属性设置动画一个循环的运行时长为 5 秒（<code>5s</code>）。</li>
<li>我们使用 <code>ease-in-out</code> 函数在 <code>div</code> 上应用一个慢慢开始并慢慢结束的动画。</li>
<li><code>animation-fill-mode</code> 将告诉浏览器当动画结束时保留最后的关键帧样式声明。</li>
<li>我们创建了名为 <code>change-width</code> 的关键帧规则集。</li>
<li>我们为浏览器定义了两个关键帧，分别在 <code>div</code> 动画的持续时间为百分之零（<code>0%</code>）和百分之一百（<code>100%</code>）时应用。</li>
</ol>
<p>下面是 <code>animation-fill-mode</code> 属性的另一个例子。</p>
<h4 id="">如何在动画延迟这段时间增加样式</h4>
<pre><code class="language-css">div {
  width: 70px;
  height: 50px;
  background-color: green;
  animation-name: change-width;
  animation-duration: 5s;
  animation-timing-function: ease-in-out;
  animation-delay: 3s;
  animation-fill-mode: backwards;
}

@keyframes change-width {
  from {width: 70px; background-color: purple;}
  to {width: 100%; background-color: orange;}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-nfmq3r"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 属性制定了我们希望应用到 <code>div</code> 元素的 <code>@keyframes</code>。</li>
<li><code>animation-duration</code> 属性设置动画一个循环的运行时长为 5 秒（<code>5s</code>）。</li>
<li>我们使用 <code>ease-in-out</code> 函数在 <code>div</code> 上应用一个慢慢开始并慢慢结束的动画。</li>
<li><code>animation-fill-mode</code> 将告诉浏览器在 <code>animation-delay</code> 期间保留最开始的关键帧样式声明。</li>
<li>我们创建了名为 <code>change-width</code> 的关键帧规则集。</li>
<li>我们为浏览器定义了两个关键帧，分别在 <code>div</code> 动画的持续时间为百分之零（<code>0%</code>）和百分之一百（<code>100%</code>）时应用。</li>
</ol>
<p>让我们看 <code>animation-fill-mode</code> 属性的第三个例子。</p>
<h4 id="">如何在动画延迟时和完成动画后增加样式</h4>
<pre><code class="language-css">div {
  width: 70px;
  height: 50px;
  background-color: green;
  animation-name: change-width;
  animation-duration: 5s;
  animation-timing-function: ease-in-out;
  animation-delay: 3s;
  animation-fill-mode: both;
}

@keyframes change-width {
  from {width: 70px; background-color: purple;}
  to {width: 100%; background-color: orange;}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-gbypmt"><strong>试着编辑它</strong></a></p>
<p>在上面片段中我们做了什么：</p>
<ol>
<li><code>animation-name</code> 属性制定了我们希望应用到 <code>div</code> 元素的 <code>@keyframes</code>。</li>
<li><code>animation-duration</code> 属性设置动画一个循环的运行时长为 5 秒（<code>5s</code>）。</li>
<li>我们使用 <code>ease-in-out</code> 函数在 <code>div</code> 上应用一个慢慢开始并慢慢结束的动画。</li>
<li><code>animation-fill-mode</code> 将告诉浏览器同时应用 <code>forwards</code> 和 <code>backwards</code> 两条规则。</li>
<li>我们创建了名为 <code>change-width</code> 的关键帧规则集。</li>
<li>我们为浏览器定义了两个关键帧，分别在 <code>div</code> 动画的持续时间为百分之零（<code>0%</code>）和百分之一百（<code>100%</code>）时应用。</li>
</ol>
<h2 id="cssanimation">什么是 CSS <code>animation</code> 属性？</h2>
<p>我们使用 <code>animation</code> 属性作为它们的缩写：</p>
<ul>
<li><code>animation-name</code></li>
<li><code>animation-duration</code></li>
<li><code>animation-timing-function</code></li>
<li><code>animation-delay</code></li>
<li><code>animation-iteration-count</code></li>
<li><code>animation-direction</code></li>
<li><code>animation-play-state</code></li>
<li><code>animation-fill-mode</code></li>
</ul>
<p>换句话说，不需要写这些：</p>
<pre><code class="language-css">div {
  animation-name: change-width;
  animation-duration: 5s;
  animation-timing-function: ease-in-out;
  animation-delay: 2s;
  animation-iteration-count: 3;
  animation-direction: alternate;
  animation-play-state: running;
  animation-fill-mode: both;
}
</code></pre>
<p>另外，你也可以使用 <code>animation</code> 属性缩短你的代码，像是这样：</p>
<pre><code class="language-css">div {
  animation: 5s ease-in-out 2s 3 alternate both running change-width;
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-37ccew"><strong>试着编辑它</strong></a></p>
<p>这是 <code>animation</code> 属性的语法：</p>
<pre><code class="language-css">animation: animation-duration animation-timing-function animation-delay animation-iteration-count animation-direction animation-fill-mode animation-play-state animation-name;
</code></pre>
<p><strong>注意：</strong></p>
<ul>
<li>时间值的排列方式很重要。浏览器将第一个时间值视为 <code>animation-duration</code>，而将第二个时间值分配给 <code>animation-delay</code>。</li>
<li>最好将 <code>animation-name</code> 放在最后，不然，浏览器可能赋值 <code>animation-name</code> 到其他属性。</li>
<li>你可以使用 <code>animation</code> 属性应用多个 <code>@keyframes</code> <a href="https://codesweetly.com/css-ruleset">规则集</a>到一个元素。看这个例子：</li>
</ul>
<pre><code class="language-css">div {
  width: 70px;
  height: 70px;
  background-color: green;
  animation: 
    5s ease-in-out 3s 3 alternate both change-width, 
    5s 3s infinite alternate both change-shape, 
    5s 3s infinite rotate-hue;
}

@keyframes change-width {
  from {width: 70px; background-color: purple;}
  to {width: 100%; background-color: orange;}
}

@keyframes change-shape {
  from {border-radius: 0%; border: 1px solid blue;}
  to {border-radius: 50%; border: 7px solid green;}
}

@keyframes rotate-hue {
  from {filter: hue-rotate(0deg);}
  to {filter: hue-rotate(360deg);}
}
</code></pre>
<p><a href="https://codesweetly.com/try-it-sdk/css/css-animations/js-4lyg4d"><strong>试着编辑它</strong></a></p>
<p>上方的片段使用逗号分隔了三个 <code>@keyframes</code> 并应用到 <code>div</code> 元素上。</p>
<p><strong>注意：</strong> 我们用了 <a href="https://www.quackit.com/css/functions/css_hue-rotate_function.cfm"><code>hue-rotate()</code></a> 函数去改变了色调。</p>
<h2 id="important-stuff-to-know-about-css-transitions-and-animations">CSS 过渡和动画的重要知识要点</h2>
<ol>
<li>你不能为所有的 CSS 属性增加动画。可以去看看 MDN 的<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties">可动画化的 CSS 属性</a>文章，以查看可以进行动画处理的属性列表。</li>
<li>CSS 过渡和动画对于大多数 CSS 属性来说是<a href="https://codesweetly.com/web-tech-terms-e#expensive-operation-computing">昂贵的操作</a>，除了 <code>opacity</code> 和 <code>transform</code>。</li>
<li>应该注意 CSS 过渡可能通过元素的堆叠顺序引起<a href="https://dzhavat.github.io/2021/02/18/debugging-layout-repaint-issues-triggered-by-css-transition.html">布局的重绘问题</a>，</li>
</ol>
<h2 id="wrapping-up">总结</h2>
<p>在这这篇文章中，我们讨论了 CSS 过渡和动画之间的不同，也用了一些例子来讨论应该如何使用它。</p>
<p>感谢阅读！</p>
<h3 id="reacttypescript">这还有些有用的 React Typescript 资源</h3>
<p>我写了一本有关<a href="https://amzn.to/3Pa4bI4">如何创建 NPM 包</a>的书！</p>
<p>这是一本对初学者友好的书，你可以像是专家一样从零开始去创建、测试、发布一个NPM包。</p>
<a href="https://amzn.to/3Pa4bI4">
  <figure class="kg-card kg-card-image kg-card-hascaption">
      <img src="https://www.freecodecamp.org/news/content/images/2023/09/creating-npm-package-banner-codesweetly.png" alt="可以在 Amazon 上找到这本如何创建 NPM 包" class="kg-image" width="600" height="400" loading="lazy">
      <figcaption>可以在 Amazon 上找到这本书</figcaption>
  </figure>
</a>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript DOM 操作手册 ]]>
                </title>
                <description>
                    <![CDATA[ DOM 操作是在学习 JavaScript 中最令人兴奋的主题之一。这是因为 JavaScript 的主要用途之一就是使网页具有交互性，而 DOM 在这里起到了重要作用。 DOM 是一个非常强大的工具，允许你与网页上的元素进行交互和操作。本手册将帮助你理解并自信地使用 DOM。 你将从 DOM 是什么以及 DOM 可以用来做什么开始学习，然后我们将深入学习如何选择、修改和为 DOM 元素设置样式，你也将学习如何创建一个新元素并添加到你的网页上。 手册也包含了像是如何遍历 DOM、DOM 事件是什么，以及一些项目实践的想法等主题。 让我们开始吧！ 目录  * DOM 是什么 * DOM 可以用来做什么          * 如何选择 DOM 元素 * getElementById     * ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/the-javascript-dom-manipulation-handbook/</link>
                <guid isPermaLink="false">66278b8ee2f5d704bae5420a</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mingxun Liu ]]>
                </dc:creator>
                <pubDate>Tue, 23 Apr 2024 10:19:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2024/04/The-JavaScript-DOM-Manipulation-Handbook-Cover-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/the-javascript-dom-manipulation-handbook/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">The JavaScript DOM Manipulation Handbook</a>
      </p><!--kg-card-begin: markdown--><p>DOM 操作是在学习 JavaScript 中最令人兴奋的主题之一。这是因为 JavaScript 的主要用途之一就是使网页具有交互性，而 DOM 在这里起到了重要作用。</p>
<p>DOM 是一个非常强大的工具，允许你与网页上的元素进行交互和操作。本手册将帮助你理解并自信地使用 DOM。</p>
<p>你将从 DOM 是什么以及 DOM 可以用来做什么开始学习，然后我们将深入学习如何选择、修改和为 DOM 元素设置样式，你也将学习如何创建一个新元素并添加到你的网页上。</p>
<p>手册也包含了像是如何遍历 DOM、DOM 事件是什么，以及一些项目实践的想法等主题。</p>
<p>让我们开始吧！</p>
<h2 id="">目录</h2>
<ul>
<li><a href="#what-is-dom">DOM 是什么</a>
<ul>
<li><a href="#use-of-dom">DOM 可以用来做什么</a></li>
</ul>
</li>
<li><a href="#how-to-select-dom">如何选择 DOM 元素</a>
<ul>
<li><a href="#getElementById">getElementById</a></li>
<li><a href="#getElementsByClassName">getElementsByClassName</a></li>
<li><a href="#getElementsByTagName">getElementsByTagName</a></li>
<li><a href="#querySelector">querySelector</a></li>
<li><a href="#querySelectorAll">querySelectorAll</a></li>
</ul>
</li>
<li><a href="#changee-dom-content">如何更改 DOM 元素的内容</a>
<ul>
<li><a href="#innerhtml"><code>innerHTML</code> 属性</a></li>
<li><a href="#innerhtml-risk">使用 <code>innerHTML</code> 的安全风险</a></li>
<li><a href="#innertext-and-textcontent"><code>innerText</code> 和 <code>textContent</code> 属性</a></li>
</ul>
</li>
<li><a href="#handle-dom">如何处理 DOM 元素的属性</a>
<ul>
<li><a href="#getAttribute"><code>getAttribute()</code> 方法</a></li>
<li><a href="#setAttribute"><code>setAttribute()</code> 方法</a></li>
<li><a href="#removeAttribute"><code>removeAttribute()</code> 方法</a></li>
<li><a href="#hasAttribute"><code>hasAttribute()</code> 方法</a></li>
</ul>
</li>
<li><a href="#change-dom-style">如何更改 DOM 的样式</a>
<ul>
<li><a href="#use-style">使用 <code>.style</code> 属性设置样式</a></li>
<li><a href="#use-class">使用 <code>class</code> 设置样式</a></li>
</ul>
</li>
<li><a href="#loop-through-dom">如何遍历 DOM</a>
<ul>
<li><a href="#node-and-element-difference">节点和元素之间的不同</a></li>
<li><a href="#parentNode-or-parentElement">使用 <code>parentNode</code> 还是 <code>parentElement</code> 选择父级</a></li>
<li><a href="#childNodes-or-children">使用 <code>childNodes</code> 还是 <code>children</code> 选择子级</a></li>
<li><a href="#choose-first-or-last-element-node">选择第一个或是最后一个元素/节点</a></li>
<li><a href="#choose-sibling-node">在 DOM 中选择兄弟节点</a></li>
</ul>
</li>
<li><a href="#dom-event-and-event-listener">DOM 事件和事件监听器</a>
<ul>
<li><a href="#event-listener-and-handler">事件监听器与事件处理函数</a></li>
<li><a href="#register-events">JavaScript 中三种注册事件的方法</a></li>
<li><a href="#practice">实践挑战</a></li>
<li><a href="#solution">实践挑战的解决方案</a></li>
<li><a href="#event-object">事件对象</a></li>
<li><a href="#event-type">事件类型</a></li>
</ul>
</li>
<li><a href="#event-flow">JavaScript 的事件流</a>
<ul>
<li><a href="#event-bubbling">事件冒泡</a></li>
<li><a href="#event-capturing">事件捕获</a></li>
<li><a href="#event-stopPropagation-method">Event 的 <code>stopPropagation()</code> 方法</a></li>
</ul>
</li>
<li><a href="#JS-DOM-manipulation-projects-ideas">JavaScript DOM 项目</a></li>
<li><a href="#conclusion">总结</a></li>
</ul>
<h2 id="what-is-dom">DOM 是什么</h2>
<p>DOM 全称是文档对象模型（Document Object Model），但是这是什么意思呢？我们分开来看看。</p>
<p><strong>文档</strong>指的是你在浏览器上所看到的网页。具体来说，HTML 文档处理页面内容的结构，包括组成页面的文本、图片、链接和其他的元素。</p>
<p><strong>对象</strong>指的是像 img, h1, p 这样的元素被视为对象。每一个对象都有属性（类似 id、 class、style）和方法，利用这些属性和方法你就可以操作这些元素。</p>
<p><strong>模型</strong>指的是它是 HTML 文档的一种表示或副本，以分层树的形式呈现。这颗树包括了所有元素，并且有它们之间的父子关系。</p>
<p>浏览器会确保 DOM 与 HTML 文档同步，使它们总是保持一致。因此，如果 HTML 中的某些内容发生变化，DOM 也会相应变化，反之亦然。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2024/01/JavaScript--2-.png" alt="HTML DOM 树" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>HTML DOM 树</figcaption>
</figure>
<p>在树的顶部是 Document 对象，它只有一个子元素 —— <code>html</code> 元素。<code>html</code> 元素也被称为根元素，它有 <code>head</code> 和 <code>body</code> 两个子元素，而每个子元素也都有它们自己的子元素。</p>
<p>元素之间的父子关系使你能够遍历、移动和选择它们。稍后会详细介绍。</p>
<h3 id="use-of-dom">DOM 可以用来做什么</h3>
<p>DOM 操作允许开发者与网页的结构、样式、内容交互。下面是一些你可以通过 DOM 用来做的事情：</p>
<ul>
<li>更改或移除 DOM 中存在的元素</li>
<li>创建并添加新元素到网页</li>
<li>更改一些元素的样式</li>
<li>给元素添加事件监听器让它们可交互</li>
</ul>
<h2 id="how-to-select-dom">如何选择 DOM 元素</h2>
<p>要对 DOM 元素执行操作，首先必须选择或访问相应的元素。在这一节，你将学到一些常见的方法去选择 DOM 元素。</p>
<p>让我们使用下面通讯录片段来展示各种 DOM 选择器方法如何工作。</p>
<pre><code class="language-HTML">  &lt;h1 id="page-title"&gt;Phonebook&lt;/h1&gt;
  
  &lt;p class="family"&gt;Marie&lt;/p&gt;
  &lt;p class="family"&gt;Jose&lt;/p&gt;
  &lt;p class="work"&gt;Anne&lt;/p&gt;
  &lt;p class="work"&gt;Joan&lt;/p&gt;
</code></pre>
<p>一个标题元素和四个段落元素的简单代码片段</p>
<p>这段代码包括了一个 id 为 <code>page-title</code> 的 <code>h1</code> 标题和四个 <code>p</code> 段落，前两个段落都具有 <code>family</code> 类，而后两个具有 <code>work</code> 类。</p>
<h3 id="getElementById">1. getElementById</h3>
<p>你可以使用这个方法去选择带有 id 属性的元素，id 是独一无二的标识符。例如，当一个 <code>h1</code> 元素有值为 <code>page-title</code> 的 id，页面上的其他元素不应该有相同值作为 id。</p>
<p>这意味着每当你使用 <code>getElementById()</code> 方法，你将只会从 DOM 中选择一个元素。</p>
<p>我们来看例子：</p>
<p>这个 <code>h1</code> 有值为 <code>page-title</code> 的 id，以下是使用 <code>getElementById()</code> 方法选择它的办法：</p>
<pre><code class="language-javascript">const titleElement = document.getElementById("page-title")
console.log(titleElement)
</code></pre>
<p>这里例子选择了 <code>h1</code> 元素并赋值给了变量 <code>titleElement</code>。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-02-at-9.01.01-AM.png" alt="使用 getElementById() 方法访问元素的结果" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 getElementById() 方法访问元素的结果</figcaption>
</figure>
<p>如果 DOM 中没有给定 id 的元素，则 <code>getElementById()</code> 将返回 <code>null</code>。</p>
<h3 id="getElementsByClassName">2. getElementsByClassName</h3>
<p>你可以使用这个方法选择超过一个对象。这个方法获取 class 属性的值作为参数在 DOM 中选择给定 class 的所有元素。与 id 不一样，你可以在不同的 HTML 元素上给一个相同的 class。</p>
<p>看这个例子：</p>
<pre><code class="language-javascript">const famContacts = document.getElementsByClassName("family")
console.log(famContacts)
</code></pre>
<p>这返回了给定 class 的所有元素的 HTML 集合，控制台上如下所示：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-01-at-10.35.51-AM.png" alt="getElementsByClassName() 方法返回一个 HTML 集合" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>getElementsByClassName() 方法返回一个 HTML 集合</figcaption>
</figure>
<p>注意：HTML 集合看起来像是数组，但实际上不是。你可以使用括号符号访问元素，就像访问数组一样，但是不能使用像是 <code>map</code>、<code>filter</code> 或是 <code>forEach</code> 等数组方法。</p>
<pre><code class="language-javascript">console.log(famContacts[0]) 
</code></pre>
<p>这样会获得 HTML 集合中的第一个元素，也就是名字为 Marie 的段落。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-02-at-9.03.35-AM-1.png" alt="使用索引访问 HTML 集合中的元素" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用索引访问 HTML 集合中的元素</figcaption>
</figure>
<p>但是如果你想通过循环遍历 <code>famContacts</code> HTML 集合中的所有元素呢？你首先需要将 HTML 集合转换成数组，然后你就可以任意使用数组方法了。</p>
<p>一个简单的办法从 HTML 集合创建数组就是使用展开语法，像是这样：</p>
<pre><code class="language-javascript">let famContactsArray = [...famContacts]

famContactsArray.forEach(element =&gt; console.log(element))
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-02-at-9.06.48-AM.png" alt="打印 HTML 集合中所有元素" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>打印 HTML 集合中所有元素</figcaption>
</figure>
<p>使用 <code>forEach</code> 方法，你可以访问 <code>famContactsArray</code> 中每一个子项。如果你没有从 HTML 集合创建一个数组，而试着将数组方法直接用于它浏览器将会抛出一个错误。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-01-at-11.57.27-AM.png" alt="当你将数组方法直接用于 HTML 集合的错误信息" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>当你将数组方法直接用于 HTML 集合的错误信息</figcaption>
</figure>
3. getElementsByTagName
<p>这个方法将通过标签名来选择元素。例如：<code>getElementByTagName('p')</code> 将选择页面中所有 <code>p</code> 标签。</p>
<p>像是 <code>getElementsByClassName()</code>，这个方法也返回一个被选择元素的 HTML 集合。</p>
<p>看这个例子:</p>
<pre><code class="language-javascript">const allContacts = document.getElementsByTagName('p')
console.log(allContacts)
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-02-at-8.39.36-AM.png" alt="一个包含所有 p 标签的 HTML 集合" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>一个包含所有 p 标签的 HTML 集合</figcaption>
</figure>
<p>你可以从 HTML 集合创建一个数组，然后使用任意数组方法。</p>
<pre><code class="language-javascript">let allContactsArray = [...allContacts]

allContactsArray.map(element =&gt; console.log(element))
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-02-at-9.08.26-AM.png" alt="在 allContactsArray 使用 map() 方法" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>在 allContactsArray 使用 map() 方法</figcaption>
</figure>
<blockquote>
<p>译者注：由于 <code>map()</code> 创建一个新数组，在没有使用返回的数组的情况下调用它是不恰当的；应该使用 <code>forEach</code> 或 <code>for...of</code> 作为代替。详见 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map#%E6%8F%8F%E8%BF%B0"><code>Array.prototype.map()</code></a>。</p>
</blockquote>
<h3 id="querySelector">4. querySelector</h3>
<p>你可以使用这个方法在 DOM 中选择任意的 HTML 元素，它仅返回一个元素：第一个匹配选择器的元素。</p>
<p><code>querySelector()</code> 用法类似 CSS 选择器。</p>
<p>举个例子，当你想要选择一个具有 id 的元素时，你会怎么做？用 <code>#id</code>。那当你想要选择具有 class 的元素呢？你会用 <code>.class</code>。</p>
<p>看这个例子:</p>
<pre><code class="language-javascript">const firstWorkContact = document.querySelector('.work')
console.log(firstWorkContact)
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-02-at-11.38.12-AM.png" alt="使用 querySelector() 的例子" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 querySelector() 的例子</figcaption>
</figure>
<p>上边这个例子只获取到了一个具有 <code>work</code> 类的元素，其他的被忽略了。</p>
<p>让我们再看一个像是 CSS 选择器一样使用 <code>querySelector()</code> 的例子。下方 <code>div</code> 元素包含了 4 个 <code>button</code>。</p>
<pre><code class="language-HTML">&lt;div&gt;
    &lt;button&gt;First button&lt;/button&gt;
    &lt;button&gt;Second button&lt;/button&gt;
    &lt;button&gt;Third button&lt;/button&gt;
    &lt;button&gt;Fourth button&lt;/button&gt;
&lt;/div&gt;
</code></pre>
<p>假设你想要选择第三个按钮，你可以像下面那样使用 <code>querySelector()</code>。代码中使用了 CSS <code>nth-child</code> 选择器去获取 <code>div</code> 中的第三个 <code>button</code>。</p>
<pre><code class="language-javascript">const thirdBtn = document.querySelector('div button:nth-child(3)')
console.log(thirdBtn)
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-02-at-2.42.48-PM.png" alt="使用 querySelector() 获取第三个按钮的结果" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 querySelector() 获取第三个按钮的结果</figcaption>
</figure>
<p>但如果你想选择所有四个按钮而不只是第一个呢？你应该使用 <code>querySelectorAll()</code> 代替。</p>
<h3 id="querySelectorAll">5. querySelectorAll</h3>
<p>类似 <code>querySelector()</code>，<code>querySelectorAll</code> 也是使用 CSS 选择器去选择 HTML 元素。不同的是它返回匹配选择器的所有元素，而不仅仅是第一个。</p>
<p>让我们使用 <code>querySelectorAll()</code> 选择上一个示例中所有的按钮。</p>
<pre><code class="language-javascript">const allBtns = document.querySelectorAll('button')
console.log(allBtns)
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-02-at-3.04.18-PM.png" alt="Screenshot-2023-12-02-at-3.04.18-PM" width="600" height="400" loading="lazy"></p>
<p><code>querySelectorAll()</code> 返回一个被选择元素组成的 NodeList。</p>
<p>注意：<code>querySelectorAll()</code> 返回一个 <code>NodeList</code>。<code>NodeList</code> 与 HTML 集合有点不同，你不需要转换成数组就可以使用 <code>forEach()</code> 方法。</p>
<pre><code class="language-javascript">allBtns.forEach(btn =&gt; console.log(btn))
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-02-at-3.00.19-PM.png" alt="在 NodeList 使用 forEach() 方法" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>在 NodeList 使用 forEach() 方法</figcaption>
</figure>
<p>但是你仍然不能在 NodeList 使用 <code>map</code>、<code>filter</code> 等这类数组方法，如果你需要的话应该创建一个数组。</p>
<p>你可以阅读这篇<a href="https://www.freecodecamp.org/news/dom-manipulation-htmlcollection-vs-nodelist/"> freeCodeCamp 关于 HTML 集合和 NodeList 之间区别的文章</a>来了解更多。</p>
<h2 id="changee-dom-content">如何更改 DOM 元素的内容</h2>
<p>目前为止，你已经学到了几种不同的方法去选择 DOM 元素，但这仅仅是开始。现在，让我们来看看如何操作 DOM 去更改网页的内容。</p>
<p>你需要做的第一件事就是选择元素，你可以用上一小节学到的任一方法。</p>
<p>在选择元素后，你可以使用这几种方法去添加或更新内容。</p>
<h3 id="innerhtml">`innerHTML` 属性</h3>
<p>这是一种可以让你读取、更新元素的内容或结构的方法。让我们看看应该如何使用 <code>innerHTML</code> 方法。</p>
<p>下面是一个包含三个 <code>p</code> 标签的代码片段，每个 <code>p</code> 标签都有一个 id。</p>
<pre><code class="language-HTML">  &lt;p id="topic"&gt;JS array methods&lt;/p&gt;
  &lt;p id="first-method"&gt;map&lt;/p&gt;
  &lt;p id="second-method"&gt;filter&lt;/p&gt;
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-03-at-8.17.55-AM.png" alt="三个 p 标签的代码片段" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>三个 p 标签的代码片段</figcaption>
</figure>
<p>你可以使用 <code>innerHTML</code> 获取任一段落的内容。举个例子，我们来获取第一个段落的内容。</p>
<pre><code class="language-javascript">const topicElement = document.querySelector('#topic')
console.log(topicElement.innerHTML)
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-03-at-8.10.36-AM.png" alt="topicElement 的 innerHTML 的输出" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>topicElement 的 innerHTML 的输出</figcaption>
</figure>
<p>现在，假设你想将 topic 内容从“JS array methods”更改为“JavaScript array methods”。你可以通过将新文本赋值给元素的 innerHTML 来实现。</p>
<pre><code class="language-javascript">const topicElement = document.querySelector('#topic')
topicElement.innerHTML = "JavaScript array methods"
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-03-at-8.16.59-AM-1.png" alt="topic 内容从 “JS Array methods” 更新到 “JavaScript array methods”" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>topic 内容从 “JS Array methods” 更新到 “JavaScript array methods”</figcaption>
</figure>
<p>使用 <code>innerHTML</code>，你可以更改的不只是内容，你也可以更改元素的 HTML 结构。例如，如果你想让 “JavaScript” 加粗，你可以这样做：</p>
<pre><code class="language-javascript">topicElement.innerHTML = "&lt;b&gt;JavaScript&lt;/b&gt; array methods"
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-03-at-8.27.45-AM.png" alt="使用 innerHTML 将 “JavaScript” 加粗了”" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 innerHTML 将 “JavaScript” 加粗了</figcaption>
</figure>
<h3 id="innerhtml-risk">使用 `innerHTML` 的安全风险</h3>
<p>使用 <code>innerHTML</code> 会带来潜在的安全风险，例如：<a href="https://www.freecodecamp.org/news/cross-site-scripting-what-is-xss/">XSS（Cross-site scripting，跨站脚本）攻击</a>。</p>
<p>如果插入的内容来自用户输入或任何不受信任的来源，请确保在使用 <code>innerHTML</code> 前做好校验或清洗，以防止 XSS 攻击。你可以使用类似 <a href="https://www.npmjs.com/package/dompurify">DOMPurify</a> 的库来实现这一点。</p>
<p>此外，如果你处理纯文本内容，可以考虑使用 <code>innerText</code> 和 <code>textContent</code>。</p>
<h3 id="innertext-and-textcontent">`innerText` 和 `textContent` 属性</h3>
<p><code>innerText</code> 和 <code>textContent</code> 忽略 HTML 标签，会把它们当作字符串的一部分。你可以使用这两种方法去读取或更新 DOM 元素的文本。</p>
<p>这两种方法的关键区别在于如何处理文本。使用 <code>innerText</code> 会返回显示在屏幕上的文本，而使用 <code>textContent</code> 则返回标记中的文本。来看下面的例子。</p>
<pre><code class="language-HTML">&lt;p&gt;Key =&lt;span style="display: none;"&gt;     ABC123&lt;span&gt;&lt;/p&gt;
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-03-at-10.03.41-AM.png" alt="一个 p 标签中有一些文本和一个隐藏的 span 标签" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>一个 p 标签中有一些文本和一个隐藏的 span 标签</figcaption>
</figure>
<p>这个例子中包括了一个 <code>p</code> 标签，<code>p</code> 标签中 <code>span</code> 标签包含了 Key 的值。因为 <code>span</code> 内联样式被设置为 “none”，所以 Key 值不会显示在屏幕上。</p>
<p>现在，让我们选择这个 <code>p</code> 标签，然后打印 <code>innerText</code>、<code>textContent</code> 这两个值，看看有什么不同。</p>
<pre><code class="language-javascript">const paragraph = document.querySelector('p');

console.log("innerText: ", paragraph.innerText)
console.log("textContent: ", paragraph.textContent)
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-03-at-10.06.11-AM.png" alt="innerText 和 textContent 的打印结果" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>innerText 和 textContent 的打印结果</figcaption>
</figure>
<p>请注意 <code>innerText</code> 返回的文本是在屏幕上显示的样子（不包含使用 CSS 隐藏的 Key 值），<code>textContent</code> 返回的文本包括了隐藏的元素和空格。</p>
<p>让我们再看另一个添加文本到元素的例子。下面的代码包括两个 <code>p</code> 标签，每个 <code>p</code> 标签都有一个 <code>b</code> 标签和一个空的 <code>span</code> 标签，以及它们之间有一个 <code>hr</code> 标签。</p>
<pre><code class="language-HTML">  &lt;p&gt;
    &lt;b&gt;innerText Example&lt;/b&gt;
    &lt;span id="inner-text"&gt;&lt;/span&gt;
  &lt;/p&gt;
  
  &lt;hr&gt;	
 
  &lt;p&gt;
    &lt;b&gt;textContent Example&lt;/b&gt;
    &lt;span id="textContent"&gt;&lt;/span&gt;
  &lt;/p&gt;
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-03-at-10.48.11-AM.png" alt="演示 innerText 和 textContent 属性的示例" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>演示 innerText 和 textContent 属性的示例</figcaption>
</figure>
<p>现在，让我们选择两个 <code>span</code> 元素，并在其中添加相同的文本。这将帮助你更好地理解 <code>innerText</code> 和 <code>textContent</code> 之间的区别。</p>
<pre><code class="language-javascript">const example1 = document.querySelector('#inner-text');
const example2 = document.querySelector('#text-content');

let address = `
  ABC Street,
  Spintex Road,
  Accra,
  Ghana.
`;

example1.innerText = address;
example2.textContent = address;
</code></pre>
<p>这段代码给两个示例提供了相同的变量 <code>address</code>，第一个使用了 <code>innerText</code>，第二个使用了 <code>textContent</code>。请看下面的结果：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-03-at-10.46.46-AM.png" alt="使用 innerText 和 textContent 更新内容的结果" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 innerText 和 textContent 更新内容的结果</figcaption>
</figure>
<p>请注意 <code>innerText</code> 换行了，而 <code>textContent</code> 却没有。</p>
<p>两个方法的另一个关键不同是它们在内部循环的行为，当在一个循环中处理批量操作或频繁更新时 <code>innerText</code> 比 <code>textContent</code> 会慢一些。</p>
<p>看看<a href="https://www.freecodecamp.org/news/innerhtml-vs-innertext-vs-textcontent/">这篇 freeCodeCamp 文章</a>可以了解更多 <code>innerHTML</code>、<code>innerText</code> 和 <code>textContent</code> 之间的不同。</p>
<h2 id="handle-dom">如何处理 DOM 元素的属性</h2>
<p><a href="https://www.freecodecamp.org/news/html-attributes-explained/">HTML 属性</a> 提供了有关 HTML 元素的有用信息。这些属性总是包含在元素的开始标签内，属性由一个 name 和 value 组成（但也有只出现 name 的例外情况）。</p>
<p>浏览器根据 HTML 结构生成 DOM 时，会将这些属性转化为 DOM 对象的动态属性。</p>
<p>看这个例子：</p>
<p>这个 HTML 中有一个按钮，按钮有一些属性：</p>
<pre><code class="language-HTML">&lt;button id="myBtn" type="submit"&gt;Click Here&lt;/button&gt;
</code></pre>
<p>对于这个例子，浏览器会创建一个 <code>HTMLButtonElement</code> 对象到 DOM 中，并且这个对象的属性也互相匹配。</p>
<ul>
<li><code>HTMLButtonElement.id</code> 的值是 <code>myBtn</code></li>
<li><code>HTMLButtonElement.type</code> 的值是 <code>submit</code></li>
</ul>
<p>要使用 JavaScript 与这些属性进行交互和操作，可以使用诸如 <code>getAttribute()</code> 和 <code>setAttribute()</code> 之类的方法直接访问这些属性。</p>
<h3 id="getAttribute">`getAttribute()` 方法</h3>
<p>顾名思义，你可以使用这个方法去获取元素上已经存在的属性。</p>
<p>它接受一个参数（属性名）返回属性值，如果你给的属性值在这个元素中不存在，这个方法会返回 <code>null</code>。</p>
<p>看这个例子：</p>
<pre><code class="language-HTML">&lt;img src="image.jpg" alt="An example image"&gt;
</code></pre>
<pre><code class="language-javascript">const imageElement = document.querySelector('img')
console.log(imageElement.getAttribute('src'))
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-09-at-9.00.25-AM.png" alt="getAttribute() 用来获取 src 属性值" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>getAttribute() 用来获取 src 属性值</figcaption>
</figure>
<p>在上面的例子中，使用 <code>getAttribute()</code> 方法，你可以获取 <code>img</code> 标签的 <code>src</code> 属性值。</p>
<h3 id="setAttribute">`setAttribute()` 方法</h3>
<p>这个方法用来设置、更改元素的属性。它接受两个参数，第一个参数是你想要更改的属性名，第二个参数是你想要设定的新值。</p>
<p>看这个例子：</p>
<pre><code class="language-HTML">&lt;img src="image.jpg" alt="An example image"&gt;
</code></pre>
<pre><code class="language-javascript">const imageElement = document.querySelector('img')

console.log("BEFORE:", imageElement.getAttribute('src'))
imageElement.setAttribute('src', 'new-image.jpg')
console.log("AFTER:", imageElement.getAttribute('src'))
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-09-at-9.27.14-AM.png" alt="使用 setAttribute() 更新 src 属性值" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 setAttribute() 更新 src 属性值</figcaption>
</figure>
<p>这个例子先打印了 <code>src</code> 属性值，然后使用 <code>setAttribute()</code> 从 <code>image.jpg</code> 到 <code>new-image.jpg</code> 更改了值。</p>
<p>当你给 <code>setAttribute()</code> 传递了一个在这个元素中不存在的属性作为参数时，它将创建一个新属性。例如，你可以添加一个 height 属性给 <code>img</code> 标签：</p>
<pre><code class="language-javascript">const imageElement = document.querySelector('img')

console.log("BEFORE:", imageElement.getAttribute('height'))
imageElement.setAttribute('height', '200')
console.log("AFTER:", imageElement.getAttribute('height'))
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-09-at-9.32.53-AM.png" alt="给 img 添加 height 属性的例子" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>给 img 添加 height 属性的例子</figcaption>
</figure>
<p>第一个打印语句返回 <code>null</code> 是因为 height 属性不存在，但是在我们使用 <code>setAttribute()</code> 设置过后，第二个打印语句返回了 height 的正确值。</p>
<h3 id="removeAttribute">`removeAttribute()` 方法</h3>
<p>在前面的小节中，你学到了如何使用 <code>setAttribute()</code> 添加一个新属性。如果你想移除一个已存在的属性呢？</p>
<p>你可以使用 <code>removeAttribute()</code> 方法，传递一个你想要移除属性名作为参数。</p>
<p>看这个例子：</p>
<pre><code class="language-HTML">  &lt;img src="image.jpg" alt="An example image" height="200"&gt;
</code></pre>
<p>让我们使用 <code>removeAttribute()</code> 方法移除 <code>height</code> 属性从 <code>img</code> 标签上。</p>
<pre><code class="language-javascript">const imageElement = document.querySelector('img')

console.log("BEFORE:", imageElement.getAttribute('height'))
imageElement.removeAttribute('height', '200')
console.log("AFTER:", imageElement.getAttribute('height'))
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-09-at-10.09.35-AM.png" alt="使用 removeAttribute() 的示例" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 removeAttribute() 的示例</figcaption>
</figure>
<p>在调用 <code>removeAttribute()</code> 前，我们第一次打印了 height 的属性值，是 200，但在调用 <code>removeAttribute()</code> 之后，第二次打印结果是 <code>null</code>，可以确认我们从 <code>img</code> 中移除了 height 属性。</p>
<h3 id="hasAttribute">`hasAttribute()` 方法</h3>
<p>处理 DOM 属性的的另一个方法是 <code>hasAttribute()</code>，你可以使用这个方法检查元素是否有指定的属性。</p>
<p>如果指定属性存在， 则 <code>hasAttribute()</code> 方法返回 <code>true</code>，否则返回 <code>false</code>。</p>
<p>看这个例子：</p>
<pre><code class="language-html">&lt;img src="image.jpg" alt="An example image" height="200"&gt;
</code></pre>
<p>让我们使用 <code>hasAttribute()</code> 去检查 <code>img</code> 上是否存在 <code>height</code> 和 <code>width</code> 属性。</p>
<pre><code class="language-javascript">const imageElement = document.querySelector('img')

console.log("HEIGHT", imageElement.hasAttribute('height'))
console.log("WIDTH", imageElement.hasAttribute('width'))
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-09-at-10.20.53-AM.png" alt="使用 hasAttribute() 检查属性是否存在的示例" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 hasAttribute() 检查属性是否存在的示例</figcaption>
</figure>
<p>因为它存在这个属性，所以 height 返回了 <code>true</code>，而检查 width 时返回了 <code>false</code>，因为它不存在。</p>
<h2 id="change-dom-style">如何更改 DOM 的样式</h2>
<p>在 JavaScript 中，有两种主要方法处理 DOM 元素的样式，你可以使用 <code>.style</code> 属性或者使用 <code>class</code>。每种方法都有自己的优点以及最适合的情况。</p>
<h3 id="use-style">使用 `.style` 属性设置样式</h3>
<p>如果你想对某个元素做指定更改的话，你可以使用 <code>.style</code> 属性。<code>.style</code> 属性允许你采用<a href="https://www.freecodecamp.org/news/inline-style-in-html/">内联</a>的形式直接为元素设置样式，这意味着他会覆盖你在 CSS 中的样式。</p>
<p>使用 <code>.style</code> 属性，你可以访问全部的 CSS 属性，看下面的演示：</p>
<pre><code class="language-html">  &lt;h1&gt;Styling elements with JavaScript&lt;/h1&gt;
</code></pre>
<pre><code class="language-javascript">const header = document.querySelector('h1')
console.log(header.style)
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/ezgif.com-video-to-gif--8-.gif" alt="打印到控制台上的 h1 元素的样式声明" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>打印到控制台上的 h1 元素的样式声明</figcaption>
</figure>
<p><code>console.log()</code> 打印了该元素的 CSS 样式声明以及所有的 CSS 属性。</p>
<p>现在我们看一个如何使用 <code>.style</code> 属性的例子。</p>
<pre><code class="language-html">  &lt;h1&gt;I love JavaScript&lt;/h1&gt;
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-11-at-7.56.41-AM.png" alt="一个 h1 标题" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>一个 h1 标题</figcaption>
</figure>
<p>这里有一个 <code>h1</code> 标题，现在让我们使用 <code>.style</code> 属性给它增加一点样式。</p>
<pre><code class="language-javascript">const paragraph = document.querySelector('h1')

paragraph.style.color = 'white'
paragraph.style.backgroundColor = 'green'
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-11-at-7.59.15-AM.png" alt="使用 .style 属性给 h1 元素增加背景色" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 .style 属性给 h1 元素增加背景色</figcaption>
</figure>
<p>注意：在 JavaScript 中，如果 CSS 属性名包含两个或更多的单词，你不能使用“-”连字符。例如，在 CSS 中你可以写 <code>background-color</code>，但是在 JavaScript 代码中，你必须使用小驼峰命名，所以 <code>background-color</code> 变成了 <code>backgroundColor</code>。</p>
<p>你也可以通过设定属性值为空字符串删除一个元素的样式。</p>
<pre><code class="language-javascript">element.style.propertyName = ""
</code></pre>
<h3 id="use-class">使用 class 设置样式</h3>
<p>通过 class，你可以一次创建样式，并将其应用于不同的元素。这有助于提高代码的可维护性。</p>
<h4 id="classname"><code>className</code> 属性</h4>
<p><code>className</code> 属性展示了 DOM 元素的 class 属性。并且你可以使用它去获取或设置 class 的属性值。</p>
<p>看这个例子：</p>
<pre><code class="language-html">&lt;p class="food rice-dish"&gt;Jollof rice&lt;/p&gt;
</code></pre>
<pre><code class="language-javascript">const jollofParagraph = document.querySelector('p')

console.log(jollofParagraph.className)

jollofParagraph.className = 'favorite'

console.log(jollofParagraph.className)
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-11-at-9.13.37-AM.png" alt="使用 className 属性更改 class 值的例子" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 className 属性更改 class 值的例子</figcaption>
</figure>
<p><code>className</code> 可以读取或替换最近的 class，在上面的例子中，第一次打印是 class 的初始值，在更新 <code>className</code> 属性后，第二次打印则是新值。</p>
<p>这里还有一个更灵活的属性，举个例子，如果你想添加一个其他 class 而不是使用新的 class 替换老的 class 呢？这就是 <code>classList</code> 属性的作用所在。</p>
<h4 id="classlist"><code>classList</code> 属性</h4>
<p>使用 <code>classList</code> 属性，你可以添加或者移除 class。你可以切换 class、使用新值替换 class，甚至可以检查 class 中是否包含某个值。</p>
<p>看这个例子：</p>
<pre><code class="language-html">&lt;p class="food"&gt;Jollof rice&lt;/p&gt;
</code></pre>
<pre><code class="language-javascript">const jollofParagraph = document.querySelector('p')
console.log(jollofParagraph.classList)
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-11-at-9.43.30-AM.png" alt="classList 中只有一个值" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>classList 中只有一个值</figcaption>
</figure>
<h4 id="classlistaddclass">使用 <code>classList.add()</code> 添加 class</h4>
<pre><code class="language-javascript">jollofParagraph.classList.add('fav', 'tasty')

console.log(jollofParagraph.classList)
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-11-at-9.46.14-AM.png" alt="使用 classList.add() 添加新 class 的例子" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 classList.add() 添加新 class 的例子</figcaption>
</figure>
<p>这段代码添加了 <code>fav</code> 和 <code>tasty</code> 两个新 class 到 class 列表。</p>
<h4 id="classlistremoveclass">使用 <code>classList.remove()</code> 移除 class</h4>
<pre><code class="language-javascript">jollofParagraph.classList.remove('tasty')

console.log(jollofParagraph.classList)
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-11-at-9.50.26-AM.png" alt="使用 classList.remove() 移除 class 的例子" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 classList.remove() 移除 class 的例子</figcaption>
</figure>
<p>这段代码从 class 列表中移除了 <code>tasty</code> class。</p>
<h4 id="classlistreplaceclass">使用 <code>classList.replace()</code> 替换 class</h4>
<pre><code class="language-javascript">jollofParagraph.classList.replace('fav', 'favorite')

console.log(jollofParagraph.classList)
</code></pre>
<p>这段代码使用 <code>favorite</code> 替换了 <code>fav</code></p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-11-at-9.53.30-AM.png" alt="使用 classList.replace() 替换 class 的例子" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 classList.replace() 替换 class 的例子</figcaption>
</figure>
<h4 id="classlistcontainsclass">使用 <code>classList.contains()</code> 检查 class 是否存在</h4>
<pre><code class="language-javascript">const isFavorite = jollofParagraph.classList.contains('favorite')
const isSoup = jollofParagraph.classList.contains('soup')

console.log("Contains favorite: ", isFavorite)
console.log("Contains soup: ", isSoup)
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-11-at-10.09.53-AM.png" alt="使用 classList.contains() 检查 class 是否存在的例子" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 classList.contains() 检查 class 是否存在的例子</figcaption>
</figure>
<p>这段代码会检查传递给它的 class 是否包含在 class 列表中。</p>
<p>如果包含在 class 列表中（例如 <code>favorite</code>），则返回 <code>true</code>；如果不包含在 class 列表中（例如 <code>soup</code>），则返回 <code>false</code>。</p>
<h4 id="classlisttoggleclass">使用 <code>classList.toggle()</code> 切换 class</h4>
<p>当你使用 <code>toggle()</code> 时，它会首先检查该 class 是否存在。如果存在，它将删除该 class 。如果不存在，则会添加。</p>
<pre><code class="language-javascript">jollofParagraph.classList.toggle('favorite')
console.log(jollofParagraph.classList)

jollofParagraph.classList.toggle('favorite')
console.log(jollofParagraph.classList)

jollofParagraph.classList.toggle('favorite')
console.log(jollofParagraph.classList)
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-11-at-10.19.18-AM.png" alt="使用 classList.toggle() 切换 class 的例子" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 classList.toggle() 切换 class 的例子</figcaption>
</figure>
<p>第一次切换，<code>favorite</code> 存在 class 列表中，所以移除。</p>
<p>第二次切换，<code>favorite</code> 不存在 class 列表中，所以添加。</p>
<p>第三次切换，现在 <code>favorite</code> 存在 class 列表中了，所以从 class 列表中移除。</p>
<p><code>toggle()</code> 会根据 class 的存在与否，不断从 class 列表中添加或删除该值。</p>
<h2 id="loop-through-dom">如何遍历 DOM</h2>
<p>遍历 DOM 意味着在 HTML 文档中的不同元素或节点间移动，包括选择和访问父级、子级或者是兄弟元素（或是节点）。你可以这样做去操作文档结构或是获取信息。</p>
<p>但在我们进入这一小节前，你需要理解节点和元素之间的不同。</p>
<h3 id="node-and-element-difference">节点和元素之间的不同</h3>
<p>节点是 DOM 的构件，它们代表着 HTML 结构中的不同组件。</p>
<p>元素是一种特定的节点，但并非所有节点都是元素。代码中一些像是元素属性、文本内容或者是注释都是节点，但它们不是元素。</p>
<p>元素是一种特定类型的节点，它定义了文档内容的结构，可以把元素当作你在用的 HTML 标签，例如 <code>&lt;div&gt;</code>、<code>&lt;p&gt;</code> 和 <code>&lt;ul&gt;</code>。每个元素都可以由属性、文本内容和其他嵌套元素组成。</p>
<h3 id="parentNode-or-parentElement">使用 `parentNode` 还是 `parentElement` 选择父级</h3>
<p>当要选择 DOM 元素的父级时，你可以使用 <code>parentNode</code> 或者 <code>parentElement</code>，它们都可以获取到你给的元素的父级。</p>
<p>从实用角度看，元素或节点的父级总是一个元素。所以，无论你使用哪一个，你总能获得选择元素的正确父级。</p>
<p>让我们看一个选择元素父级的例子：</p>
<pre><code class="language-html">  &lt;div class="container"&gt;
    &lt;p class="full-text"&gt;
        &lt;i id="italics"&gt;Some italicized text&lt;/i&gt;
    &lt;/p&gt;
  &lt;/div&gt;
</code></pre>
<pre><code class="language-javascript">const italicizedText = document.getElementById('italics')

console.log(italicizedText.parentNode)
console.log(italicizedText.parentNode.parentNode)
</code></pre>
<p>首先，选择一个元素，然后，调用 <code>parentNode</code> 这个属性去获取父级。你也可以像是第二个打印语句一样链式调用 <code>parentNode</code> 属性去获取父级的父级。</p>
<p>下面的截图展示了两个打印语句的输出。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-12-at-9.44.45-AM.png" alt="选择元素父级的示例" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>选择元素父级的示例</figcaption>
</figure>
<h3 id="childNodes-or-children">使用 `childNodes` 还是 `children` 选择子级</h3>
<p>你可以使用 <code>.childNodes</code> 和 <code>.children</code> 属性去选择元素的内容，但它们有些不同。</p>
<p><strong><code>childNodes</code>：</strong> 返回一个被选择元素的所有子节点的 NodeList。它包含元素和像是文本、注释或是其他非元素节点。</p>
<p><strong><code>.children</code>：</strong> 返回一个被选择元素的只包含子元素的 HTML 集合。它不包含像是文本、注释这样的非元素节点。</p>
<p>让我们通过一个例子看看它们的不同：</p>
<pre><code class="language-html">  &lt;div id="container"&gt;
    A text node
    &lt;p&gt;Some paragraph&lt;/p&gt;
    &lt;!-- This is a comment --&gt;
    &lt;span&gt;Span Element&lt;/span&gt;
  &lt;/div&gt;
</code></pre>
<p>上面的代码只有两个元素，<code>p</code> 和 <code>span</code>，但是有其他节点 —— 文本节点、注释。</p>
<pre><code class="language-javascript">const container = document.getElementById('container');

const containerChildNodes = container.childNodes;
const containerChildren = container.children;

console.log(containerChildNodes);
console.log(containerChildren);
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-12-at-10.29.23-AM.png" alt="使用 childNodes 属性的示例" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 childNodes 属性的示例</figcaption>
</figure>
<p><code>childNodes</code> 将返回所有的子节点（包括元素和非元素），它还将元素之间的空格作为文本节点。</p>
<p>这用起来可能会让人感到困惑。因此，除非有充分的理由，否则应坚持使用 <code>.children</code> 属性。</p>
<p><code>children</code> 将只会返回子元素（<code>p</code> 和 <code>span</code>）。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-12-at-10.34.08-AM.png" alt="使用 children 属性的示例" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用 children 属性的示例</figcaption>
</figure>
<h3 id="choose-first-or-last-element-node">选择第一个或是最后一个元素/节点</h3>
<p>如果你只需要选择第一个或是最后一个元素，你可以使用这四个属性。</p>
<ul>
<li><code>firstChild</code>: 只选择父元素的第一个子节点。</li>
<li><code>lastChild</code>: 只选择父元素的最后一个子节点。</li>
<li><code>firstElementChild</code>: 选择父元素的第一个子元素。</li>
<li><code>lastElementChild</code>: 选择父元素的最后一个子元素。</li>
</ul>
<p>让我们用上一小节中相同的例子，看看它们分别使如何工作的：</p>
<pre><code class="language-html">  &lt;div id="container"&gt;
    A text node
    &lt;p&gt;Some paragraph&lt;/p&gt;
    &lt;!-- This is a comment --&gt;
    &lt;span&gt;Span Element&lt;/span&gt;
  &lt;/div&gt;
</code></pre>
<pre><code class="language-javascript">const container = document.getElementById('container');

console.log("FIRST CHILD:", container.firstChild)
console.log("LAST CHILD:", container.lastChild)
console.log("FIRST ELEMENT: ", container.firstElementChild)
console.log("LAST ELEMENT:", container.lastElementChild)
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-13-at-7.43.25-AM.png" alt="选择第一个或是最后一个元素/节点的示例" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>选择第一个或是最后一个元素/节点的示例</figcaption>
</figure>
<p>注意 <code>firstChild</code> 返回的是第一个文本节点，但 <code>firstElementChild</code> 返回的是第一个元素 <code>p</code>。这意味着它忽略了 <code>p</code> 标签前的文本节点。</p>
<p>另外，请注意 <code>lastChild</code> 返回一个文本节点，尽管从标签上来看 <code>span</code> 之后似乎什么都没有。这是因为 <code>lastChild</code> 属性将 <code>span</code> 的结束标签和 <code>div</code> 元素的结束标签之间的换行符/空格视为一个节点。</p>
<p>这就是为什么通常来说使用 <code>firstElementChild</code> 和 <code>lastElementChild</code>会更安全。</p>
<h3 id="choose-sibling-node">在 DOM 中选择兄弟节点</h3>
<p>你已经学到了如何选择元素的父级或子级，你也可以使用以下属性选择元素的兄弟节点。</p>
<ul>
<li><code>nextSibling</code>: 选择相同父元素的下一个节点。</li>
<li><code>nextElementSibling</code>: 选择下一个元素忽略任何非元素节点。</li>
<li><code>previousSibling</code>: 选择相同父元素的上一个节点。</li>
<li><code>previousElementSibling</code>: 选择上一个元素忽略任何非元素节点。</li>
</ul>
<p>看这个例子：</p>
<pre><code class="language-html">  &lt;div&gt;
    &lt;p id="one"&gt;First paragraph&lt;/p&gt;
    text node
    &lt;p id="two"&gt;Second paragraph&lt;/p&gt;
    another text node
    &lt;p id="three"&gt;Third paragraph&lt;/p&gt;
    &lt;p id="four"&gt;Fourth paragraph&lt;/p&gt;
  &lt;/div&gt;
</code></pre>
<pre><code class="language-javascript">const paragraphTwo = document.getElementById('two')

console.log("nextSibling: ", paragraphTwo.nextSibling)
console.log("nextElementSibling: ", paragraphTwo.nextElementSibling)
console.log("previousSibling: ", paragraphTwo.previousSibling)
console.log("previousElementSibling: ", paragraphTwo.previousElementSibling)
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-13-at-7.57.18-AM.png" alt="选择兄弟节点的示例" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>选择兄弟节点的示例</figcaption>
</figure>
<p><code>nextSibling</code> 和 <code>previousSibling</code> 会选择文本节点，因为它们会考虑父节点内的所有节点。然而，<code>nextElementSibling</code> 和 <code>previousElementSibling</code> 可以只选择 <code>p</code> 标签，因为它们忽略文本这样的非元素节点。</p>
<h2 id="dom-event-and-event-listener">DOM 事件和事件监听器</h2>
<p>DOM 事件是浏览器中发生的动作。有了这些事件，你就可以使网站具有互动性。</p>
<p>一些 DOM 事件是用户发起的，像是点击、移动鼠标或是用键盘打字。另一些是浏览器发起的，像是页面加载完成。</p>
<h3 id="event-listener-and-handler">事件监听器与事件处理函数</h3>
<p>事件监听器是一个让你知道事件什么时候发生的方法，它允许你监听注意 DOM 事件，这样当事件发生时，你可以做点什么。</p>
<p>事件处理函数是对这个事件的响应，当事件发生时这个函数将会运行。</p>
<p>举个例子，你可以给按钮附加一个事件监听器，当用户点击的时候你就可以知道了。然后，你可以写一个事件处理函数，在点击事件发生时在屏幕上打印一些内容。</p>
<p>在这个案例中，当点击发生时事件监听器会通知你的应用，然后触发响应；这个响应（事件发生时调用的函数）就是事件处理函数。</p>
<h3 id="register-events">JavaScript 中三种注册事件的方法</h3>
<p>你可以使用 JavaScript 通过下面三种不同方法监听并响应 DOM 事件。</p>
<ul>
<li><strong>使用内联的事件处理函数：</strong> 就是你添加一个事件监听器作为 HTML 元素的属性。在 JavaScript 早期，这是使用事件唯一的方法。看下面这个例子：</li>
</ul>
<pre><code class="language-javascript">// Example of using an inline event handler

&lt;button onclick="alert('Hello')"&gt;Click me!&lt;/button&gt;
</code></pre>
<ul>
<li><strong>使用 onEvent 处理函数：</strong> 当元素只有一个事件处理函数时你可以这样使用。当你使用这个方法添加超过一个事件处理函数时，只有最后一个函数会运行，因为它会覆盖之前其他的。</li>
</ul>
<pre><code class="language-html">&lt;!-- An example of using an on-event handler --&gt;

&lt;button&gt;Click me!&lt;/button&gt;

&lt;script&gt;
  const myButton = document.querySelector('button')
	
  myButton.onclick = function() {
    console.log("Run first handler")
  }
	
  myButton.onclick = function() {
    console.log("Run second handler")
  }
&lt;/script&gt;
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-14-at-7.41.49-AM.png" alt="只有第二个事件处理函数被执行了" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>只有第二个事件处理函数被执行了</figcaption>
</figure>
<p>正如在控制台所看到的结果，浏览器只运行了第二个事件处理函数的代码。</p>
<ul>
<li><strong>使用 <code>addEventListener()</code> 方法：</strong> 这个方法允许你附加超过一个事件处理函数到一个元素上。并且它将按照它们被添加的顺序执行。</li>
</ul>
<p>一般来说，你应该坚持使用 <code>addEventListener()</code>，除非你有一个令人信服的理由。</p>
<p><code>addEventListener()</code> 接受两个参数，第一个参数是你想监听的事件名称，第二个参数是当事件发生时你想要运行的事件处理函数。</p>
<pre><code class="language-html">&lt;!-- An example of using the addEventListener method --&gt;

&lt;button&gt;Click me!&lt;/button&gt;

&lt;script&gt;
  const myButton = document.querySelector('button')
	
  myButton.addEventListener('click', function() {
    console.log("Run first handler")
  })
	
  myButton.addEventListener('click', function() {
    console.log("Run second handler")
  })
&lt;/script&gt;
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-14-at-7.51.22-AM.png" alt="addEventListener() 执行了两个处理函数" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>addEventListener() 执行了两个处理函数</figcaption>
</figure>
<h3 id="practice">实践挑战</h3>
<p>再继续学习之前这里有一个挑战。在看解题方法之前，先试着自己解决它。</p>
<p>请看下面的 HTML 和 CSS 代码。</p>
<p>这个挑战包括了两个元素，一个 <code>div#gift-box</code> 和一个 <code>button#click-btn</code>，礼物盒子有一个 <code>hide</code> class。</p>
<p>你的任务是写 JavaScript 去监听按钮的点击事件，当用户点击按钮时显示隐藏的盒子。</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
  &lt;head&gt;&lt;/head&gt;
  &lt;body&gt;
    
      &lt;div id="gift-box" class="hide"&gt;🎁&lt;/div&gt;
      &lt;button id="click-btn"&gt;Show the box&lt;/button&gt;
      
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<pre><code class="language-css">.hide {
  display: none;
}

#gift-box {
  font-size: 5em;
}
</code></pre>
<p><a href="https://stackblitz.com/edit/js-cywa91?file=index.html,style.css,index.js"><strong>在 StackBlitz 解决这个挑战</strong></a></p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/ezgif.com-video-to-gif-converted.gif" alt="挑战解决方案的动图演示" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>挑战解决方案的动图演示</figcaption>
</figure>
<h3 id="solution">实践挑战的解决方案</h3>
<p>如果你能解决这个难题，那恭喜你。如果你没有解决也没事，下面提供了解决方案和解释。</p>
<pre><code class="language-javascript">const giftBoxElement = document.getElementById('gift-box')
const buttonElement = document.getElementById('click-btn')

buttonElement.addEventListener('click', function() {
  giftBoxElement.classList.remove('hide')
})
</code></pre>
<p>为了解决这个挑战，首先你需要选择 <code>#gift-box</code> 和 <code>#click-btn</code> 两个元素。</p>
<p>然后，你需要给按钮添加一个事件监听器，像是之前提到的，<code>addEventListener()</code> 接受两个参数。</p>
<p>在这个案例中，第一个参数是 <code>'click'</code>，第二个参数是一个函数。</p>
<p>目标是显示这个盒子，盒子使用 <code>hide</code> class 在 CSS 中设置了 <code>display</code> 为 <code>none</code>。显示盒子的一种方法是使用 JavaScript 从 classList 中移除 <code>hide</code>。</p>
<h3 id="event-object">事件对象</h3>
<p>这是一个当事件发生时浏览器传递给事件处理函数作为参数的 JavaScript 对象。对象包含了一些有用的属性和方法：</p>
<ul>
<li><code>type</code>：发生的事件类型（例如：点击、鼠标悬浮、按下按键等等）</li>
<li><code>target</code>：触发这个事件的元素</li>
<li><code>clientX</code> 和 <code>clientY</code>：事件发生时，鼠标指针的水平和垂直的坐标</li>
<li><code>preventDefault()</code>：阻止与事件相关的默认动作，例如阻止表单的默认提交事件</li>
<li><code>stopPropagation()</code>：阻止事件通过 DOM 传播，后面会详细说明</li>
</ul>
<p>你可以查看所有的属性和方法在 <a href="https://developer.mozilla.org/en-US/docs/Web/API/Event">MDN 文档</a>。</p>
<h3 id="event-type">事件类型</h3>
<p>浏览器允许你监听的 DOM 事件种类很多，下面列举一些常见的。</p>
<p><strong>鼠标事件：</strong></p>
<ul>
<li><code>click</code>：当元素被点击</li>
<li><code>dbclick</code>：当元素被双击</li>
<li><code>mouseover</code>：当鼠标指针移入元素</li>
<li><code>mouseleave</code>：当鼠标指针离开元素</li>
<li><code>mousedown</code>：当鼠标在元素上按下</li>
<li><code>mouseup</code>：当鼠标在元素上松开</li>
</ul>
<p><strong>键盘事件：</strong></p>
<ul>
<li><code>keydown</code>：当键盘按键被按下</li>
<li><code>keyup</code>：当键盘按键被松开</li>
<li><code>keypress</code>：当按键被按下并显示事件的按键时，注意这个事件不是所有按键都可以触发，尤其是不可打印字符按键</li>
</ul>
<p><strong>表单事件：</strong></p>
<ul>
<li><code>submit</code>：当表单被提交</li>
<li><code>input</code>：当输入框字段更改</li>
<li><code>change</code>：当表单元素值被更改并失焦</li>
</ul>
<p><strong>窗口事件：</strong></p>
<ul>
<li><code>load</code>：当浏览器完成页面加载</li>
<li><code>unload</code>：当用户离开页面</li>
<li><code>resize</code>：当浏览器窗口被调整大小</li>
<li><code>scroll</code>：当用户滚动浏览文档</li>
</ul>
<p>你可以在这查看<a href="https://www.w3schools.com/jsref/dom_obj_event.asp">详细的 DOM 事件表</a>。</p>
<h2 id="event-flow">JavaScript 的事件流</h2>
<p>当 JavaScript 事件发生时，事件会在 DOM 中从发生事件的目标传播到最外层的元素，反之亦然。</p>
<p>例如，假设你点击了页面上的一个按钮。在点击按钮的同时，你也点击了它的父元素以及按钮在 DOM 层次结构中的任何元素。</p>
<h3 id="event-bubbling">事件冒泡</h3>
<p>这是指事件首先在发生的目标（或是某个元素）被注册，然后向外注册到父元素，最后注册到最外层的元素。</p>
<p>看这个例子：</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Event bubbling DEMO&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="outer"&gt;
            &lt;div id="inner"&gt;
              &lt;button id='btn'&gt;Click Here&lt;/button&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>例子中包含了一个 <code>#btn</code> 按钮，随着事件冒泡，当按钮上发生事件（如点击）时，事件会按以下顺序发生。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/12/4.png" alt="4" width="600" height="400" loading="lazy"></p>
<p>DOM 中的事件冒泡：从 <code>button</code> 到 <code>div#inner</code> 到 <code>div#outer</code> 到 <code>body</code> 到 <code>html</code> 到 <code>document</code>。</p>
<p>事件从目标元素开始向上冒泡，回到最外层的祖先元素。</p>
<h3 id="event-capturing">事件捕获</h3>
<p>事件捕获与事件冒泡相反，事件从最外层祖先元素开始向下沿着 DOM 树到目标元素。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/JavaScript--2-.png" alt="DOM 中的事件捕获" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>DOM 中的事件捕获</figcaption>
</figure>
<p>在事件捕获阶段，附加到元素上的事件侦听器是按照从最顶层的祖先到目标元素的层次结构顺序执行的。</p>
<p>如果你想知道为什么这很重要，让我们使用和上面相同的 HTML 片段，看一个实际的例子：</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Event bubbling DEMO&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="outer"&gt;
            &lt;div id="inner"&gt;
              &lt;button id='btn'&gt;Click Here&lt;/button&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>让我们分别添加一个事件监听器到<code>button</code>、<code>#inner</code>、<code>#outer</code> 上：</p>
<pre><code class="language-javascript">const button = document.getElementById('btn')
const innerDiv = document.getElementById('inner')
const outerDiv = document.getElementById('outer')

button.addEventListener('click', function() {
  console.log('Click on button')
})

innerDiv.addEventListener('click', function() {
  console.log('Click on inner Div')
})

outerDiv.addEventListener('click', function() {
  console.log('Click on outer Div')
})
</code></pre>
<p>默认情况下，浏览器使用事件冒泡的机制，因此，无需为事件监听器添加任何其他参数。这是事件处理函数响应按钮点击时的运行顺序：</p>
<ol>
<li><code>button</code></li>
<li><code>#innerDiv</code></li>
<li><code>#outerDiv</code></li>
</ol>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-15-at-11.54.07-AM.png" alt="在冒泡阶段，事件从目标元素到最外层处理" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>在冒泡阶段，事件从目标元素到最外层处理</figcaption>
</figure>
<p>使用事件捕获模型，你可以通过给事件监听器传递第三个参数为 <code>true</code>。</p>
<pre><code class="language-javascript">const button = document.getElementById('btn')
const innerDiv = document.getElementById('inner')
const outerDiv = document.getElementById('outer')

button.addEventListener('click', function() {
  console.log('Click on button')
}, true)

innerDiv.addEventListener('click', function() {
  console.log('Click on inner Div')
}, true)

outerDiv.addEventListener('click', function() {
  console.log('Click on outer Div')
}, true)
</code></pre>
<p>现在运行，事件处理函数的执行顺序将会是相反的方向，像是这样：</p>
<ol>
<li><code>#outerDiv</code></li>
<li><code>#innerDiv</code></li>
<li><code>button</code></li>
</ol>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-15-at-11.58.38-AM.png" alt="在捕获阶段，事件从最外层到目标元素处理" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>在捕获阶段，事件从最外层到目标元素处理</figcaption>
</figure>
<h3 id="event-stopPropagation-method">`stopPropagation()` 停止传播事件</h3>
<p>你已经学习了事件冒泡如何在元素上注册事件，并一直注册到最外层的祖先元素，也看到了事件捕获是怎么反过来的。</p>
<p>但是如果你不想注册事件到所有祖先呢？这就是 <code>stopPropagation()</code> 的作用所在，你可以使用这个方法在整个 DOM 中阻止事件的传播。</p>
<p>让我们看看如何在之前的例子中使用 <code>stopPropagation()</code>：</p>
<pre><code class="language-javascript">button.addEventListener('click', function(event) {
  event.stopPropagation()
  console.log('Click on button')
})

innerDiv.addEventListener('click', function() {
  console.log('Click on inner Div')
})

outerDiv.addEventListener('click', function() {
  console.log('Click on outer Div')
})
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/12/Screenshot-2023-12-15-at-2.48.37-PM.png" alt="Screenshot-2023-12-15-at-2.48.37-PM" width="600" height="400" loading="lazy"></p>
<p><code>stopPropagation()</code> 只允许第一个事件监听器的执行。</p>
<p>现在，只有一个事件处理函数被触发。因为 <code>stopPropagation()</code> 在按钮的事件处理函数中，所以在 <code>innerDiv</code> 和 <code>outerDiv</code> 的事件处理函数并没有被触发。</p>
<p>此外，注意 event 对象，你需要在事件处理函数中作为参数传递它。</p>
<h2 id="JS-DOM-manipulation-projects-ideas">JavaScript DOM 项目</h2>
<p>构建项目是提高对编码概念理解的绝佳方式，所以，卷起袖子，准备工作吧！</p>
<p>这有五个项目想法去帮助你练习巩固你的技巧。</p>
<h3 id="">切换开关</h3>
<p>设计一个开关，当它被点击时切换它的状态，更新 DOM （例如颜色）反映开关的当前状态。</p>
<h3 id="">随机颜色选择器</h3>
<p>创建一个简单的应用，用户可以点击一个按钮生成一个随机颜色。在屏幕上包括一个填充被选择颜色的图形，同时显示颜色代码。</p>
<h3 id="">倒计时</h3>
<p>构建一个从指定时间开始的计时器，在屏幕上实时更新显示剩余时间。</p>
<h3 id="">单词计数器</h3>
<p>开发一个应用，它提供一个文本输入框和文本域给用户用来输入。当用户输入时，在屏幕上实时显示单词个数。</p>
<h3 id="todo">交互式的 Todo 列表</h3>
<p>创建一个应用，允许用户添加、删除、编辑任务。你可以随你开心添加一些想要的高级特性，例如标记任务完成、过滤任务或是排序等。</p>
<h2 id="conclusion">总结</h2>
<p>如果你已经走到了这里，那你现在应该对操作 JavaScript DOM 有着不错的理解。随着实践，你将有足够的信心来处理需要了解这些 DOM 操作概念的高级项目。</p>
<p>一个良好的操作原版 JS DOM 的基础将在使用 React，Angular，Vue，Svelte 这些 JavaScript 库时派上用场。</p>
<p>感谢你阅读本文，祝你编码愉快！想获取更多有深度的教程，欢迎随时订阅<a href="https://www.youtube.com/@DevAfterHours">我的 YouTube 频道</a>。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
