w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com zh-hans w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/dom-tree-and-traversals.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><p>上一节,咱们整理了<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/663.html">DOM系列</a>中的第一篇,主要介绍<" href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/browser-and-the-dom.html">浏览器与DOM</a>相关的知识。从标题中我们可以看出来,今天所要学的东西包含两个部分,第一部分是DOM树,第二部分是遍历DOM。如果你和我一样对于DOM树和遍历DOM是初次接触,那个人建议您花点时间好好看看这两部分的知识。</p> <h2>DOM树</h2> <p>众所周之,HTML文档的主干就是标记(也就是大家熟悉的HTML标签元素)。</p> <p>根据文档对象模型(即:DOM),每个HTML标签事实上都是一个<strong>对象</strong>。嵌套的标签被称为<strong>之</strong>元素(或子标签)。除此之外,标签内的<strong>文本</strong>也是一个对象。而这些对象都可以使用JavaScript访问。</p> <p>那么啥是DOM树呢?我们先来看看现实生活中的例子。想象一棵与所有世代有关系的家庭树(大家熟知的族谱),其包括了:祖父母、父母、孩子、兄弟姐妹等等。我们通常以等级的方式组织豪庭树(族谱)。</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-1.jpeg" alt="" /></p> <p>上图是一个家族族谱的图。其中<code>Tossico</code>、<code>Akikazu</code>、<code>Hitomi</code>和<code>Takemi</code>是祖父母。而<code>Toshiaki</code>和<code>juliana</code>是父母。另外<code>TK</code>、<code>Yuji</code>、<code>Bruno</code>和<code>Kaio</code>是父母的孩子(其实也是我的兄弟姐妹们)。</p> <p>除了家族族谱之外,生活中还有另一个示例,那就是一个组织的结构层次,比如:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-2.jpeg" alt="" /></p> <p>而在HTML中,DOM其实也类似一棵树的,它和前面所举例的家族族谱,组织机构图是类似的,<strong>HTML中DOM就是一棵树</strong>。</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-3.jpeg" alt="" /></p> <h3>DOM的一个示例</h3> <p>我们来看一个DOM的示例,比如下面这样的一个HTML文档:</p> <pre><code>&lt;!DOCTYPE HTML&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;About elks&lt;/title&gt; &lt;/head&gt; &lt;body&gt; The truth about elks. &lt;/body&gt; &lt;/html&gt; </code></pre> <p>DOM将HMTML表示为标记的树结构(也就是大家所说的DOM树),就如下面这样的样子:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-4.png" alt="" /></p> <p>在上面的图中,你可以单击元素的节点,它们的子节点可以展开或者收缩,如下图所示:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-5.gif" alt="" /></p> <p>HTML的标签被称为元素(<code>element</code>)节点(或只是元素)。嵌套标签成为一个子元素(也被称为子)。因此,对于一个HTML文档而言,<code>&lt;html&gt;</code>是一个根节点(也被称为根元素),然后<code>&lt;head&gt;</code>和<code>&lt;body&gt;</code>是<code>&lt;html&gt;</code>的子元素。</p> <p>元素内的文本被称这文本节点,标记为<code>#text</code>。文本节点仅包含一个字符串。它可能没有子元素,也就是说它永远只是树的叶子(没有成为树枝的可能)。</p> <p>除此之外,要注意文本节点中的两个特殊字符:</p> <ul> <li>换行符:<code>↵</code>(对应JavaScript中的<code>\n</code>)</li> <li>空白符:<code>␣</code></li> </ul> <p>空格和换行符是完全有效的字符,它们形成文本节点并成为DOM的一部分。因此,例如在<code>&lt;head&gt;</code>标签之上的示例中,在<code>&lt;title&gt;</code>这前包含了一些空格,并且该文本成为一个<code>#text</code>节点(它只包含一条换行符和一些空格)。</p> <p>不过要注意的是,有两个将会除外:</p> <ul> <li>在<code>&lt;head&gt;</code>标签之前的空格和换行符由于历史原因将被忽略</li> <li>如果我们将一些东西放在<code>&lt;/body&gt;</code>之后,那么它就会自动地移到<code>&lt;/body&gt;</code>的前面,正如HTML规范要求的一样,所有内容必须在<code>&lt;/body&gt;</code>中一样。因此,在<code>&lt;/body&gt;</code>之后可能没有空格</li> </ul> <p>在其他情况之下,一切都很简单。如果文档中有空格(就像任何字符一样),那么它们就会成为DOM中的文本节点,如果我们删除它们,那么就不会有任何东西,也不再会有空格符或换行符的节点。</p> <p>比如下面这个示例:</p> <pre><code>&lt;!DOCTYPE HTML&gt; &lt;html&gt;&lt;head&gt;&lt;title&gt;About elks&lt;/title&gt;&lt;/head&gt;&lt;body&gt;The truth about elks.&lt;/body&gt;&lt;/html&gt; </code></pre> <p>上面的HTML结构对应的DOM树如下图所示:</p> <p><img src="/sites/default/files/blogs/2015/1805/dom-tree-6.png" alt="" /></p> <p>相比上面的截图可以看出来,没有了空格符和换行符的文本节点。</p> <p>通过上面的示例,可能你对DOM树有一定的了解了。但对一些一技术的定义估计还不是非常的了解,接下来花点时间来说一下DOM中的一些技术定义。</p> <h3>DOM中的技术定义</h3> <p>DOM树(<code>tree</code>)是一个DOM节点(<code>nodes</code>)的集合(拿到生活中来说,树是称为节眯的实体集合)。而其中节点由边(<code>edges</code>)连接。每个节点(<code>node</code>)都包含一个值(<code>value</code>)或数据(<code>data</code>),它可能或有可能没有子节点(<code>child node</code>)。</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-7.jpeg" alt="" /></p> <p><code>tree</code>的<code>first node</code>称为<code>root</code>节点。如果<code>root</code>节点由另一个节点连接,则<code>root</code>节点是父节点,连接的节点是子节点。</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-8.jpeg" alt="" /></p> <p>所有的树节点(<code>Tree nodes</code>)都被<code>edges</code>连接在一起。它是树(<code>trees</code>)的重要组成部分,因为它管理节点(<code>nodes</code>)之间的关系。</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-9.jpeg" alt="" /></p> <p>对于一棵树而言,叶子(<code>leaes</code>)是树(<code>tree</code>)上的最后一个节点(<code>nodes</code>)。它们是没有子节点。就像真正的树一样,DOM也是有根(<code>root</code>)、枝(<code>Element</code>)和叶子(文本节点)。</p> <p>除此之外,其他还需要理解的重要概念是高度(<code>height</code>)和深度(<code>depth</code>)。树的高度是叶子最长路径的长度;节点的深度是路径到其根的长度。用下图来阐述会更形象一些:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-10.jpeg" alt="" /></p> <p>简单的总结一些术语:</p> <ul> <li><strong><code>root</code></strong>(根节点)是树(<code>tree</code>)最顶端的节点</li> <li><strong><code>edge</code></strong>(边缘)是两个节点(<code>node</code>)之间的连接</li> <li><strong><code>child</code></strong>(子节点)是具有父节点的节点</li> <li><strong><code>parent</code></strong>(父节点)是一个节点,它具有子节点的边缘</li> <li><strong><code>leaf</code></strong>(树叶)是树中没有子节点的节点</li> <li><strong><code>height</code></strong>(高度)是叶子最长路径的长度</li> <li><strong><code>depth</code></strong>(深度)是路径到其根的长度</li> </ul> <blockquote> <p>有关于这方面更深入的介绍可以阅读<a href="http://www.xysjxj.com/quot;//medium.freecodecamp.org/@leandrotk">@TK</a>的《<" href="http://www.xysjxj.com/quot;//medium.freecodecamp.org/all-you-need-to-know-about-tree-data-structures-bceacb85490c">Everythin" you need to know about tree data structures</a>》一文。</p> </blockquote> <p>另外@TK的文章还涉及到了<strong>深度优先遍历</strong>和<strong>广度优秀遍历</strong>,有关于这两个概念的深入介绍,可以阅读:</p> <ul> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/querySelectorAll-vs-getElementsByTagName.html"><code>querySelectorAll</code> 和 <code>getElementsByTagName</code></a></li> <li><a href="http://www.xysjxj.com/quot;//medium.com/basecs/demystifying-depth-first-search-a7c14cccf056">Demystifyin" Depth-First Search</a></li> <li><a href="http://www.xysjxj.com/quot;//medium.com/basecs/breaking-down-breadth-first-search-cebe696709d9">Breakin" Down Breadth-First Search</a></li> </ul> <p>其实有关于<strong>深度优先遍历</strong>和<strong>广度优秀遍历</strong>在DOM树中的作用并不明显,对于后续的DOM遍历还是有很大的影响。</p> <p>经过的上面的学习,我们对于DOM树有了一定的了解。除此之外,浏览器对于DOM还具有自动较正的特性。</p> <h3>自动校正</h3> <p>如果浏览器遇到格式错误的HTML,它会自动更正它(校正)。</p> <p>例如,HTML最顶端的标签总是<code>&lt;html&gt;</code>。即使它不存在文档中 —— 它将存在DOM中,浏览器也会创建它。另外<code>&lt;body&gt;</code>也是一样。</p> <p>例如,HTML文件只包含一个单词<code>Hello</code>,浏览器将它放置在成<code>&lt;html&gt;</code>和<code>&lt;body&gt;</code>中,并且也会添加所需的<code>&lt;head&gt;</code>。其DOM将是:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-11.png" alt="" /></p> <p>另外,生成DOM时,浏览器会自动处理文档中的错误,比如关闭标签等等。比如下面这样一个无效的文档:</p> <pre><code>&lt;p&gt;Hello &lt;li&gt;Mom &lt;li&gt;and &lt;li&gt;Dad </code></pre> <p>事实上,浏览器渲染时,它照样会成为一个正常的 DOM,那是因为浏览器读取标签并会自动修复丢失的部分(比如说关闭标签):</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-12.png" alt="" /></p> <p>除此之外,还有一个有趣的<strong>“特殊情况”</strong>,那就是<code>table</code>(表格元素)。根据DOM规范,它必须有<code>&lt;tbody&gt;</code>,但是如果你在HTML文档中忘记写该标签元素时,浏览器会自动在DOM中添加<code>&lt;tbody&gt;</code>标签。比如:</p> <pre><code>&lt;table id="table"&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; </code></pre> <p>此时浏览器渲染出来的DOM结构如下:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-13.png" alt="" /></p> <h3>其他节点类型</h3> <p>我们可以在一个HTML文档中添加更多的标签和在页面中添加注释,比如:</p> <pre><code>&lt;!DOCTYPE HTML&gt; &lt;html&gt; &lt;body&gt; The truth about elks. &lt;ol&gt; &lt;li&gt;An elk is a smart&lt;/li&gt; &lt;!-- comment --&gt; &lt;li&gt;...and cunning animal!&lt;/li&gt; &lt;/ol&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p>对于上面的HTML文档,其对应的DOM树如下图所示:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-14.png" alt="" /></p> <p>上图中,我们看到了一个新的节点类型 —— <strong>注释节点</strong>,标记为 <code>#comment</code>。</p> <p>你可能会想,为什么要将注释添加到DOM中呢?它不会以任何方式影响视觉上的效果,但是有一个规则,如果某个东西在HTML中,那么它也必须在DOM树中。</p> <blockquote> <p><strong>HTML中的一切,甚至是注释,都将成为DOM的一部分。</strong></p> </blockquote> <p>即使是<code>&lt;!DOCTYPE ...&gt;</code>指令也是一个DOM节点。它在DOM树中,在<code>&lt;html&gt;</code>之前。我们不会去触摸那个节点,我们甚至不会在图上画它,但它却实实大大的存在那里。</p> <p><code>document</code>对象也是一个DOM节点,表示整个文档。在DOM中,其有<a href="http://www.xysjxj.com/quot;//dom.spec.whatwg.org/#node"><code>12</code>种节点类型</a>。在实际操作中,我们通常使用4种方法:</p>" <ul> <li><code>document</code>:进入DOM的入口点</li> <li>元素节点:HTML标签,树构建块</li> <li>文本节点:包含文本</li> <li>注释:有时候我们可以把信息放在这里,但它不会显示出来,不过JavaScript却可以从DOM中读取它</li> </ul> <p>或许你和我一样,希望能对每个HTML文档对应的DOM结构能实时的查看,我们希望有对应的工具能帮助我们。事实上是有类似这样的工具,比如 <a href="http://www.xysjxj.com/quot;//software.hixie.ch/utilities/js/live-dom-viewer/">Liv" DOM Viewer</a>。只要输入文档,它就会立即显示DOM树结构。</p> <h3>DOM中的空白符</h3> <p><a href="http://www.xysjxj.com/quot;//developer.mozilla.org/zh-CN/docs/Web/Guide/API/DOM/Whitespace_in_the_DOM">DO" 中的空白符</a>会让处理节点结构时增加不少麻烦。在Mozilla 的软件中,原始文件里所有空白符都会在 DOM 中出现(不包括标签内含的空白符)。这样的处理方式有其必要之处,一方面编辑器中可迳行排列文字、二方面 CSS 里的 <code>white-space: pre</code> 也才能发挥作用。 如此一来就表示:</p> <ul> <li>有些空白符会自成一个文本节点。</li> <li>有些空白符会与其他文本节点合成为一个文本节点。</li> </ul> <p>换句话说,下面这段 HTML 代码对应的 DOM 节点结构会如附图所示,其中<code>\n</code>代表换行符:</p> <pre><code>&lt;!-- My document --&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;My Document&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;h1&gt;Header&lt;/h1&gt; &lt;p&gt; Paragraph &lt;/p&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p>对应的DOM树,如下图所示:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-15.png" alt="" /></p> <p>这么一来,要使用 DOM 游走于节点结构间又不想要无用的空白符时,会有点困难。</p> <p>以下的 JavaScript 代码定义了许多函数,能够让你在处理 DOM 中的空白符时轻松点:</p> <pre><code>/** * 以下所谓的“空白符”代表: * "\t" TAB \u0009 (制表符) * "\n" LF \u000A (换行符) * "\r" CR \u000D (回车符) * " " SPC \u0020 (真正的空格符) * * 不包括 JavaScript 的“\s”,因为那代表如不断行字符等其他字符。 */ /** * 测知某节点的文字内容是否全为空白。 * * @参数 nod |CharacterData| 类的节点(如 |Text|、|Comment| 或 |CDATASection|)。 * @传回值 若 |nod| 的文字内容全为空白则传回 true,否则传回 false。 */ function is_all_ws( nod ) { // Use ECMA-262 Edition 3 String and RegExp features return !(/[^\t\n\r ]/.test(nod.data)); } /** * 测知是否该略过某节点。 * * @参数 nod DOM1 |Node| 对象 * @传回值 若 |Text| 节点内仅有空白符或为 |Comment| 节点时,传回 true, * 否则传回 false。 */ function is_ignorable( nod ) { return ( nod.nodeType == 8) || // 注释节点 ( (nod.nodeType == 3) &amp;&amp; is_all_ws(nod) ); // 仅含空白符的文字节点 } /** * 此为会跳过空白符节点及注释节点的 |previousSibling| 函数 * ( |previousSibling| 是 DOM 节点的特性值,为该节点的前一个节点。) * * @参数 sib 节点。 * @传回值 有两种可能: * 1) |sib| 的前一个“非空白、非注释”节点(由 |is_ignorable| 测知。) * 2) 若该节点前无任何此类节点,则传回 null。 */ function node_before( sib ) { while ((sib = sib.previousSibling)) { if (!is_ignorable(sib)) return sib; } return null; } /** * 此为会跳过空白符节点及注释节点的 |nextSibling| 函数 * * @参数 sib 节点。 * @传回值 有两种可能: * 1) |sib| 的下一个“非空白、非注释”节点。 * 2) 若该节点后无任何此类节点,则传回 null。 */ function node_after( sib ) { while ((sib = sib.nextSibling)) { if (!is_ignorable(sib)) return sib; } return null; } /** * 此为会跳过空白符节点及注释节点的 |lastChild| 函数 * ( lastChild| 是 DOM 节点的特性值,为该节点之中最后一个子节点。) * * @参数 par 节点。 * @传回值 有两种可能: * 1) |par| 中最后一个“非空白、非注释”节点。 * 2) 若该节点中无任何此类子节点,则传回 null。 */ function last_child( par ){ var res=par.lastChild; while (res) { if (!is_ignorable(res)) return res; res = res.previousSibling; } return null; } /** * 此为会跳过空白符节点及注释节点的 |firstChild| 函数 * * @参数 par 节点。 * @传回值 有两种可能: * 1) |par| 中第一个“非空白、非注释”节点。 * 2) 若该节点中无任何此类子节点,则传回 null。 */ function first_child( par ){ var res=par.firstChild; while (res) { if (!is_ignorable(res)) return res; res = res.nextSibling; } return null; } /** * 此为传回值不包含文字节点资料的首尾所有空白符、 * 并将两个以上的空白符缩减为一个的 |data| 函数。 *( data 是 DOM 文字节点的特性值,为该文字节点中的资料。) * * @参数 txt 欲传回其中资料的文字节点 * @传回值 文字节点的内容,其中空白符已依前述方式处理。 */ function data_of( txt ) { var data = txt.data; // Use ECMA-262 Edition 3 String and RegExp features data = data.replace(/[\t\n\r ]+/g, " "); if (data.charAt(0) == " ") data = data.substring(1, data.length); if (data.charAt(data.length - 1) == " ") data = data.substring(0, data.length - 1); return data; } </code></pre> <h2>DOM的遍历</h2> <p>如果你阅读了上面的的内容,或许你已经意识到,DOM看起来就像一个巨大的树 —— 一棵巨大的树,它的元素挂载在树枝上。为了获得更多的技术,DOM中的元素被安排在一个层次结构中,它定义了你最终在浏览器中看到的内容:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-16.png" alt="" /></p> <p>这个层次结构用于帮助我们组织HTML元素。它还用于帮助你的CSS样式规则理解什么样式适用于哪些东西。从JavaScript角度来看,这个层次结构确实增加了一点复杂性。你会花相当多的时间去弄清楚你现在所有的DOM和你需要去的地方。当我们考虑创建新的元素或移动元素时,这将变得更加明显。这种复杂性是你需要适应的。</p> <h3>找到你的方式</h3> <p>在你找到元素并与它们做一些事情之前,你首先需要了解元素的位置。我解决这个问题,最简单的方法就是从头开始,然后一路向下。这就是我们要做的。</p> <p>为了更易于帮助在大家理解,先回到<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/browser-and-the-dom.html">上一节中的示例中</a>:</p> <pre><code>&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta content="DOM, JavaScript, W3cplus" name="keywords" /&gt; &lt;meta content="DOM系列,浏览器和DOM!" name="description" /&gt; &lt;title&gt;LOL! Sea Otter! Little Kid!&lt;/title&gt; &lt;link href="style.css" rel="stylesheet"/&gt; &lt;/head&gt; &lt;body&gt; &lt;div id="container"&gt; &lt;img src="韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)_logo.png"/&gt; &lt;h1&gt;DOM系列学习!&lt;/h1&gt; &lt;p class="bodyText"&gt;开始学习DOM,这是一个有关于DOM学习的系列伟德1946手机版...&lt;p&gt; &lt;div class="submitButton"&gt;next&lt;/div&gt; &lt;/div&gt; &lt;script src="main.js"&gt;&lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p>来自DOM顶部的视图由<code>window</code>、<code>document</code>和<code>html</code>元素组成:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-17.png" alt="" /></p> <p>由于这三样东西的重要性,DOM为你提供了通过<code>window</code>、<code>document</code>和<code>document.documentElement</code>访问它们的方法。</p> <pre><code>var windowObject = window; var documentObject = document; var htmlElement = document.documentElement; </code></pre> <p>需要注意的一点是,<strong><code>window</code></strong>和<strong><code>document</code></strong>都是全局属性。不必要明确的声明它们,可以直接从容器里拿出来用就行了。</p> <p>往往,最顶层的树节点可以直接作为<code>document</code>属性使用,比如:</p> <pre><code>&lt;html&gt; = document.documentElement </code></pre> <p>顶部文档节点<code>document.documentElement</code>,其对应的就是<code>&lt;html&gt;</code>的DOM节点。另外一个广泛使用的DOM节点是<code>&lt;body&gt;</code>元素,其对应的是<code>document.body</code>:</p> <pre><code>&lt;body&gt; = document.body </code></pre> <p>同样的,<code>&lt;head&gt;</code>标签可以用<code>document.head</code>。</p> <p>不过有一点需要注意:</p> <blockquote> <p><strong><code>document.body</code>有可能为<code>null</code>。当脚本在访问不存在的元素时,返回的值将会为<code>null</code></strong>。</p> </blockquote> <p>比如,当你的脚本在<code>&lt;/head&gt;</code>中运行,比如<code>document.body</code>是将返回的值将是<code>null</code>,因为浏览器还没有读取它。但在<code>&lt;/body&gt;</code>中的<code>&lt;script&gt;</code>中返回的则是<code>&lt;body&gt;</code>元素:</p> <pre><code>&lt;html&gt; &lt;head&gt; &lt;script&gt; console.log('From head:', document.body) // =&gt; null &lt;/script&gt; &lt;/head&gt; &lt;body&gt; &lt;script&gt; console.log('From body:', document.body) // =&gt; HTMLBodyElement &lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p>上面我们所看到的是<code>html</code>、<code>head</code>和<code>body</code>元素的获取。但事实上,一旦你进入HTML元素级别,你的DOM将开始分支并变得更有趣。在这一点上,你有几种获取DOM的方式。通过使用<code>querySelector()</code>和<code>querySelectorAll()</code>可以帮助你精确地获取你想要获取的DOM元素。或许你已经在项目中大量使用这两种方法了。但事实上,对于许多实际案例来说,这两种方法太过局限。</p> <p>有时候,你不知道你想去哪里。<code>querySelector()</code>和<code>querySelectorAll()</code>主法在这里无法帮助您。你只想上车然后开车,并想找到你想要去的地方。回到DOM的世界当中时,你会发现自己一直处理这个位置。这就是DOM提供的各种内置属性,所有的Motorcycle Diaries将会帮助你,接下来我们将看看这些属性。</p> <p>能够帮助你的是知道所有的DOM元素都至少有一个组合,包括<strong>父母(Parents)</strong>、<strong>兄弟姐妹(Siblings)</strong>和<strong>子元素(Children)</strong>。为了更直观的帮助大家理解,来看下图,下图中包含<code>div</code>的<code>script</code>的一个树形图:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-18.png" alt="" /></p> <p><code>div</code>和<code>script</code>是<strong>兄弟元素</strong>。他们是兄弟元素的原因是他们共有一个相同的父元素<code>body</code>。<code>script</code>元素没有子元素,但是<code>div</code>元素有四个子元素,<code>img</code>、<code>h1</code>、<code>p</code>和<code>div</code>。这四个元素也相互被称为兄弟元素,同样的是因为他们有相同的父元素。这其实很好理解,如果你阅读了文章前面的DOM树相关的内容,你会发现它们就像现实的生活中一样,父母、孩子 和兄弟姐妹的关系基于你所关注的树的位置(对应的就是家族族谱)。几乎每个元素,取决于你看它们的角度,可以扮演多个家庭角色。</p> <p>为了更好的理解,DOM中提供了一些对应的属性(这些属性具有一定的依赖关系)。包括:<code>firstChild</code>、<code>lastChild</code>、<code>parentNode</code>、<code>children</code>、<code>previousSibling</code>和<code>nextSibling</code>。从他们的名称上来看,就可以推出这些属性的作用。这几个属性结合在一起将构建一个DOM遍历链接图,允许在DOM节点间找到你想要找到的DOM:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-19.png" alt="" /></p> <p>为了更好的理解DOM遍历相关的知识点,咱们接下来将围绕这几个属性来展开。</p> <h3>兄弟姐妹和父母打交道</h3> <p>在这些DOM属性中,最容易处理的是父母和兄弟姐妹。对应的属性有<code>parentNode</code>、<code>previousSibling</code>和<code>nextSibling</code>。下面这张图将帮助你了解这三个属性是如何工作的:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-20.png" alt="" /></p> <p>这张图虽然有点零乱,但是你仔细看的话,你可以理清楚它们之间的关系,以及他们之间发生了什么。<code>parentNode</code>属性指向元素的父元素。<code>previousSibling</code>和<code>nextSibling</code>属性允许元素元素它的前一个或下一个兄弟元素。你可以在图中看到箭头的方向指向。最后一行,<code>img</code>的<code>nextSibling</code>是<code>div</code>,相应的<code>div</code>的<code>previousSibling</code>是<code>img</code>元素。不管是通过<code>img</code>或<code>div</code>的<code>parentNode</code>属性都将把我们带入到第二行中的<code>div</code>(事实上就是<code>img</code>和<code>div</code>的元素)。通过上图,大家理解起来是不是很简单。</p> <h3>子元素打交道</h3> <p>上面咱们看到的是如何通过DOM的属性来访问兄弟元素和父元素,事实上,除此之外,DOM还提供一些属性可以访问元素的子元素,比如<code>firstChild</code>、<code>lastChild</code>和<code>children</code>。同样用一图来向大家展示:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-21.png" alt="" /></p> <p><code>firstChild</code>和<code>lastChild</code>属性是指父元素的第一个和最后一个子元素。如果父类只有一个子元素,就像例子中的<code>body</code>元素一样,<code>firstChild</code>和<code>lastChild</code>都指向相同的元素<code>div</code>。如果一个元素没有子元素,那么<code>firstChild</code>和<code>lastChild</code>属性将返回一个<code>null</code>。</p> <p>与其他属性相比,其中<code>children</code>属性相对而方要更为复杂一些。当你在父类上访问<code>children</code>属性时,基本上会得到父元素的子元素集合。这个集合并不是数组,但它确实有一些类似数组的能力,就是大家所说的<strong>类数组</strong>,其具有<code>length</code>属性,可以通过<code>[]</code>或<code>item()</code>来索引集合中具体的元素。比如上图中的<code>div.children[0]</code>访问到的是第一个<code>img</code>元素。</p> <p>在DOM中获取子节点,除了前面提到的三个属性之外,还有一个<code>childNodes</code>属性,不过它和<code>children</code>有一个很明显的区别:</p> <blockquote> <p><strong><code>children</code>只获取子节点(即子元素),而<code>childNodes</code>除了获取的子节点还包括文本节点</strong>。</p> </blockquote> <p>除此之外,还有一个特殊的函数<code>hasChildNodes()</code>可以用来判断某个元素是否包含子节点。</p> <p>这个时候,你把它们放在一起,你就可以对DOM进行遍历。也可以做一些事情。比如,检查某个元素是否有子元素存在,我们就可以这样做:</p> <pre><code>let bodyElement = document.body if (bodyElement.firstChild) { // 这里做你想做的事情... } </code></pre> <p>如果<code>body</code>没有子节点,那么<code>if</code>语奖将返回<code>null</code>。当然,你也可以使用<code>bodyElement.lastChild</code>或<code>bodyElement.children</code>做为<code>if</code>语句的条件。</p> <p>再来看另一个简单示例,前面提到过了,通过<code>children</code>可以获取某个元素的所有子节点(前提是这个元素有子元素存在)。这个时候得到的是一个类数组,如查你要获取到该元素中的每个子节点,就需要使用<code>for</code>循环来处理:</p> <pre><code>var bodyElement = document.body; for (var i = 0; i &lt; bodyElement.children.length; i++) { var childElement = bodyElement.children[i]; document.writeln(childElement.tagName); } </code></pre> <h3>通过元素遍历DOM</h3> <p>上面咱们看到的是通过DOM节点来遍历DOM。比如<code>childNodes</code>属性,除了可以获取元素节点之外,还可以获取文本节点,甚至是注释节点。但很多时候,对于DOM的操作,咱们只需要获取想要的DOM元素节点,而不需要考虑文本和注释节点。这个时候咱们只需要操作元素节点即可,这对应DOM中操作元素节点的一些属性。同样的使用下图来向大家阐述,易于理解:</p> <p><img src="/sites/default/files/blogs/2018/1805/dom-tree-22.png" alt="" /></p> <p>和前面相比,这里的属性多了<code>Element</code>这个词,其对应的含义:</p> <ul> <li><code>children</code>:元素节点的子元素</li> <li><code>firstElementChild</code>、<code>lastElementChild</code>:元素的第一个或最后一个子元素</li> <li><code>previousElementSibling</code>和<code>nextElementSibling</code>:元素的前一个或后一个相邻元素</li> <li><code>parentElement</code>:元素的父元素</li> </ul> <h2>总结</h2> <p>这篇文章是DOM系列的第二篇文章,主要介绍了DOM树和DOM的遍历。前一部分只要介绍了DOM树,简单的理解,任何一个HTML文档都可以类似于家族的族普来绘制对应的DOM树。通过DOM树可以理清楚每个DOM元素(或者说DOM节点)之间的关系。比如,父子关系、兄弟关系等。</p> <p>另外,在DOM中找到对应的元素是每位JavaScript开发人员都应该需要掌握的技巧之一。这篇文章的后一部分主要向大家介绍了如何对DOM进行遍历,其实就是通过DOM的属性怎么获取DOM的元素或节点。简单的归纳一下,分为:</p> <ul> <li>向上获取,比如<code>parentNode</code>、<code>parentElement</code>和<code>closest</code>;</li> <li>向下获取,比如<code>querySelector()</code>、<code>querySelectorAll()</code>、<code>children</code>、<code>firstChildren</code>、<code>lastChildren</code>和<code>childNodes</code></li> <li>兄弟元素(节点),比如<code>nextElementSibling</code>、<code>previousElementSibling</code>、<code>nextSibling</code>和<code>previousSibling</code></li> </ul> <p>如果文中有不对之处,或者你有更好的经验,欢迎在下面的评论中与我们一起分享。<strong>最后要说明的是,文章中有些图片来自互联网,如涉及侵权,烦请告之。</strong></p> <h2>扩展阅读</h2> <ul> <li><a href="http://www.xysjxj.com/quot;//zellwk.com/blog/dom-traversals/">Traversin" the DOM with JavaScript</a></li> <li><a href="http://www.xysjxj.com/quot;//www.kirupa.com/html5/traversing_the_dom.htm">Traversin" the DOM</a></li> <li><a href="//javascript.info/dom-navigation">Walking the DOM</a></li> <li><a href="//javascript.info/dom-nodes">DOM Tree</a></li> </ul> <div class="blog-author media"><a class="media-object" href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank"><img src="/sites/default/files/blogs/author/airen.jpg"></a><div class="media-body"><h3 class="media-heading"><a href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank">大漠</a></h3><div class="media-des">常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等韦德1946手机版客户端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/book-comment.html"" target="_blank">图解CSS3:核心技术与案例实战</a>》。</div></div></div> <p>如需转载,烦请注明出处:<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/dom-tree-and-traversals.html">https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/dom-tree-and-traversals.html</a></p> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/660.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">DOM</a></div><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/tags/663.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">DOM系列</a></div></div></div> Mon, 21 May 2018 16:26:37 +0000 Airen 2412 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/browser-and-the-dom.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><p>最近回过头来在<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/660.html">学习JavaScript中的DOM知识</a>,随着学习进度的向前推移,越发感觉DOM知识点较多。为了能更好的系统了解清楚DOM相关的知识,我打算重新将DOM的学习划入到<strong>DOM系列</strong>当中。那么今天将是这个系列的第一篇。你将了解到DOM是什么,为什么它有用,以及如何与将来在伟德1946手机版中所做的一切联系起来。</p>" <h2>JavaScript的API</h2> <p>在JavaScript语言的核心功能上提供的应用程序编程接口,简称<strong>API</strong>的功能会提供额外的超能力给我们使用。API是已经建立好的一套代码韦德娱乐平台,目的是让开发者可以实现除此之外很难甚至不可能实现的程序。它们的作用就像是已经制作好的家具套件对家居建设的作用一样 —— <strong>从一堆已经切好的木板开始组装一个书柜,显然比自己设计,寻找合适的木材,裁切至合适的大小和形状,找到合适大小的螺丝钉,然后组装成一个书柜要简单得多。</strong></p> <p>JavaScript的API通常分成两个类:</p> <p><img src="/sites/default/files/blogs/2018/1805/browser-dom-1.png" alt="" /></p> <p>而我们这个时候只关注浏览器API。</p> <p>浏览器API已经安装在浏览网页的浏览器中,而且能够从周围的计算机环境中揭露数据,或者做有用的复杂事情。比如我们要学习的DOM。它就是浏览器API中的文档对象模型API,允许你操作HTML和CSS,创建、移除和修改HTML,动态地应用新的样式到Web页面等等。</p> <h2>什么是HTML,CSS和JavaScript</h2> <p>在我们开始真正学习DOM之前,让我们快速地看看一些你可能已经知道的东西。</p> <p>首先,在HTML文档中放入的内容是围绕HTML、CSS和JavaScript进行的。我们将这三者视为平等的合作伙伴,将在你的浏览器中建立你所看到的:</p> <p><img src="/sites/default/files/blogs/2018/1805/browser-dom-2.png" alt="" /></p> <p>HTML、CSS和JavaScript三者都扮演着重要的角色,每个角色扮演的角色都非常不同。</p> <ul> <li><strong>HTML是一种标记语言</strong>,用来结构化我们的网页内容和赋予内容含义,例如定义段落、标题、和数据表,或在页面中嵌入图片和视频。</li> <li><strong>CSS 是一种样式规则语言</strong>,我们将样式应用于我们的 HTML 内容, 例如设置背景颜色和字体,在多个列种布局我们的内容。</li> <li><strong>JavaScript 是一种编程语言</strong>,允许你创建动态更新的内容,控制多媒体,图像动画,和一些其他的东西。好吧,虽然不是一切,但是它的神奇之处是你能够用几行JavaScript代码就能实现。</li> </ul> <h2>HTML定义了结构</h2> <p>HTML定义了页面的结构,通常包含了你所看到的内容:</p> <pre><code>&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta content="DOM, JavaScript, W3cplus" name="keywords"&gt; &lt;meta content="DOM系列,浏览器和DOM!" name="description"&gt; &lt;title&gt;LOL! Sea Otter! Little Kid!&lt;/title&gt; &lt;link href="style.css" rel="stylesheet"/&gt; &lt;/head&gt; &lt;body&gt; &lt;div id="container"&gt; &lt;img src="韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)_logo.png"/&gt; &lt;h1&gt;DOM系列学习!&lt;/h1&gt; &lt;p class="bodyText"&gt;开始学习DOM,这是一个有关于DOM学习的系列伟德1946手机版...&lt;p&gt; &lt;div class="submitButton"&gt;next&lt;/div&gt; &lt;/div&gt; &lt;script src="main.js"&gt;&lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p>HTML本身就像Meg Griffin的家庭成员一样,很无聊。如果你不知道Meg是谁,也懒得去Googl,那下图就是她的样式:</p> <p><img src="/sites/default/files/blogs/2018/1805/browser-dom-3.png" alt="" /></p> <p>无论如何,你也希望看到乏味的HTML文档。为了将你的内容从简单单调的东西转换成吸引人的东西,那就需要添加CSS。</p> <h2>CSS是来美化Web页面的</h2> <p>CSS是你的主要样式语言,允许你给HTML元素添加布局和美化,让Web页面更具吸引力:</p> <pre><code>body { font-family: "Arial"; background-color: #CCCFFF; } #container { margin-left: 30%; } #container img { padding: 20px; } #container h1 { font-size: 56px; font-weight: 500; } #container p.bodyText { font-size: 16px; line-height: 24px; } .submitButton { display: inline-block; border: 5px #669900 solid; background-color: #7BB700; padding: 10px; width: 150px; font-weight: 800; } </code></pre> <p>在很常的时间里,HTML和CSS成了你所需要的东西,用于创建一个漂亮的外观和功能的页面。有结构、有布局、有导航。甚至可以进行简单的交互,比如鼠标悬浮翻转的效果。是不是觉得很幸福,很美好。</p> <h2>JavaScript在Web页面上做什么</h2> <p>JavaScript是允许你在网页中实现复杂事情的一门编程语言 —— 每次当你浏览网页时不只是显示静态信息 —— 显示即时更新的内容,或者交互式的地图,或2D、3D图形动画,又或者自动播放视频等,你可以确信JavaScript参与其中。</p> <p>在确实查看一些代码之前,咱们先探索当你的页面上运行JavaScript的时候实际发生了什么?咱们先来简单的回顾当你在浏览器中加载一个Web网页时发生什么?浏览器中读取一个网页,其实浏览器就运行了你的代码,即组成Web页面的三大核心,HTML、CSS和JavaScript。而此时的浏览器好比一个工厂,获取原材料(代码)然后生产出一个产品(Web页面)。</p> <p><img src="/sites/default/files/blogs/2018/1805/browser-dom-4.png" alt="" /></p> <p>在 HTML 和 CSS 已经被集合和组装成一个网页后,浏览器的 JavaScript 引擎执行 JavaScript。这保证了当 JavaScript 开始运行时,网页的结构和样式已经在该出现的地方了。</p> <p>这是一个好事情,正如 JavaScript 的普遍用处是通过 DOM API(如之前提及的那样)动态地修改 HTML 和 CSS 来更新用户交界面。如果 JavaScript 在 HTML 和 CSS 加载完成之前加载运行,那么会发生错误。</p> <h2>初遇DOM</h2> <p>浏览器显示的是一个Web文档。更具体地说,要总结前面提到的HTML、CSS和JavaScript,它们共同创建了所显示的内容。浏览器呈现给用户的是一个表面上的东西,我们需要深挖一步,看看其幕后的东西 —— <strong>层次结构</strong>,浏览器也会使用它来理解正在发生的一切。</p> <p>这个层次结构被称为<strong>文档对象模型</strong>(Document Object Model),大家更喜欢叫它<strong>DOM</strong>。下面是对应前面示例的HTML的DOM视图:</p> <p><img src="/sites/default/files/blogs/2018/1805/browser-dom-7.png" alt="" /></p> <p>尽管这个结构示意图是简化后的,但适用于所有DOM结构的情况下。从上图可以看出来,DOM实际上是由HTML元素和其他的多种类型组成的。所有组成DOM的东西被称为<strong>DOM节点</strong>。</p> <p>这些节点可以是元素、属性、文本内容、注释、文档相关的内容以及其他你根本没有想到的东西。而我们更为关心的一种节点是元素类型,因为这是我们99%的时间所要处理的。</p> <p>你想要访问的每个HTML元素都有一个与之相关联的特定类型,并且所有这些类型都从组成所有节点的节点基础扩展:</p> <p><img src="/sites/default/files/blogs/2018/1805/browser-dom-8.png" alt="" /></p> <p>HTML元素位于一个以<strong><code>Node</code></strong>开头的链的末端,并在以一个类型结束之前继续使用<strong><code>HTML</code></strong>和<strong><code>HTMLElement</code></strong>(比如<code>HTMLDivElement</code>,<code>HTMLHeadingElement</code>等),它与HTML元素本身相匹配。在该链路的某些部分中,将介绍用于操作HTML元素的属性和方法。</p> <p>在我们使用DOM来修改HTML元素之前,让我们先来了解两个特殊对象。</p> <h2><code>window</code>对象</h2> <p>JavaScript语言最初是为Web浏览器创建的。从那时起,它已经发展成为一种有许多用途和平台的语言。而接下来所要说的浏览器就是这个平台之一。JavaScript规范调用了一个宿主环境(其实就是浏览器)。</p> <p>宿主环境为语言核心提供了特定于平台的对象和函数。Web浏览器提供了一种控制Web页面的方法。Node.js提供服务器端的特性等等。但我们接下来很长的一段时间要学习的和了解的都只是涉及Web浏览器控制页面的方法。</p> <p>有关于JavaScript在Web浏览器中运行时的鸟瞰图,可以用下图来描述:</p> <p><img src="/sites/default/files/blogs/2018/1805/browser-dom-5.png" alt="" /></p> <p>其中<code>window</code>是一个根对象。他有两个角色:</p> <ul> <li>首先,它是JavaScript代码的<strong>全局对象</strong></li> <li>其次,它代表了<strong>浏览器窗口</strong>,并提供了控制它的方法</li> </ul> <p><code>window</code> 对象表示一个包含DOM文档的窗口,其 <code>document</code> 属性指向窗口中载入的 DOM文档。在标签浏览器中,每个标签具有自己的 <code>window</code> 对象。也就是说,同一个窗口的标签之间不会共享一个 <code>window</code> 对象。其次,<code>window</code>对象有很多对应的方法、属性和事件。可以在浏览器开发者工具中,输入<code>window</code>,查看到<code>window</code>所有的东西,比如下图所示的(只截取了其中的一部分):</p> <p><img src="/sites/default/files/blogs/2018/1805/browser-dom-6.png" alt="" /></p> <p>前面提到,<code>window</code>可以作来一个全局对象,它可以下面这样使用:</p> <pre><code>function sayHi() { console.log('Hi, 大漠!') } // 全局函数可以作为`window`的属性访问 window.sayHi() </code></pre> <p>事实上,通过<code>window</code>对象你可以做很多的事情,比如访问当前URL、本地存储、屏幕信息、移动滚动条、设置状态栏文本等等。</p> <h2><code>document</code>对象</h2> <p><code>document</code>对象是一个很有趣的东西,这也是我们将会花一些时间集中探讨的一个对象。类似于<code>window</code>一样,咱们可以先在浏览器控制台上输入<code>document</code>,回车,看看输出的结果:</p> <p><img src="/sites/default/files/blogs/2018/1805/browser-dom-9.png" alt="" /></p> <p>正如上图所示,<code>document</code>对象是所有HTML元素的网关,这些元素构成了Web页面显示的内容。要记住的是,<code>document</code>对象并不仅仅表示HTML文档的只读版本,这一点在将来的伟德1946手机版中更有意义。这是一个双向的通道,你可随意的阅读和操作你的文件。</p> <p>通过JavaScript对DOM做出的任何更改都会在浏览器中反映出来,即页面的内容会立马更新。这意味着可以动态地添加、删除、移动和修改元素以及它们的属性,也可以给元素添加一些内联的CSS样式以及执行各种其他的功能。如果你想要使用JavaScript,可以在HTML中通过<code>&lt;script&gt;</code>标签来添加JavaScript代码或引用<code>.js</code>文件。如果使用得当,这是一个相当强大的功能。例如:</p> <pre><code>// 修改`body`元素的背景色为红色 document.body.style.backgroundColor = 'red' // 1s之后再把body元素的背景色变成黑色 setTimeout(() =&gt; { document.body.style.backgroundColor = 'black' }, 1000) </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/browser-dom-10.gif" alt="" /></p> <p>这里使用<code>document.body.style</code>,但事实上<code>document</code>还有很多属性和方法。相关的属性和方法可有通过下面的规范来查阅:</p> <ul> <li><a href="http://www.xysjxj.com/quot;//en.wikipedia.org/wiki/World_Wide_Web_Consortium">W3C</a>中<" href="http://www.xysjxj.com/quot;//www.w3.org/TR/dom">DOM规范</a></li>" <li><a href="http://www.xysjxj.com/quot;//en.wikipedia.org/wiki/WHATWG">WhatWG</a>中<" href="http://www.xysjxj.com/quot;//dom.spec.whatwg.org/">DOM规范</a></li>" </ul> <p>事实上,这是两组人,他们的意见并不总是相同的,所以这两套看上去就是两套标准。但它们之间又非常相似,最终会合并。</p> <p>在过去,根本没有标准 —— 每个浏览器都实现了它想要的功能。不同的浏览器对相同的东西有不同的集合、方法和属性,开发人员必须为每个浏览器做兼容处理,编写不同的代码,这是非常蛋疼的时代。</p> <p>即使是现在,我们有时候也能遇到使用浏览器特定性属笥的旧代码,并在不兼容的情况下工作。但是,在本系列伟德1946手机版中,我们将使用现代码的东西,不将考虑任何浏览器兼容的事情。</p> <p><code>document</code>对象的另一个方面内容是与事件相关的。这个将会放到后续的内容来介绍。这里不做过多的阐述。</p> <h2>总结</h2> <p>DOM是用于处理HTML文档的最重要的功能。它将HTML、CSS和JavaScript联系起来。它还提供了对<code>document</code>和<code>window</code>对象的访问级别。</p> <p>这篇文章只是了解了DOM中最简单,最基础的一部分。实际上,使用它的功能与你的Web文档交互是更大更有趣的部分。不过这将是后面要了解的东西,因为在深入探讨这部分之前,我们需要先了解DOM的一些基础和概念,只有这样才能发挥DOM的强大优势,更好的帮助我们操作Web页面或者说Web应用程序。</p> <p>我是这方面的初学者,也是初次整理DOM系列相关的伟德1946手机版,如果有不对之处,还请各位多多指正。当然,如果您愿意分享这方面的经验,我们期待您在下面的评论中分享。帮助我和像我这样的初学者更好的成长。</p> <p><strong>最后需要声明,文章中有些图片是通过Google搜索来的,在使用的地方未标注出处地址,如果有侵权或影响到您的利益,烦请告之,我将会相应的删除。跪谢!</strong></p> <p>提前预告一下,下一节,我们将围绕着DOM树进行学习和探讨,感兴趣的同学,欢迎持续关注这个系列的相关更新!</p> <div class="blog-author media"><a class="media-object" href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank"><img src="/sites/default/files/blogs/author/airen.jpg"></a><div class="media-body"><h3 class="media-heading"><a href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank">大漠</a></h3><div class="media-des">常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等韦德1946手机版客户端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/book-comment.html"" target="_blank">图解CSS3:核心技术与案例实战</a>》。</div></div></div> <p>如需转载,烦请注明出处:<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/browser-and-the-dom.html">https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/browser-and-the-dom.html</a></p> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/660.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">DOM</a></div><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/tags/663.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">DOM系列</a></div></div></div> Thu, 17 May 2018 14:20:59 +0000 Airen 2411 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/understanding-the-dom.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><blockquote> <p>特别声明,本文整个思路来源于@Tania Rascia的系列文章《<a href="http://www.xysjxj.com/quot;//www.digitalocean.com/community/tutorial_series/understanding-the-dom-document-object-model">Understandin" the DOM</a>》。</p> </blockquote> <p>DOM是Document Object Model的简称,是网站具有交互性的重要组成部分。它是一个接口,允许编程语言操作网站的内容、结构和样式。JavaScript是浏览器中连接到DOM的客户端脚本语言。</p> <p>欲要更好的操作好Web网站,我们就很有必要的理解DOM。而且这也是学习JavaScript很重要的部分之一。接下来我们将从以下几个部分来展开对DOM的理解和学习。</p> <ul> <li>DOM简介</li> <li>理解DOM树和节点</li> <li>如何访问DOM中的元素</li> <li>如何遍历DOM</li> <li>如何更改DOM</li> </ul> <p>那我们开始吧!</p> <h2>DOM简介</h2> <p>Web网站都会有一些事件交互的行为,比如说图像幻灯片之间进行旋转,当用户试图提交不完整表单时显示错误信息,或者切换导航菜单等,这一切的一切其实都是JavaScript访问和操作DOM的结果。这样一来,我们就很有必要去了解DOM是什么,如果处理DOM,以及HTML源代码和DOM之间的区别等等。</p> <blockquote> <p>尽管DOM与语言并没有太大关系,或者说其是创建独立于特定的编程语言,但在我们这篇文章中所要聊的都是关于JavaScript对HTML中DOM的操作。</p> </blockquote> <h3>先决条件</h3> <p>为了有效地了解DOM以及它与Web工作的关系,建议你对HTML和CSS有一定的了解,而且熟悉基本的JavaScript语法和代码结构。这样有益于帮助大家更好的理解后续的内容。</p> <h3>DOM是什么</h3> <p>在最基本的层面上来说,Web网站是由一个HTML文档组成。我们使用的浏览网站的浏览器是一个解释HTML和CSS的程序,它把样式、内容和结构呈现给你 —— <strong>也就是你在浏览器看到的样子</strong>。</p> <blockquote> <p>对于浏览器打下一个网址,它所经历的事情和所涉及到的知识可多了,因此也有一个最经典的面试题:<a href="http://www.xysjxj.com/quot;//www.google.com.hk/search?newwindow=1&amp;safe=strict&amp;ei=Miz4WvWfNoqw_QaX94ewCg&amp;q=%E6%B5%8F%E8%A7%88%E5%99%A8%E6%89%93%E5%BC%80%E4%B8%80%E4%B8%AA%E7%BD%91%E9%A1%B5%E6%97%B6%E9%83%BD%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88&amp;oq=%E6%B5%8F%E8%A7%88%E5%99%A8%E6%89%93%E5%BC%80%E4%B8%80%E4%B8%AA%E7%BD%91%E9%A1%B5%E6%97%B6%E9%83%BD%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88&amp;gs_l=psy-ab.3..35i39k1l6.68497.68922.0.69267.2.2.0.0.0.0.0.0..1.0....0...1c.1j2.64.psy-ab..1.1.350.6...351.va5-X3ikvqg">浏览器打开一个网页时都发生了什么?</a>。</p>" </blockquote> <p>除了解析HTML结构和CSS样式这外,浏览器还创建了文档对象模型,也就是我们要说的DOM。该模型允许JavaScript以对象的形式访问网站文档的文本内容和元素。</p> <p>JavaScript是一种交互式语言,通过它可以更容易地理解新概念。让我们来创建一个最简单的Web页面。创建一个<code>index.html</code>文件,并将其保存在一个新的项目目录中。</p> <pre><code>&lt;!DOCTYPE html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;title&gt;Learning the DOM&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;h1&gt;Document Object Model&lt;/h1&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p>熟悉HTML的都知道,上面这个代码是Web页面最基础的架子。它包含了网站文档的最基本东西:文档类型(<code>DOCTYPE</code>)、带有<code>&lt;head&gt;</code>和<code>&lt;body&gt;</code>的<code>&lt;html&gt;</code>标签。</p> <p>出于我们的目的,我将使用Chrome浏览器,当然你也可以使用你自己喜欢的浏览器来获得类似的输出。使用Chrome浏览器,打开刚才创建的<code>index.html</code>文件。在浏览器看到的Web页面将是一个最简单的页面,就只有一个标题 —— “Document Object Model” 。通过浏览器的开发者工具,在“Elements”选项卡下,你将看到<code>index.html</code>页面对应的DOM。比如下图所示:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-1.png" alt="" /></p> <p>在这种情况下,它看起来与我们刚刚编写的HTML源代码完全相同 —— 一个<code>DOCTYPE</code>,以及几个HTML标签元素。浏览器开发者工具中,将鼠标悬停在元素上时,Web页面中将会突出显示相应的元素内容。HTML元素左侧的小箭头,允许您切换嵌套元素的视图(将折叠的部分展开显示):</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-2.gif" alt="" /></p> <h3><code>document</code>对象</h3> <p><code>document</code>对象是一个内置对象,它有许多属性和方法,可以用来访问和修改Web页面。为了理解如何使用DOM,你必须了解对象如何在JavaScript中工作。如果你对对象的概念一点都不了解,那么可以先查阅一些<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/546.html">JavaScript中对象</a>相关的知识。</p>" <p>在浏览器中访问前面创建的<code>index.html</code>页面,并且使用开发者工具进入到<code>Console</code>选项卡中。然后在开发者工具中输入<code>document</code>,并按回车键。将看到的输出内容与<code>Elements</code>选项卡中看到的内容相同。</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-3.png" alt="" /></p> <p>这里键入<code>document</code>只是为了更好的帮助大家巩固<code>document</code>对象是什么以及如何修改它。后续的内容你将会发现我在此处所说的。</p> <h3>DOM和HTML源代码的区别是什么?</h3> <p>目前,在这个示例中,HTML源代码和DOM似乎是完全相同的。在两个实例中,浏览器生成的DOM将与HTML源代码不同:</p> <ul> <li>DOM被客户端(可能是浏览器)JavaScript修改</li> <li>浏览器会自动修复源代码中的错误</li> </ul> <p>接下来简单的演示如何通过客户端JavaScript修改DOM。比如在开发者工具中输入<code>document.body</code>,得到的结果如下图所示:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-4.png" alt="" /></p> <p><code>document</code>是一个对象,<code>body</code>是我们用点符号(<code>.</code>)访问的对象属性。输入<code>document.body</code>将会在控制台中输入<code>body</code>元素自身和它里面的一切。</p> <p>在控制台中,我们可以在这个Web页面上改变<code>body</code>对象的一些属性。比如修改<code>style</code>的属性,将页面的背景颜色改变紫红色。只需要控制台中输入:</p> <pre><code>document.body.style.backgroundColor = 'fuchsia' </code></pre> <p>控制台上输入上面的代码回车后,将看到Web页面的实时更新,<code>body</code>的<code>background-color</code>也将变成<code>fuchsia</code>。结果如下:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-5.gif" alt="" /></p> <p>这个时候在键入<code>document.body</code>之后,你将看到DOM发生了变化:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-6.png" alt="" /></p> <p>上面输入的是JavaScript代码,将<code>fuchsia</code>分配给<code>body</code>元素的<code>background-color</code>,而且现在变成是DOM的其中一部分。</p> <p>但是,你现在在页面中用鼠标右键单击,并在出来的菜单项中选择“查看页面源代码”选项。你会注意到,网站源代码并不包含通过JavaScript添加的新样式属性。网站的来源不会改变,也不会受到客户端JavaScript的影响。如果你刷新页面,我们在控制台添加的新代码将会消失。</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-7.gif" alt="" /></p> <p>当源代码中出现错误时,DOM可能具有不同于HTML源代码的另一个实例。其中一个常见的例子是<code>table</code>标记 —— 就是需要一个<code>tbody</code>标签,但很多开发人员,往往在写HTML代码时忘了添加。浏览器将会自动更正错误并添加<code>tbody</code>标签。另外DOM还将修复未关闭的HTML标签。</p> <p>经过上面的学习,知道怎么访问文档对象,以及如何在浏览器的开发者工具中使用JavaScript来更新文档对象的属性。另外知道了HTML源代码和DOM之间的区别之处。有关于DOM更深入的信息,还可以<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model">查看Mozilla上有关于文档对象模型(DOM)</a>资料。</p>" <h2>理解DOM树和节点</h2> <p>DOM通常被称为<strong>DOM树</strong>,它由称为节点的对象树组成。在介绍DOM的过程中,了解到了文档对象是什么,如何访问文档对象并使用控制台修改其属性,以及HTML源代码和DOM之间的区别。</p> <p>接下来将会回顾一些HTML相关的术语,这对于理解如何使用JavaScript处理DOM非常重要。除此之外,接下来的内容将帮助我们了解DOM树,DOM节点是什么,以及如何识别最常见的节点类型。</p> <h3>HTML术语</h3> <p>理解HTML和JavaScript术语对于理解如何处理DOM非常重要。让我们简要的回顾一下一些HTML相关的术语。</p> <p>先来看一下这个HTML元素:</p> <pre><code>&lt;a href="http://www.xysjxj.com/quot;index.html"&gt;Home&lt;/a&gt" </code></pre> <p>这是一个锚元素,它指向<code>index.html</code>。</p> <ul> <li><code>a</code>是一个HTML标签</li> <li><code>href</code>是一个HTML标签属性</li> <li><code>index.html</code>是属性值</li> <li><code>Home</code>是一个文本</li> </ul> <p>在开始和闭合标签之间的所有内容组合生成整个HTML元素。把这个链接代码放到<code>index.html</code>中之后就变成下面这样:</p> <pre><code>&lt;!DOCTYPE html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;title&gt;Learning the DOM&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;h1&gt;Document Object Model&lt;/h1&gt; &lt;a id="nav" href="http://www.xysjxj.com/quot;index.html"&gt;Home&lt;/a&gt" &lt;/body&gt; &lt;/html&gt; </code></pre> <p>使用JavaScript访问元素的最简单方法是<code>id</code>属性。所以在链接中添加了个<code>id</code>为<code>nav</code>的值。</p> <p>这个时候,使用<code>getElementById()</code>方法来访问所需要的元素。比如在控制台中输入下面的内容:</p> <pre><code>document.getElementById('nav') </code></pre> <p>通过上面的代码,就获取到了<code>index.html</code>中<code>id</code>为<code>nav</code>的元素:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-8.png" alt="" /></p> <p>正如上面所演示的一样,使用<code>getElementById()</code>可以选择到我们想要的元素。如果我们想多次访问这个<code>nav</code>链接时就得多次输入那个对象和方法,因此为了更容易的使用它,我们可以将其放入一个变量中。比如:</p> <pre><code>let navLink = document.getElementById('nav') </code></pre> <p><code>navLink</code>变量包含我们的锚元素。从这里,我们可以很容易的修改它的属性和值。例如,我们可以通过更改<code>href</code>属性来更改链接的位置:</p> <pre><code>navLink.href = 'https://wwww.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com' rel="nofollow" </code></pre> <p>我们也可以使用<code>textContent</code>属性来改变锚元素的文本内容:</p> <pre><code>navLink.textContent = '点击这里访问W3cplus' </code></pre> <p>这个时候在控制台中输入<code>navLink</code>之后,你看到效果如下,同时对应的Web页面也有所更改:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-9.png" alt="" /></p> <p>同样的,如果你刷新页面,这一切都将恢复到最初的状态。</p> <p>此时,你应该了解了如何使用<code>document</code>的方法来访问元素,如何将选到的元素赋值给一个变量,以及如何修改元素中的属性和值。</p> <h3>DOM树和节点</h3> <p>在DOM中,所有的项都定义为<strong>节点</strong>。DOM中的节点类型有很多种,但有三个主要的节点类型是我们经常使用的:</p> <ul> <li><strong>元素(<code>Element</code>)</strong>节点</li> <li><strong>文本(<code>Text</code>)</strong>节点</li> <li><strong>注释(<code>Comment</code>)</strong>节点</li> </ul> <p>HTML中的元素被称为DOM中的<strong>元素节点</strong>。元素之外的任何单独的文本都是<strong>文本节点</strong>,而HTML注释则是一个<strong>注释节点</strong>。除了这三种节点类型之外,<code>document</code>也是一个<strong>文档节点</strong>,而且它是其他所有节点的根节点。</p> <p>DOM是由嵌套节点的树结构组成,它通常被称为<strong>DOM树</strong>。你可能熟悉是族谱,它由父母、孩子和兄弟姐妹组成。DOM中的节点也被称为<strong>父节点</strong>、<strong>子节点</strong>和<strong>兄弟节点</strong>,这一切都取决于它们与其他节点的关系。</p> <p>为了演示,咱们修改一下前面的<code>index.html</code>文件。将添加文本、注释和元素节点。</p> <pre><code>&lt;!DOCTYPE html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;title&gt;Learning About Nodes&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;h1&gt;An element node&lt;/h1&gt; &lt;!-- A comment node --&gt; A text node. &lt;/body&gt; &lt;/html&gt; </code></pre> <p><code>html</code>元素节点是父节点。<code>head</code>和<code>body</code>是兄弟节点,也是<code>html</code>的子节点。<code>body</code>包含三个子节点,它们都是兄弟节点 —— <strong>节点的类型不会改变其嵌套的级别</strong>。</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-10.png" alt="" /></p> <blockquote> <p>在使用HTML生成的DOM时,HTML源代码的缩进会创建许多空的文本节点,这在开发者工具中的<code>Elements</code>选项卡中是不可见的。更详细的内容可以阅读<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace_in_the_DOM">DOM中的空白</a>相关的资料。</p>" </blockquote> <h3>确定节点类型</h3> <p>文档中的每个节点都有一个节点类型,通过<code>nodeType</code>属性可以访问该节点类型。Mozilla提供了<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/Node/nodeType">所有节点类型常量的最新列表</a>。下面是我们最常见节点类型:</p>" <table> <thead> <tr> <th>节点类型</th> <th>节点常量值</th> <th>示例</th> </tr> </thead> <tbody> <tr> <td><code>ELEMENT_NODE</code></td> <td><code>1</code></td> <td>比如 <code>&lt;body&gt;</code>元素</td> </tr> <tr> <td><code>TEXT_NODE</code></td> <td><code>3</code></td> <td>不属于元素的文本</td> </tr> <tr> <td><code>COMMENT_NODE</code></td> <td><code>8</code></td> <td><code>&lt;!-- A comment node --&gt;</code></td> </tr> </tbody> </table> <p>在开发者工具中的<code>Elements</code>选项卡中,你可能会注意到,每当单击并高亮显示DOM中的任一行时,将会在它的旁边出现<code>==$0</code>的值。</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-11.png" alt="" /></p> <p>这是一种非常方便的方法,可以在控制台中输入<code>$0</code>来访问当前元素。</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-12.gif" alt="" /></p> <p>在控制台中,使用<code>nodeType</code>属性可以获取当前选定节点的节点类型,比如:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-13.gif" alt="" /></p> <p>选择<code>body</code>元素后,<code>$0.nodeType</code>输出的值是<code>1</code>,可以看到它与<code>ELEMENT_NODE</code>相关。对于注释和文本执行相同的操作,它们将分别输出的值是<code>8</code>和<code>3</code>。</p> <p>当你知道如何访问元素时,你就可以看到对应的节点类型。比如:</p> <pre><code>document.body.nodeType; // =&gt; 1 </code></pre> <p>除了<code>nodeType</code>之外,你还可以使用<code>nodeValue</code>属性获取文本或注释节点的值,也可以使用<code>nodeName</code>属性来获取元素的标签名。</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-14.png" alt="" /></p> <h3>用事件修改DOM</h3> <p>到目前为止,我们只看到了如何在浏览器开发者工具的控制台中修改DOM,这样一来只是暂时的修改,因为只要刷新了页面,每次更改都会丢失。比如前面的示例,在控制台中修改<code>body</code>的<code>background-color</code>。那么接下来,我们可以结合我们所学到的内容,在页面中创建一个交互按钮,当点击按钮后再修改<code>body</code>的<code>background-color</code>。</p> <p>同样在<code>index.html</code>中,咱们添加一个<code>id</code>为<code>changeBackground</code>的<code>&lt;button&gt;</code>元素。并且在<code>&lt;/body&gt;</code>前添加一个<code>&lt;script&gt;&lt;/script&gt;</code>标签,主要用来放置操作DOM所需要的JavaScript代码。</p> <pre><code>&lt;body&gt; &lt;h1&gt;Document Object Model&lt;/h1&gt; &lt;button id="changeBackground"&gt;Change Background Color&lt;/button&gt; &lt;script&gt; // JavaScript操作DOM的代码写在这里 &lt;/script&gt; &lt;/body&gt; </code></pre> <p>JavaScript中的<strong>事件</strong>是用户所采取的操作。当用户将鼠标悬浮在某个元素上,或者单击某个元素,或者按下键盘上的特定键时,这些都是<strong>事件类型</strong>。在我们这个示例中,希望监听<code>button</code>上的事件,当用户单击它时执行操作,也就是改变<code>body</code>的<code>background-color</code>。我们可以在按钮中添加一个<code>click</code>事件来实现这个效果。</p> <p>首先通过<code>getElementById()</code>方法找到这个<code>button</code>元素,并将其赋值给一个变量:</p> <pre><code>let button = document.getElementById('changeBackground'); </code></pre> <p>使用<code>addEventListener()</code>方法来监听<code>button</code>上的<code>click</code>事件,并执行一次单击后的函数。</p> <pre><code>button.addEventListener('click', () =&gt; { // 单击按钮对应要做的事情 }) </code></pre> <p>最后在函数内部,将使用前面的代码来修改<code>body</code>的<code>background-color</code>:</p> <pre><code>document.body.style.backgroundColor = 'fuchsia'; </code></pre> <p>这样把JavaScript的代码一起放置在<code>&lt;script&gt;</code>标签中:</p> <pre><code>let button = document.getElementById('changeBackground'); button.addEventListener('click', () =&gt; { document.body.style.backgroundColor = 'fuchsia'; }) </code></pre> <p>保存你的<code>index.html</code>文件,然后在浏览器中重新打开这个页面。当你点击按钮时,将会触发事件发生 —— 即修改<code>body</code>的背景色为<code>fuchsia</code>:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-15.gif" alt="" /></p> <h2>如何访问DOM元素</h2> <p>为了能够熟练地访问DOM中的元素,有必要了解<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/blog/tags/288.html">CSS选择器</a>、语法和术语以及对HTML元素的理解。接下来将介绍几种访问DOM元素的方法:通过<code>ID</code>、<code>class</code>、标签和查询选择器。</p>" <p>下面是我们将会介绍的五种访问DOM元素的概述。</p> <table> <thead> <tr> <th>获取方式</th> <th>选择器语法</th> <th>方法</th> </tr> </thead> <tbody> <tr> <td><code>ID</code></td> <td><code>#demo</code></td> <td><code>getElementById()</code></td> </tr> <tr> <td><code>class</code></td> <td><code>.demo</code></td> <td><code>getElementsByClassName()</code></td> </tr> <tr> <td><code>Element</code></td> <td><code>h1</code></td> <td><code>getElementsByTagName()</code></td> </tr> <tr> <td>选择器(单个)</td> <td>&nbsp;</td> <td><code>querySelector()</code></td> </tr> <tr> <td>选择器 (所有)</td> <td>&nbsp;</td> <td><code>querySelectorAll()</code></td> </tr> </tbody> </table> <p>在学习DOM时,在你自己的电脑上键入你想要的示例很重要,这样可以确保你理解并保留你所学习的信息。</p> <p>为了更好的帮助大家理解如何访问DOM中的元素,把<code>index.html</code>文件做一下修改。修改后的代码如下:</p> <pre><code>&lt;!DOCTYPE html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;meta charset="utf-8"&gt; &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt; &lt;title&gt;Accessing Elements in the DOM&lt;/title&gt; &lt;style&gt; html { font-family: sans-serif; color: #333; } body { max-width: 500px; margin: 0 auto; padding: 0 15px; } div, article { padding: 10px; margin: 5px; border: 1px solid #dedede; } &lt;/style&gt; &lt;/head&gt; &lt;body&gt; &lt;h1&gt;Accessing Elements in the DOM&lt;/h1&gt; &lt;h2&gt;ID (#demo)&lt;/h2&gt; &lt;div id="demo"&gt;Access me by ID&lt;/div&gt; &lt;h2&gt;Class (.demo)&lt;/h2&gt; &lt;div class="demo"&gt;Access me by class (1)&lt;/div&gt; &lt;div class="demo"&gt;Access me by class (2)&lt;/div&gt; &lt;h2&gt;Tag (article)&lt;/h2&gt; &lt;article&gt;Access me by tag (1)&lt;/article&gt; &lt;article&gt;Access me by tag (2)&lt;/article&gt; &lt;h2&gt;Query Selector&lt;/h2&gt; &lt;div id="demo-query"&gt;Access me by query&lt;/div&gt; &lt;h2&gt;Query Selector All&lt;/h2&gt; &lt;div class="demo-query-all"&gt;Access me by query all (1)&lt;/div&gt; &lt;div class="demo-query-all"&gt;Access me by query all (2)&lt;/div&gt; &lt;script&gt; &lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p>在<code>index.html</code>文件中的<code>&lt;script&gt;</code>中将使用不同的<code>document</code>方法来访问DOM中的元素。具体的方法先不看,保存上面的代码之后,在浏览器中看到的页面效果有点类似下图这样:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-16.png" alt="" /></p> <p>我们将使用上面表格中的不同方法来访问<code>index.html</code>中的元素。</p> <h3>使用<code>ID</code>访问元素</h3> <p>在DOM中访问单个元素的最简单方法是通过它唯一的ID来访问。可以使用<code>document</code>对象中的<code>getElementById()</code>方法来访问元素中的<code>ID</code>:</p> <pre><code>document.getElementById(); </code></pre> <p>为了通过访问<code>ID</code>获取元素,所以HTML元素必须具有ID属性。在我们的示例中,有一个<code>id</code>名为<code>demo</code>的<code>div</code>元素:</p> <pre><code>&lt;div id="demo"&gt;Access me by ID&lt;/div&gt; </code></pre> <p>在控制台中,通过<code>document.getElementById('demo')</code>来获取<code>id</code>为<code>demo</code>的元素,并且将其赋值给<code>demoId</code>变量:</p> <pre><code>const demoId = document.getElementById('demo') </code></pre> <p>在控制台通过<code>console.log(demoId)</code>就可以输出我们选中的元素:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-17.png" alt="" /></p> <p>为了验证我们选择的元素是对的,可以通过改变元素的<code>border</code>为<code>red</code>来确定:</p> <pre><code>demoId.style.border = '1px solid red' </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-18.png" alt="" /></p> <p>上面的代码我们都是在浏览器控制台中输入的,前面也提到过了,刷新浏览器就会丢失,如果你想不让其丢失,你可以把相关的代码放置在<code>&lt;script&gt;</code>标签内。</p> <p>通过<code>ID</code>访问元素是在DOM中快速获取元素的一种有效方法。然而,它也有缺点的。<code>ID</code>必须始终是唯一的,因此<code>getElementById()</code>方法只能访问到单个元素。如果你想要在整个页面中添加一个功能,那么你的代码将很快变得非常令人讨厌。</p> <h3>使用<code>class</code>来访问DOM元素</h3> <p>使用<code>class</code>属性用于访问DOM中的一个或多个指定元素。可以使用<code>getElementsByClassName()</code>方法获取给定类名的所有元素。</p> <p>比如我们的<code>index.html</code>文件中有两个<code>div</code>都带有类名<code>demo</code>:</p> <pre><code>&lt;div class="demo"&gt;Access me by class (1)&lt;/div&gt; &lt;div class="demo"&gt;Access me by class (2)&lt;/div&gt; </code></pre> <p>同样的,我们在控制台中使用<code>getElementsByClassName()</code>方法来访问<code>class</code>为<code>demo</code>的元素,并将其赋值给一个变量<code>demoClass</code>:</p> <pre><code>const demoClass = document.getElementsByClassName('demo') </code></pre> <p>此时,你可能认为可以像使用<code>ID</code>那样修改元素。如果我们也像前面介绍<code>ID</code>访问DOM元素的示例一样,给使用<code>class</code>的<code>div</code>添加一个<code>border</code>样式,比如我们这里将其添加一个<code>orange</code>的边框。那么结果将会如何呢?</p> <pre><code>demoClass.style.border = '1px solid orange' </code></pre> <p>运行的结果并不如你所期待的,控制台将会报出错误信息:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-19.png" alt="" /></p> <p>会报错是因为使用<code>getElementsByClassName()</code>方法并没有得到一个元素,而是得到一个类数组一样的元素。</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-20.png" alt="" /></p> <p>从上图中可以看出来,我们得到的是一个<code>HTMLCollection</code>。</p> <p>在学习《<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/dom-dynamic-collection.html">JavaScript中的DOM动态集合</a>》一节中,我们知道,<code>HTMLCollection</code>是一个类数组,可以使用<code>[]</code>或者<code>item()</code>来访问。而且从上图中我们可以看到控制台中输出的结果带有一个<code>length</code>值。那么我们想要访问到其中的元素就必须使用索引号来访问JavaScript数组。也就是说,我们可以使用<code>[0]</code>的方式来访问到第一个元素,比如使用<code>demoClass[0]</code>:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-21.png" alt="" /></p> <p>既然通过<code>[0]</code>的方式能访问到具体元素,那么就可以像<code>ID</code>选择器中一样,给元素添加样式。比如:</p> <pre><code>demoClass[0].style.border = '1px solid orange' </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-22.png" alt="" /></p> <p>通常我们使用<code>class</code>访问元素时,希望选中文档中的所有指定类的元素,而不像上图一样仅仅是其中的一个。刚才也提到了<code>getElementsByClassName()</code>方法返回的是一个<code>HTMLCollection</code>,是一个类数组,那么我们可以通过<code>for</code>循环来遍历数组中的每一项,让我们不再只选择一个元素:</p> <pre><code>for (let i = 0, len = demoClass.length; i &lt; len; i++){ demoClass[i].style.border = '1px solid orange' } </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-23.png" alt="" /></p> <p>现面<code>index.html</code>页面中含有<code>demo</code>类名的元素都添加了<code>border</code>为<code>1px solid orange</code>的样式。正如上图所示的效果。</p> <h3>通过标签访问元素</h3> <p>访问页面上多个元素除了上面提到的<code>getElementsByClassName()</code>方法之外,还可以使用<code>getElementsByTagName()</code>方法来访问。不同的是前者通过访问指定的<code>class</code>来获取元素,而后者是通过访问指定的HTML标签来访问元素。比如我们使用<code>getElementsByTagName()</code>方法来访问<code>index.html</code>中所有的<code>&lt;article&gt;</code>标签,并将其赋值给变量<code>demoTag</code>:</p> <pre><code>const demoTag = document.getElementsByTagName('article') console.log(demoTag) </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-24.png" alt="" /></p> <p>同样的,<code>getElementsByTagName()</code>类似于<code>getElementsByClassName()</code>返回的也是一个<code>HTMLCollection</code>类数组。也就是说,如果我们要给<code>index.html</code>中所有<code>&lt;article&gt;</code>元素添加一个<code>blue</code>的<code>border</code>,也要使用<code>for</code>循环来遍历:</p> <pre><code>for (let i = 0, len = demoTag.length;i &lt; len; i++) { demoTag[i].style.border = '1px solid blue' } </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-25.png" alt="" /></p> <h3>查询选择器</h3> <p>如果你有使用jQuery API的相关经验的话,你可能会熟悉jQuery使用CSS选择器访问DOM的方法。</p> <pre><code>$('#demo'); $('.demo'); $('article'); </code></pre> <p>时至今日,我们在JavaScript中可以使用<code>querySelector()</code>和<code>querySelectorAll()</code>方法执行类似jQuery API选择DOM元素的方法.</p> <p>如果我们要访问单个元素,可以使用<code>querySelector()</code>方法。比如我们要访问<code>index.html</code>中<code>id</code>为<code>demo-query</code>的元素,我们就可以这样操作:</p> <pre><code>const demoQuery = document.querySelector('#demo-query') </code></pre> <p>对于多个元素的选择器(比如<code>class</code>或标签),使用<code>querySelector()</code>将返回与查询匹配的第一个元素。如查要选择所有元素,则可以使用<code>querySelectorAll()</code>方法。比如,我们可以使用<code>querySelectorAll()</code>方法访问<code>index.html</code>中所有<code>class</code>为<code>demo-query-all</code>的元素:</p> <pre><code>const demoQueryAll = document.querySelectorAll('.demo-query-all') </code></pre> <p><code>querySelectorAll()</code>方法有点类似于<code>getElementsByClassName()</code>方法,只不过其返回的是一个<code>NodeList</code>的类数组。同样的,如果要给所有的<code>class</code>为<code>demo-query-all</code>元素添加<code>green</code>边框效果,不能直接使用,需要使用类似于<code>forEach()</code>方法对类数组进行遍历:</p> <pre><code>demoQueryAll.forEach(query =&gt; { query.style.border = '1px solid green' }) </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-26.png" alt="" /></p> <p>使用<code>querySelector()</code>方法,在方法中的参数可以使用逗号<code>,</code>做为分隔符,比如<code>querySelector('div, article')</code>将匹配到文档中的第一个<code>div</code>元素以及第一个<code>article</code>元素。使用<code>querySelectorAll()</code>方法也可以如何操作,比如<code>querySelectorAll('div, article')</code>方法将选中文档中所有<code>div</code>和<code>article</code>元素。</p> <p>使用查询选择器方法非常强大,因为你可以像在CSS文件中一样访问DOM中的任何元素或元素组。有关选择器的完整列表,可以查看<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors">Mozilla提供的CSS选择器相关资料</a>。</p>" <p>上面的代码都是在浏览器的控制台中输入的,一旦你刷新浏览器,所有的JavaScript将失效。如果你想让效果永久保留,你可以在<code>index.html</code>的<code>&lt;script&gt;</code>标签中输入上述的JavaScript代码,或者是单独创建一个<code>.js</code>文件,然后在<code>&lt;script&gt;</code>标签中使用<code>src</code>属性引入你创建的<code>.js</code>文件。这个已经是JavaScript的一些基础知识,想必你应该很清楚了,这里不再阐述。</p> <h2>如何遍历DOM元素</h2> <p>接下来咱们学习如何在DOM树上移动DOM,比如向上、向下移动DOM,比如从一个分支移到另一个分支。学习这些知识是理解如何使用JavaScript和HTML的关键。</p> <p>下面我们就围绕着JavaScript如何通过父、子和兄弟属性遍历DOM。</p> <p>为了能更好的阐述我们要学习的内容,先把<code>index.html</code>文件的代码改成下面这样:</p> <pre><code>&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;Learning About Nodes&lt;/title&gt; &lt;style&gt; * { border: 2px solid #dedede; padding: 15px; margin: 15px; } html { margin: 0; padding: 0; } body { max-width: 600px; font-family: sans-serif; color: #333; padding: 10px;} &lt;/style&gt; &lt;/head&gt; &lt;body&gt; &lt;h1&gt;Shark World&lt;/h1&gt; &lt;p&gt;The world's leading source on &lt;strong&gt;shark&lt;/strong&gt; related information.&lt;/p&gt; &lt;h2&gt;Types of Sharks&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Hammerhead&lt;/li&gt; &lt;li&gt;Tiger&lt;/li&gt; &lt;li&gt;Great White&lt;/li&gt; &lt;/ul&gt; &lt;/body&gt; &lt;script&gt; const h1 = document.getElementsByTagName('h1')[0]; const p = document.getElementsByTagName('p')[0]; const ul = document.getElementsByTagName('ul')[0]; &lt;/script&gt; &lt;/html&gt; </code></pre> <p>浏览器刷新之后,看到的效果如下:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-27.png" alt="" /></p> <h3>根节点</h3> <p><code>document</code>对象是DOM中每个节点的根。这个对象实际上是<code>window</code>对象的一个属性,它是表示浏览器中全局顶级对象。<code>window</code>对象可以访问工具栏、窗口的宽度和高度、提示和警报等信息。<code>document</code>由<code>window</code>内的内容组成。</p> <p>下面是由每个文档所包含的根元素组成的图表。即使将一个空白HTML文件加载到浏览器中,这三个节点也将被添加到DOM中。</p> <table> <thead> <tr> <th>属性</th> <th>节点</th> <th>节点类型</th> </tr> </thead> <tbody> <tr> <td><code>document</code></td> <td><code>#document</code></td> <td><code>DOCUMENT_NODE</code></td> </tr> <tr> <td><code>document.documentElement</code></td> <td><code>html</code></td> <td><code>ELEMENT_NODE</code></td> </tr> <tr> <td><code>document.head</code></td> <td><code>head</code></td> <td><code>ELEMENT_NODE</code></td> </tr> <tr> <td><code>document.body</code></td> <td><code>body</code></td> <td><code>ELEMENT_NODE</code></td> </tr> </tbody> </table> <p>由于<code>html</code>、<code>head</code>和<code>body</code>三个元素是任何一个HTML文档必有的元素,可谓是非常常见的元素,所以它们在<code>document</code>上都有自己的属性。</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-28.png" alt="" /></p> <p>正如上图所示,你可以在浏览器的开发者工具中打印出上面表格中的四个属性。当然你也还可以测试<code>h1</code>、<code>p</code>和<code>ul</code>元素。因为在<code>script</code>标记中已将它们指定在对应的变量中,所以将返回元素。</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-29.png" alt="" /></p> <h3>父节点</h3> <p>DOM中的节点被称为父节点、子节点和兄弟节点,这取决于它们与其他节点的关系。任何节点的父节点都位于它之上的一个级别的节点,或者更接近于DOM中的<code>document</code>。DOM中有两个属性可以访问到父节点:<code>parentNode</code>和<code>parentElement</code>。</p> <p>在<code>index.html</code>中:</p> <ul> <li><code>html</code>是<code>head</code>、<code>body</code>和<code>script</code>的父节点</li> <li><code>body</code>是<code>h1</code>、<code>h2</code>、<code>p</code>和<code>ul</code>父节点,但不是<code>li</code>的父节点,那是因为<code>li</code>是<code>body</code>第二级节点(按照族谱关系来描述,<code>body</code>是<code>li</code>的爷爷节点,在这里常常被称为祖先节点)</li> </ul> <p>我们可以通过<code>p</code>的<code>parentNode</code>属性来获取到<code>p</code>的父节点。而其中<code>p</code>是一个变量,对应的是通过<code>document.getElementsByTagName('p')[0]</code>获取的第一个<code>p</code>元素。</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-30.png" alt="" /></p> <p><code>p</code>的父节点是<code>body</code>,但是我们怎么能得到祖父节点,这是两个层次呢?我们可以通过链接属性来实现:</p> <pre><code>p.parentNode.parentNode </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-31.png" alt="" /></p> <p>而<code>parentElement</code>和<code>parentNode</code>类似,都表示一个元素的父元素,但两者还是有一定的差异性:</p> <ul> <li><code>parentNode</code>是W3C标准规范定义的一个属性,用节点的形式返回一个节点的父节点</li> <li><code>parentElement</code>最早是IE浏览器才支持的一个属性,功能基本和<code>parentNode</code>一致,其也是Firefox 9和DOM4的新功能</li> <li>当一个节点的父节点的<code>nodeType !==1</code>的时候,即父节点不是<code>Element</code>的时候,通过<code>parentElement</code>得到的父节点会是<code>null</code></li> </ul> <p>比如:</p> <pre><code>document.body.parentNode; // =&gt; the &lt;html&gt; element document.body.parentElement; // =&gt; the &lt;html&gt; element document.documentElement.parentNode; // =&gt; the document node document.documentElement.parentElement; // =&gt; null </code></pre> <p>由于<code>&lt;html&gt;</code>元素( <code>document.documentElement</code> )没有作为元素的父元素,因此<code>parentElement</code>为<code>null</code> 。 (还有其他更不可能的情况, <code>parentElement</code>可能为<code>null</code> ,但您可能永远不会遇到它们。)</p> <blockquote> <p><strong>只要记住,名称中带有<code>element</code>的属性总是返回<code>Element</code>或<code>null</code> 。 没有的属性可以返回任何其他类型的节点。通常,遍历DOM时,<code>parentNode</code>更常用。</strong></p> </blockquote> <h3>子节点</h3> <p>子节点刚好与父节点相反,<strong>节点的子节点是在它下面的一个级别的节点</strong>。超过一层嵌套的任何节点通常称为<strong>后代节点</strong>。</p> <p>DOM中子节点对应的属性有:</p> <ul> <li><strong><code>childNodes</code></strong>:子节点</li> <li><strong><code>firstChild</code></strong>:第一个子节点</li> <li><strong><code>lastChild</code></strong>:最后一个子节点</li> <li><strong><code>children</code></strong>:元素的子节点</li> <li><strong><code>firstElementChild</code></strong>:第一个子元素节点</li> <li><strong><code>lastElementChild</code></strong>:最后一个子元素节点</li> </ul> <p><code>childNodes</code>属性返回包含指定节点的子节点的集合,该集合为即时更新的集合(是一个动态集合)。比如,你期望获取到<code>ul</code>元素的子节点<code>li</code>元素,就可以像下面这样操作:</p> <pre><code>ul.childNodes </code></pre> <p>输出的结果如下:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-32.png" alt="" /></p> <p>正如上图所示,<code>ul.childNodes</code>输出的结果是一个<code>NodeList</code>。除了三个<code>li</code>元素之外,还输出了四个文本节点。这是因为我们的HTML是自己编写的,而不是JavaScript生成的,并且元素之间的缩进被作为文本节点计算在DOM中。这不是很直观,主要是因为浏览器开发者工具将元素标签的空白节点去掉了。</p> <p>如果我们尝试使用<code>firstChild</code>属性来更改第一个子节点的背景颜色,将会失败的,那是因为第一个节点是文本节点。</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-33.png" alt="" /></p> <p><code>childeren</code>、<code>firstElementChild</code>和<code>lastElementChild</code>属性将检索到元素节点。比如<code>ul.children</code>将会返回三个<code>li</code>元素。</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-34.png" alt="" /></p> <p>使用<code>firstElementChild</code>,我们可以改变<code>ul</code>中的第一个<code>li</code>的<code>background-color</code>,比如:</p> <pre><code>ul.firstElementChild.style.backgroundColor = 'yellow' </code></pre> <p>在浏览器控制台上执行上面的代码之后,你可以看到<code>ul</code>中的第一个<code>li</code>的背景色已经变成了黄色:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-35.png" alt="" /></p> <p>在执行基本的DOM操作时,特定的元素属性非常有用。在JavaScript生成的Web应用程序中,选择所有节点的属性更有可能被使用,因为在这种情况下,空白和缩进将不存在。</p> <p><code>children</code>返回的是一个<code>HTMLCollection</code>,也是一个类数组。可以使用<code>for...of</code>循环来遍历所有的<code>children</code>元素:</p> <pre><code>for (let element of ul.children) { element.style.backgroundColor = 'yellow' } </code></pre> <p>如此一来,<code>ul</code>下面的所有<code>li</code>元素的背景色都变成了黄色。</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-36.png" alt="" /></p> <p>由于我们的<code>p</code>元素包含了文本和元素,所以<code>childNodes</code>属性有助于访问这些信息:</p> <pre><code>for (let element of p.childNodes) { console.log(element); } </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-37.png" alt="" /></p> <p><code>childNodes</code>和<code>children</code>返回的是一个类数组。可以通过索引号访问节点,或者找到它们的<code>length</code>属性。</p> <pre><code>document.body.children[3].lastElementChild.style.background = 'fuchsia'; </code></pre> <p>上面的代码找到了<code>body</code>中第四个子元素<code>ul</code>下的最后一个子元素<code>li</code>:</p> <ul> <li><code>document.body.children[3]</code>:选择<code>body</code>下的第四个子元素<code>ul</code></li> <li><code>document.body.children[3].lastElementChild</code>:选择<code>ul</code>下的最后一个子元素<code>li</code></li> </ul> <p>也就是说执行上面的代码之后,<code>ul</code>中的最后一个<code>li</code>的背景颜色变成了<code>fuchsia</code>:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-38.png" alt="" /></p> <h3>兄弟节点</h3> <p>节点的兄弟节点是DOM中同一级别上的任何节点。兄弟姐妹不必是同一个类型的节点 —— 文本、元素和注释节点都可以是兄弟节点。</p> <ul> <li><strong><code>previousSibling</code></strong>:前一个兄弟节点</li> <li><strong><code>nextSibling</code></strong>:下一个兄弟节点</li> <li><strong><code>previousElementSibling</code></strong>:前一个兄弟元素节点</li> <li><strong><code>nextElementSibling</code></strong>:后一个兄弟元素节点</li> </ul> <p>兄弟属性与子节点的工作方式相同,其中有一组属性遍历所有节点,以及一组仅用于元素节点的属性。<code>previousSibling</code>和<code>nextSibling</code>将会获得前一个和下一个节点;<code>previousElementSibling</code>和<code>nextElementSibling</code>只获得元素节点。来看一个小示例:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-39.png" alt="" /></p> <p>从上图就可以看出他们之间的差异性。如果我们要修改<code>tiger</code>元素的前面和后面的元素节点背景颜色,我们就可以使用<code>previousElementSibling</code>和<code>nextElementSibling</code>。比如:</p> <pre><code>tiger.nextElementSibling.style.background = 'coral'; tiger.previousElementSibling.style.background = 'aquamarine'; </code></pre> <p>最终效果如下:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-40.png" alt="" /></p> <p>咱们可以用张图来描述它们之间的一些关系</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-41.png" alt="" /></p> <h2>如何更改DOM</h2> <p>通过前面的学习,对DOM有了一定的了解,知道怎么访问和遍历DOM。但要更加精通DOM,下一步就要学习如何添加、更改、替换和删除节点。待办事项列表(Todo-List)是JavaScript中一个典型的示例。可以通过将要学习的DOM知识来对Todo-List做创建、修改和删除等操作。</p> <p>在接下来的内容中,将进一步的讨论JavaScript中的DOM是如何增、删、改等事项,因为查在前面已学习过。学习这些知识之后,我们就很容易的操作Todo-List。</p> <h3>创建节点</h3> <p>在静态网站中,是通过在<code>.html</code>文件中编写HTML代码,将元素添加到Web页面中。在动态Web应用程序中,元素和文本通常是使用JavaScript来添加的。其中<code>createElement()</code>和<code>createTextNode()</code>方法就是用于在DOM中创建新节点。</p> <ul> <li><strong><code>createElement()</code></strong>:创建一个新的元素节点</li> <li><strong><code>createTextNode()</code></strong>:创建一个新的文本节点</li> <li><strong><code>node.textContent</code></strong>:获取或设置元素节点的文本内容</li> <li><strong><code>node.innerHTML</code></strong>:获取或设置元素的HTML内容</li> </ul> <p>在开始之前,先修改<code>index.html</code>文件的代码:</p> <pre><code>&lt;!DOCTYPE html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;title&gt;Learning the DOM&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;h1&gt;Document Object Model&lt;/h1&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p>老规矩,在浏览器中打开开发者工具,并且使用<code>document.createElement()</code>方法创建一个<code>p</code>元素。同时将创建的<code>p</code>元素赋值给一个变量<code>paragraph</code>:</p> <pre><code>const paragraph = document.createElement('p') </code></pre> <p>这个时候我们创建了<code>p</code>元素,使用<code>console.log(paragraph)</code>时,可以看到控制台中输入一个空的<code>&lt;p&gt;&lt;/p&gt;</code>元素,但在HTML结构中并看不到这个新创建的<code>p</code>元素,如下图所示:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-42.gif" alt="" /></p> <p><code>paragraph</code>变量输出一个空的<code>p</code>元素,它在没有任何文本的情况下是不太有用的。为了向元素添加文本,可以通过<code>textContent</code>属性来完成:</p> <pre><code>paragraph.textContent = '我是一个段落' </code></pre> <p>执行<code>console.log(paragraph)</code>命令时,可以看到控制台上输入的<code>p</code>元素带有了“我是一个段落”的内容:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-43.png" alt="" /></p> <p>虽然通过<code>textContent</code>属性给新创建的<code>p</code>元素添加了指定的文本内容,但新创建的<code>p</code>元素和对应的文本内容并没有添加到HTML中。那么怎么添加到HTML中,后面我们会介绍相关的方法。</p> <p>除了<code>textContent</code>属性之外,还可以使用<code>innerHTML</code>属性给元素设置内容,这个属性允许你把THML元素和文本添加到指定的元素中。比如:</p> <pre><code>paragraph.innerHTML = '我是一个&lt;strong&gt;段落元素&lt;/strong&gt;' console.log(paragraph) </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-44.png" alt="" /></p> <blockquote> <p>注意,使用<code>innerHTML</code>方法给元素添加内容时会引起<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML#Security_considerations">XSS</a>风险,因为内联JavaScript可以添加到元素中。因此,建议使用<code>textContent</code>代替<code>innerHTML</code>。</p>" </blockquote> <p>上面看到的是使用<code>textContent</code>和<code>innerHTML</code>给元素添加内容,同样的,也可以使用它们来获取元素的内容。只不过<code>textContent</code>只获取元素中的文本内容,而<code>innerHTML</code>将元素中的文本内容和子元素一起将会获得。如下所示:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-45.png" alt="" /></p> <p>在JavaScript中还可以使用<code>createTextNode()</code>方法创建文本节点。</p> <pre><code>const text = document.createTextNode('我是新创建的文本节点') console.log(text) </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-46.png" alt="" /></p> <p>正如前面提到的,虽然这几个方法和属性已经创建了新的元素和文本节点,但是它们并没有插入到文档中,浏览器中并看不到任何的效果。主要是原因是<strong>我们只是创建了元素和文本节点,但没有插入到文档中</strong>。接下来的要介绍的就是怎么把新创建的元素和文本节点插入到文档中,让你在浏览器中能看到JavaScript动态创建的元素和文本。</p> <h3>将节点插入到DOM中</h3> <p>为了能在页面上看到创建的新文本节点和元素,我们需要将它们插入到<code>document</code>中。在JavaScript中可以使用<code>appendChild()</code>和<code>insertBefore()</code>等方法将新创建的项目添加到父元素的开始、中间或后面。也可以使用<code>replaceChild()</code>来替换掉一个旧节点。</p> <ul> <li><strong><code>node.appendChild()</code></strong>:添加一个节点作为父元素的最后一个子元素</li> <li><strong><code>node.insertBefore()</code></strong>:在指定的同级节点前将节点插入父元素</li> <li><strong><code>node.replaceChild()</code></strong>:用一个新节点替换现有节点</li> </ul> <p>为了实践这些方法,让我们在<code>index.html</code>中添加一个无序列表:</p> <pre><code>&lt;ul&gt; &lt;li&gt;Buy groceries&lt;/li&gt; &lt;li&gt;Feed the cat&lt;/li&gt; &lt;li&gt;Do laundry&lt;/li&gt; &lt;/ul&gt; </code></pre> <p>刷新浏览器,看到的效果如下:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-47.png" alt="" /></p> <p>为了将新创建的元素和文本节点添加到<code>ul</code>的末尾,我们需要使用上面所了解的方法<code>document.createElement()</code>和<code>node.textContent</code>先创建一个新的<code>li</code>元素,并为新创建的元素添加任何你想要的内容。</p> <pre><code>// 访问ul元素 const todoList = document.querySelector('ul') // 创建一个新的li元素 const newTodo = document.createElement('li') // 给新创建的li元素添加文本节点“Do homework” newTodo.textContent = 'Do homework' </code></pre> <p>接下来我们可以使用<code>parentNode.appendChild(newNode)</code>将新创建的<code>newTodo</code>(也就是<code>li</code>)添加到<code>todoList</code>(也就是<code>ul</code>)中,并且成为最后一个子元素:</p> <pre><code>todoList.appendChild(newTodo) </code></pre> <p>这个时候你可以看到新创建的<code>li</code>元素已经成功的插入到<code>ul</code>中了,并且成为其最后一个子元素,而且在<code>document</code>可以看到,如下图所示:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-48.png" alt="" /></p> <p>使用<code>appendChild()</code>是向指定的父节点添加最后一个子元素,但很多时候我们需要将新创建的元素添加到前面。这个时候我们就可以使用<code>insertBefore()</code>方法。该方法接受两个参数,第一个是要添加的新子节点,第二个是要跟踪新节点的同级节点(也就新节点要添加到哪个节点的前面)。换句话说,你正在将新节点插入到下一个同级节点之前。比如像下面这样:</p> <pre><code>parentNode.insertBefore(newNode, nextSibling); </code></pre> <p>回到我们示例中来,咱们使用<code>document.createElement()</code>重新创建一个新的<code>li</code>,并将其赋值给<code>anotherTodo</code>这个变量,同时给这个新创建的元素添加文本“Pay bills”。那么我们还需要一个参考节点,这里我们可以使用前面学习的知识,比如<code>firstElementChild</code>,将<code>ul</code>的第一个<code>li</code>作为要跟踪的目标节点。为了易于理解,你也可以将其赋值给一个变量,比如<code>targetTodo</code>:</p> <pre><code>const anotherTodo = document.createElement('li') anotherTodo.textContent = 'Pay bills' const targetTodo = todoList.firstElementChild todoList.insertBefore(anotherTodo, targetTodo) </code></pre> <p>你在浏览器看到的效果如下:</p> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-49.png" alt="" /></p> <p>现在我们知道了怎么给元素添加子元素,接下来我们要做的是用一个新节点替找现有的节点。同样的,先创建一个新元素<code>li</code>:</p> <pre><code>const modifiedTodo = document.createElement('li') modifiedTodo.textContent = 'Feed the dog' </code></pre> <p>与<code>insertBefore()</code>一样,<code>replaceChild()</code>接受两个参数 —— 新节点和要替换的节点:</p> <pre><code>parentNode.replaceChild(newNode, oldNode); </code></pre> <p>回到我们的实例中,替换前面新创建的第一个元素:</p> <pre><code>todoList.replaceChild(modifiedTodo, todoList.firstElementChild) </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-50.gif" alt="" /></p> <p>在JavaScript中,我们可以通过<code>appendChild()</code>、<code>insertBefore()</code>和<code>replaceChild()</code>的组合,可以在DOM中的任何地方插入节点和元素。</p> <h3>从DOM中删除节点</h3> <p>现在我们知道了如何在DOM中创建元素以及将创建的元素插入到DOM中,并且修改现有的DOM元素。最后一步来学习如何从DOM中删除现有节点。可以使用<code>removeChild()</code>从父节点中删除一个子节点,并且可以使用<code>remove()</code>删除节点本身。</p> <ul> <li><strong><code>node.removeChild()</code></strong>:删除子节点</li> <li><strong><code>node.remove()</code></strong>:删除节点</li> </ul> <p>同样的以前面的<code>ul</code>为例,使用<code>removeChild()</code>来删除任何你想要的子节点(<code>li</code>)。</p> <pre><code>// 删除最后一个子节点 todoList.removeChild(todoList.lastElementChild) // 删除第一个子节点 todoList.removeChild(todoList.firstElementChild) // 删除指定的子节点 todoList.removeChild(todoList.children[1]) </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-51.gif" alt="" /></p> <p>另外一种方法是直接在子节点上使用<code>remove()</code>方法来删除节点本身。</p> <pre><code>// 删除ul中的第一个li todoList.firstElementChild.remove() // 删除ul中的最后一个li todoList.lastElementChild.remove() </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/understander-dom-52.gif" alt="" /></p> <p>正如上面所演示的一样,使用<code>removeChild()</code>和<code>remove()</code>可以从DOM中删除任何节点。除此之外还有一个更诡异的方法可以删除子节点,那就是将父元素的<code>innerHTML</code>属性值设置为空字符串(<code>' '</code>)。使用这种方法将会一次性将父元素的所有节点删除,不过这不是首选方法,因为你无法删除指定的节点。</p> <h2>总结</h2> <p>这篇文章主要介绍了JavaScript中DOM的基本知识,通过前面的学习,知道了如何访问DOM中的任何元素,遍历DOM中的任何节点,并修改DOM本身,也可以根据自己需要删除想要删除的DOM节点。这些DOM API可以帮助我们动态的对DOM进行增、删、改、查相关的事情。言外之意,使用上面的API可以对DOM做你自己想做的一些事情。</p> <div class="blog-author media"><a class="media-object" href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank"><img src="/sites/default/files/blogs/author/airen.jpg"></a><div class="media-body"><h3 class="media-heading"><a href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank">大漠</a></h3><div class="media-des">常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等韦德1946手机版客户端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/book-comment.html"" target="_blank">图解CSS3:核心技术与案例实战</a>》。</div></div></div> <p>如需转载,烦请注明出处:<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/understanding-the-dom.html">https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/understanding-the-dom.html</a></p> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/660.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">DOM</a></div></div></div> Wed, 16 May 2018 12:26:03 +0000 Airen 2410 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/scroll-to-the-future-modern-javascript-css-scrolling-implementations.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><blockquote> <p>特别声明,本文来源于@sea_ljf的译文《<a href="http://www.xysjxj.com/quot;//www.zcfy.cc/article/scroll-to-the-future">滑向未来(现" JavaScript 与 CSS 滚动实现指南)</a>》,原文《<a href="//evilmartians.com/chronicles/scroll-to-the-future-modern-javascript-css-scrolling-implementations">Scroll to the future</a>》。</p> </blockquote> <p>一些(网站)滚动的效果是如此令人着迷但你却不知该如何实现,本文将为你揭开它们的神秘面纱。我们将基于最新的技术与规范为你介绍最新的 JavaScript 与 CSS 特性,将使你的页面滚动更平滑、美观且性能更好。</p> <p>大多数Web页面不适合单屏显示,所以Web页面出现滚动条被所有用户认为是理所当然的。对于韦德1946手机版客户端开发人员和视觉设计来说,跨浏览器提供良好的滚动体验,同时符合设计,无疑是一个挑战。尽管 Web 标准的发展速度远超从前,但代码的实现往往是落后的。下文将为你介绍一些常见的关于滚动的案例,检查一下你所用的解决方案是否被更优雅的方案所代替。</p> <h2>消逝的滚动条</h2> <p>在过去的三十年里,滚动条的外观不断改变以符合设计的趋势,设计师们为(滚动条的)颜色、阴影、上下箭头的形状与边框的圆角实验了多种风格。以下是 Windows 上的变化历程:</p> <p><img src="/sites/default/files/blogs/2018/1805/scroll-1.png" alt="" /></p> <p>在2011年,苹果设计师从 iOS 上获得灵感,为如何定义“美观的”滚动条确定了方向。所有滚动条均从 Mac 电脑中消失,不再占据任何页面空间,只有在用户触发滚动时(滚动条)才会重新出现(有些用户会设置不隐藏滚动条)。</p> <p><img src="/sites/default/files/blogs/2018/1805/scroll-2.png" alt="" /></p> <p>滚动条安静地消逝并未引起苹果粉丝的不满,已经习惯了 iPhone 与 iPad 上滚动方式的用户很快地习惯了这一设计。大多数开发人员与设计师都认为这是一个“好消息”,因为计算滚动条的宽度可真是件苦差事。</p> <blockquote> <p>然而,我们生活在一个拥有众多操作系统与浏览器的世界中,它们(对于滚动)的实现各不相同。如果你和我们一样是一名 Web 开发者,你可不能把“滚动条问题”置之不理。</p> </blockquote> <p>以下将为你介绍一些小技巧,使你的用户在滚动时有更好的体验。</p> <h2>隐藏但可滚动</h2> <p>先来看看一个关于模态弹框的经典例子。当它被打开的时候,主页面应该停止滚动。在 CSS 中有如下的快捷实现方式:</p> <pre><code>body { overflow: hidden; } </code></pre> <p>但上述代码会带来一点不良的副作用:</p> <p><img src="/sites/default/files/blogs/2018/1805/scroll-3.gif" alt="" /></p> <p>在这个示例中,为了演示目的,我们在 Mac 系统中设置了强制显示滚动条,因而用户体验与 Windows 用户相似。</p> <p>我们该如何解决这个问题呢?如果我们知道滚动条的宽度,每次当模态框出现时,可在主页面的右边设置一点边距。</p> <p>由于不同的操作系统与浏览器对滚动条的宽度不一,因而获取它的宽度并不容易。在Mac 系统中,无论任何浏览器(滚动条)都是统一<code>15px</code>,然而 Windows 系统可会令开发者发狂:</p> <p><img src="/sites/default/files/blogs/2018/1805/scroll-4.png" alt="" /></p> <p>注意,以上仅是 Windows 系统下基于当前最新版浏览器(测试所得)的结果。以前的(浏览器)版本(宽度)可能有所不同,也没人知道未来(滚动条的宽度)会如何变化。</p> <p>不同于猜测(滚动条的宽度),你可以通过 JavaScript 计算它的宽度:</p> <pre><code>const outer = document.createElement('div'); const inner = document.createElement('div'); outer.style.overflow = 'scroll'; document.body.appendChild(outer); outer.appendChild(inner); const scrollbarWidth = outer.offsetWidth - inner.offsetWidth; document.body.removeChild(outer); </code></pre> <p>尽管仅仅七行代码(就能测出滚动条的宽度),但有数行代码是操作 DOM 的。(为性能起见,)如非必要,尽量避免进行 DOM 操作。</p> <p>解决这个问题的另一个方法是在模态框出现时仍保留滚动条,以下是基于这思路的纯 CSS 实现:</p> <pre><code>html { overflow-y: scroll; } </code></pre> <p>尽管“模态框抖动”问题解决了,但整体的外观却被一个无法使用的滚动条影响了,这无疑是设计中的硬伤。</p> <p>在我们看来,更好的解决方案是完全地隐藏滚动条。纯粹用 CSS 也是可以实现的。该方法(达到的效果)和 macOS 的表现并不是完全一致,(当用户)滚动时滚动条仍然是不可见的。滚动条总是处于不可见状态,然而页面是可被滚动的。对于Chrome,Safari 和 Opera 而言,可以使用以下的 CSS:</p> <pre><code>.container::-webkit-scrollbar { display: none; } </code></pre> <p>IE 或 Edge 可用以下代码:</p> <pre><code>.container { -ms-overflow-style: none; } </code></pre> <p>至于 Firefox,很不幸,没有任何办法隐藏滚动条。</p> <p>正如你所见,并没有任何银弹。任何解决方案都有它的优点与缺点,应根据你项目的需要选择最合适的。</p> <h2>外观争议</h2> <p>需要承认的是,滚动条的样子在部分操作系统上并不好看。一些设计师喜欢完全掌控他们(所设计)应用的样式,任何一丝细节也不放过。在 <a href="http://www.xysjxj.com/quot;//github.com/search?l=JavaScript&amp;q=custom+scrollbar&amp;type=Repositories">GitHu" 上有上百个库</a>借助 JavaScript 取代系统滚动条的默认实现,以达到自定义的效果。</p> <blockquote> <p>但如果你想根据现有的浏览器定制一个滚动条呢?(很遗憾,)并没有通用的 API,每个浏览器都有其独特的代码实现。</p> </blockquote> <p>尽管5.5版本以后的 IE 浏览器允许你修改滚动条的样式,但它只允许你修改滚动条的颜色。以下是如何重新绘制(滚动条)拖动部分与箭头的代码:</p> <pre><code>body { scrollbar-face-color: blue; } </code></pre> <p>但只改变颜色对提高用户体验而言帮助不大。据此,WebKit 的开发者在2009年提出了(修改滚动条)样式的方案。以下是使用 <code>-webkit</code> 前缀在支持相关样式的浏览器中模拟 macOS 滚动条样式的代码:</p> <pre><code>::-webkit-scrollbar { width: 8px; } ::-webkit-scrollbar-thumb { background-color: #c1c1c1; border-radius: 4px; } </code></pre> <p>比如下面这个示例的效果:</p> <div style="margin-bottom: 20px;"><iframe id="OZEpMj" src="//codepen.io/airen/embed/OZEpMj?height=400&amp;theme-id=0&amp;slug-hash=OZEpMj&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <blockquote> <p>有关于自定义滚动条样式的相关介绍可以阅读@Akinjide Bankole的《<a href="//scotch.io/tutorials/customize-the-browsers-scrollbar-with-css">Customize the Browser's Scrollbar with CSS</a>》一文。</p> </blockquote> <p>Chrome、Safari、Opera 甚至于 UC 浏览器或者三星自带的桌面浏览器都支持(上述 CSS)。<a href="http://www.xysjxj.com/quot;//wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/9081910-add-support-for-scrollbar-styling">Edg" 也有计划实现它们</a>。但三年过去了,该计划仍在中等优先级中(而尚未被实现)。</p> <p>当我们讨论滚动条的定制时,Mozilla 基金会基本上是无视了设计师的需求。(有开发者在)<a href="http://www.xysjxj.com/quot;//bugzilla.mozilla.org/show_bug.cgi?id=77790">17年前</a>就已经提出了一个希望修改滚动条样式的请求。而就在几个月前,@Jef" Griffiths(Firefox 浏览器总监)终于为这个问题作出了回答:</p> <blockquote> <p>除非团队中有人对此有兴趣,否则我对此毫不关心。</p> </blockquote> <p>公平地说,从 W3C 的角度看来,尽管 WebKit 的实现得到广泛的支持,但它仍然不是标准。现有的为滚动条修改样式的草案,是基于 IE 的:仅能修改它的颜色。</p> <p>伴随着请求如同 WebKit 一样支持滚动条样式修改 <a href="//github.com/w3c/csswg-drafts/issues/2009">issue</a> 的提交,争议仍在继续。如果你想影响 CSS 工作小组,是时候参与讨论了。也许这不是优先级最高的问题,但(如同 WebKit 一样修改滚动条样式)得到标准化后,能使很多韦德1946手机版客户端工程师与设计师轻松很多。</p> <h2>流畅的操作体验</h2> <p>对于滚动而言,最常见的任务是登录页的导航(跳转)。通常,它是通过锚点链接来完成的。只需要知道元素的 <code>id</code> 即可:</p> <pre><code>&lt;a href="http://www.xysjxj.com/quot;#section"&gt;Section&lt;/a&gt" </code></pre> <p>点击该链接会 跳 到(该锚点对应的)区块上,(然而) UX 设计师一般会坚持认为该过程应是平滑地运动的。<a href="http://www.xysjxj.com/quot;//github.com/search?l=JavaScript&amp;q=animate+scroll&amp;type=Repositories">GitHu" 上有大量造好的轮子</a>(帮你解决这个问题),然而它们或多或少都用到 JavaScript。(其实)只用一行代码也能实现同样的效果,最近DOM API 中的 <code>Element.scrollIntoView()</code> 可以通过<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView">传入配置对象</a>来实现平滑滚动:</p>" <pre><code>elem.scrollIntoView({ behavior: 'smooth' }); </code></pre> <p>然而该属<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#Browser_compatibility">性兼容性较差</a>且仍是通过脚本(来控制样式)。如有可能,应尽量少用额外的脚本。</p>" <p>幸运的是,有一个全新的 <a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior">CS" 属性</a>(仍在工作草案中),可以用简单的一行代码改变整个页面滚动的行为。</p> <pre><code>html { scroll-behavior: smooth; } </code></pre> <p>结果如下:</p> <p><img src="/sites/default/files/blogs/2018/1805/scroll-5.gif" alt="" /></p> <p><em>从一个区块跳到另一个</em>。</p> <p><img src="/sites/default/files/blogs/2018/1805/scroll-6.gif" alt="" /></p> <p><em>平滑地滚动</em>。</p> <p>你可以在 <a href="http://www.xysjxj.com/quot;//codepen.io/askd/full/WdXOYW">Codepen</a>" 上试验这个属性。</p> <div style="margin-bottom: 20px;"><iframe id="BxVWQm" src="//codepen.io/airen/embed/BxVWQm?height=400&amp;theme-id=0&amp;slug-hash=BxVWQm&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>在撰写本文时,<code>scroll-behavior</code> 仅在 Chrome、 Firefox 与 Opera 上被支持,但我们希望它能被广泛支持,因为使用 CSS (比使用 JavaScript)在解决页面滚动问题时优雅得多,并更符合“<a href="http://www.xysjxj.com/quot;//en.wikipedia.org/wiki/Progressive_enhancement">渐进增强</a>”的模式。</p>" <h2>粘性 CSS</h2> <p>另一个常见的需求是根据滚动方向动态地定住元素,即有名的“粘性(即 CSS 中的<code>position: sticky</code>)”效应。</p> <p><img src="/sites/default/files/blogs/2018/1805/scroll-7.gif" alt="" /></p> <p>在以前的日子里,要实现一个“粘性”元素需要编写复杂的滚动处理函数去计算元素的大小。(然而)该函数较难处理元素在“黏住”与“不黏住”之间微小的延迟,(通常会)导致(元素)抖动的出现。通过 JavaScript 来实行(“粘性”元素)也有性能上的问题,特别是在(需要)调用 <a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect"><code>Element.getBoundingClientRect()</code></a>时。</p>" <p>不久之前,CSS 实现了 <code>position: sticky</code> 属性。只需通过指定(某方向上的)偏移量即可实现我们想要的效果。</p> <pre><code>.element { position: sticky; top: 50px; } </code></pre> <p>(编写上述代码后,)剩下的就交由浏览器实现即可。你可以在 <a href="http://www.xysjxj.com/quot;//codepen.io/askd/full/ppGQya">Codepen</a>" 上试验一下。</p> <div style="margin-bottom: 20px;"><iframe id="YQzKrN" src="//codepen.io/airen/embed/YQzKrN?height=400&amp;theme-id=0&amp;slug-hash=YQzKrN&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>撰写本文之时,<code>position: sticky</code> 在各式浏览器(包括移动端浏览器)上支持良好,所以如果你还在使用 JavaScript 去解决这个问题的话,是时候换成纯 CSS 的实现了。</p> <h2>全面使用函数节流</h2> <p>从浏览器的角度看来,滚动是一个事件,因此在 JavaScript 中是使用一个标准化的事件监听器<code>addEventListener</code> 去处理它:</p> <pre><code>window.addEventListener('scroll', () =&gt; { const scrollTop = window.scrollY; /* doSomething with scrollTop */ }); </code></pre> <p>用户往往高频率地滚动(页面),但如果滚动事件触发太频繁的话,会导致性能上的问题,可以通过使用函数节流这一技巧去优化它。</p> <pre><code>window.addEventListener('scroll', throttle(() =&gt; { const scrollTop = window.scrollY; /* doSomething with scrollTop */ })); </code></pre> <p>你需要定义一个节流函数包装原来的事件监听函数,(节流函数是)减少被包装函数的执行次数,只允许它在固定的时间间隔之内执行一次:</p> <pre><code>function throttle(action, wait = 1000) { let time = Date.now(); return function() { if ((time + wait - Date.now()) &lt; 0) { action(); time = Date.now(); } } } </code></pre> <p>为了使(节流后的)滚动更平滑,你可以通过使用 <code>window.requestAnimationFrame()</code> 来实现函数节流:</p> <pre><code>function throttle(action) { let isRunning = false; return function() { if (isRunning) return; isRunning = true; window.requestAnimationFrame(() =&gt; { action(); isRunning = false; }); } } </code></pre> <p>当然,你可以通过现有的开源轮子来<a href="http://www.xysjxj.com/quot;//lodash.com/docs/4.17.5#throttle">实现</a>,就" <a href="http://www.xysjxj.com/quot;//lodash.com/">Lodash</a>" 一样。你可以访问 <a href="http://www.xysjxj.com/quot;//codepen.io/askd/full/RxEYOv">Codepen</a>" 来看看上述解决方案与 Lodash 中的 <code>_.throttle</code> 之间的区别。</p> <div style="margin-bottom: 20px;"><iframe id="RyJpYL" src="//codepen.io/airen/embed/RyJpYL?height=400&amp;theme-id=0&amp;slug-hash=RyJpYL&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>使用哪个(开源库)并不重要,重要的是在需要的时候,记得优化你(页面中的)滚动处理函数。</p> <h2>在视窗中显示</h2> <p>当你需要实现图片懒加载或者无限滚动时,需要确定元素是否出现在视窗中。这可以在事件监听器中处理,最常见的解决方案是使用 <code>lement.getBoundingClientRect()</code> :</p> <pre><code>window.addEventListener('scroll', () =&gt; { const rect = elem.getBoundingClientRect(); const inViewport = rect.bottom &gt; 0 &amp;&amp; rect.right &gt; 0 &amp;&amp; rect.left &lt; window.innerWidth &amp;&amp; rect.top &lt; window.innerHeight; }); </code></pre> <p>上述代码的问题在于每次调用 <code>getBoundingClientRect</code> 时都会触发回流,严重地影响了性能。在事件处理函数中调用( <code>getBoundingClientRect</code> )尤为糟糕,就算使用了函数节流(的技巧)也可能对性能没多大帮助。 (回流是指浏览器为局部或整体地重绘某个元素,需要重新计算该元素在文档中的位置与形状。)</p> <p>在2016年后,可以通过使用 <a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API">Intersectio" Observer</a> 这一 API 来解决问题。它允许你追踪目标元素与其祖先元素或视窗的交叉状态。此外,尽管只有一部分元素出现在视窗中,哪怕只有一像素,也可以选择触发回调函数:</p> <pre><code>const observer = new IntersectionObserver(callback, options); observer.observe(element); </code></pre> <p>(<a href="http://www.xysjxj.com/quot;//gist.github.com/paulirish/5d52fb081b3570c81e3a">点击这里</a>,查看触发回流" DOM 属性和方法。)</p> <p>此 API 被广泛地支持,但仍有一些浏览器需要 <a href="http://www.xysjxj.com/quot;//github.com/w3c/IntersectionObserver">polyfill</a>。尽管如此,它仍是目前最好的解决方案。</p>" <blockquote> <p>有关于<code>IntersectionObserver</code>相关的介绍可以点击《<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/different-types-of-observers-supported-by-modern-browsers.html">现代浏览器支持的不同类型的观察者</a>》或者《<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/lazy-loading-images-using-intersection-observer.html">使用 Intersection Observer 实现图片延迟加</a>》。</p> </blockquote> <h2>滚动边界问题</h2> <p>如果你的弹框或下拉列表是可滚动的,那你务必要了解连锁滚动相关的问题:当用户滚动到(弹框或下拉列表)末尾(后再继续滚动时),整个页面都会开始滚动。</p> <p><img src="/sites/default/files/blogs/2018/1805/scroll-8.gif" alt="" /></p> <p>当滚动元素到达底部时,你可以通过(改变)页面的 <code>overflow</code> 属性或在滚动元素的滚动事件处理函数中取消默认行为来解决这问题。</p> <p>如果你选择使用 JavaScript (来处理),请记住要处理的不是“<code>scroll</code>(事件)”,而是每当用户使用鼠标滚轮或触摸板时触发的“<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/Events/wheel"><code>wheel</code>(事件)</a>”:</p>" <pre><code>function handleOverscroll(event) { const delta = -event.deltaY; if (delta &lt; 0 &amp;&amp; elem.offsetHeight - delta &gt; elem.scrollHeight - elem.scrollTop) { elem.scrollTop = elem.scrollHeight; event.preventDefault(); return false; } if (delta &gt; elem.scrollTop) { elem.scrollTop = 0; event.preventDefault(); return false; } return true; } </code></pre> <p>不幸的是,这个解决方案不太可靠。同时可能对(页面)性能产生负面影响。</p> <p>过度滚动对移动端的影响尤为严重。<a href="http://www.xysjxj.com/quot;//en.wikipedia.org/wiki/Loren_Brichter">@Lore" Brichter</a> 在 iOS 的 Tweetie 应用上创造了一个“下拉刷新”的新手势,这在 UX 社区中引起了轰动:包括 Twitter 与 Facebook 在内的各大应用纷纷采用了(相同的手势)。</p> <p>当这个特性出现在安卓端的 Chrome 浏览器中时,问题出现了:它会刷新整个页面而不是加载更多的内容,成为开发者在他们的应用中实现“下拉刷新”时的麻烦。</p> <p>CSS 通过 <a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/overscroll-behavior.html"><code>overscroll-behavior</code> 这个新属性解决问题</a>。它通过控制元素滚动到尽头时的行为来解决下拉刷新与连锁滚动所带来的问题,(它的属性值中)也包含针对不同平台特殊值:安卓的 <code>glow</code> 与 苹果系统中的 <code>rubber band</code>。</p> <p>现在,上面 GIF 中的问题,在 Chrome、Opera 或 Firefox 中可以通过以下一行代码来解决:</p> <pre><code>.element { overscroll-behavior: contain; } </code></pre> <p>公平地说,IE 与 Edge 实现了(它独有的) <a href="http://www.xysjxj.com/quot;//msdn.microsoft.com/en-us/library/hh772034.aspx"><code>-ms-scroll-chaining</code>" 属性</a>来控制连锁滚动,但它并不能处理所有的情况。幸运的是,<a href="//wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/32172871-implement-css-overscroll-behavior">根据这消息</a>,微软的浏览器已经准备实现 <code>overscroll-behavior</code> 这一属性了。</p> <h2>触屏之后</h2> <p>触屏设备上的滚动(体验)是一个很大的话题,深入讨论需要另开一篇文章。然而,由于很多开发者忽略了这方面的内容,这里需要提及一下。</p> <blockquote> <p>滚动手势无处不在,令人沉迷,以至于想出了<a href="http://www.xysjxj.com/quot;//www.thetimes.co.uk/article/take-a-swipe-at-smartphone-addiction-with-fake-handset-5bz3l8gqs">如此疯狂的主意</a>去解决“滚动上瘾”的问题。</p>" </blockquote> <p>周围的人在智能手机屏幕上上下移动他们的手指的频率是多少呢?经常这样对吧,当你阅读本文时,你很可能就在这么做。</p> <blockquote> <p>当你的手指在屏幕上移动时,你期待的是:<strong>页面内容平滑且流畅地移动</strong>。</p> </blockquote> <p>苹果公司<a href="http://www.xysjxj.com/quot;//thenextweb.com/apple/2012/08/07/the-apple-patent-steve-jobs-fought-hard-to-protect-and-his-connection-with-its-inventor/">开创</a>了“惯性”滚动并拥有它的<" href="http://www.xysjxj.com/quot;//patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&amp;Sect2=HITOFF&amp;d=PALL&amp;p=1&amp;u=%2Fnetahtml%2FPTO%2Fsrchnum.htm&amp;r=1&amp;f=G&amp;l=50&amp;s1=7,469,381.PN.&amp;OS=PN/7,469,381&amp;RS=PN/7,469,381">专利</a>" 。它讯速地成为了用户交互的标准并且我们对此已习以为常。</p> <p>但你也许已经注意到了,尽管移动端系统会为你实现页面上的惯性滚动,但当页面内某个元素发生滚动时,即使用户同样期待惯性滚动,但它并不会出现,这令人沮丧。</p> <p>这里有一个 CSS 的解决方案,但看起来更像是个 hack:</p> <pre><code>.element { -webkit-overflow-scrolling: touch; } </code></pre> <p>为什么这是个 hack 呢?首先,它只能在支持(Webkit)前缀的浏览器上才能工作。其次,它只适用于触屏设备。最后,如果浏览器不支持的话,你就这样置之不理吗?但无论如何,这总归是一个解决方案,你可以试着使用它。</p> <p>在触屏设备上,另一个需要考虑的问题是开发者如何处理 <code>touchstart</code> 与 <code>touchmove</code> 事件触发时可能存在的性能问题,它对用户滚动体验的影响非常大。<a href="http://www.xysjxj.com/quot;//github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#the-problem">这里</a>详细描述了整个问题。简单来说,现代的浏览器虽然知道如何使得滚动变得平滑,但为确认(滚动)事件处理函数中是否执行" <code>Event.preventDefault()</code> 以取消默认行为,有时仍可能需要花费<code>500ms</code>来等待事件处理函数执行完毕。</p> <p>即使是一个空的事件监听器,从不取消任何行为,鉴于浏览器仍会期待 <code>preventDefault</code> 的调用,也会对性能造成负面影响。</p> <p>为了准确地告诉浏览器不必担心(事件处理函数中)取消了默认行为,在 <a href="http://www.xysjxj.com/quot;//whatwg.org/">WHATWG</a>" 的 DOM 标准中存在着一个<a href="http://www.xysjxj.com/quot;//dom.spec.whatwg.org/#dom-addeventlisteneroptions-passive">不太显眼的特性</a>(能解决这问题)。(它就是)<" href="http://www.xysjxj.com/quot;//github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md">Passiv" event listeners</a>,浏览器对它的支持还是<a href="http://www.xysjxj.com/quot;//caniuse.com/#search=passive%20event%20listeners">不错的</a>。事件监听函数新接受一个可选的对象作为参数,告诉浏览器当事件触发时,事件处理函数永远不会取消默认行为。(当然,添加此参数后,)在事件处理函数中调" <code>preventDefault</code> 将不再产生效果。</p> <pre><code>element.addEventListener('touchstart', e =&gt; { /* doSomething */ }, { passive: true }); </code></pre> <p>针对不支持该参数的浏览器,这里也有一个 <a href="http://www.xysjxj.com/quot;//github.com/WICG/EventListenerOptions/blob/gh-pages/EventListenerOptions.polyfill.js">polyfill</a>" 。<a href="http://www.xysjxj.com/quot;//www.youtube.com/watch?v=NPM6172J22g">这视频</a>清晰地展示了此改进带来的影响。</p>" <div style="margin-bottom: 20px;"> <iframe width="100%" height="480" src="//www.youtube.com/embed/NPM6172J22g" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe> </div> <h2>旧技术运行良好,为何还要改动?</h2> <p>在现代互联网中,过渡地依赖 JavaScript 在各浏览器上实现相同的交互效果不再是合理的,“跨浏览器兼容性”已经成为过去式,更多的 CSS 属性与 DOM API 方法正逐步被各大浏览器所支持。</p> <p>在我们看来,当你的项目中,有特别酷炫的滚动效果时,<a href="http://www.xysjxj.com/quot;//en.wikipedia.org/wiki/Progressive_enhancement">渐进增强</a>是最好的做法。</p>" <blockquote> <p>你应该提供(给用户)所有(你能提供的)基础用户体验,并逐步在更先进的浏览器上提供更好的体验。</p> </blockquote> <p>必要时使用 polyfill,它们不会产生(不必要的)依赖,一旦(某个 polyfill 所支持的属性)得到广泛地支持,你就可以轻松地将它删掉。</p> <p>六个月之前,在本文尚未成文之时,之前我们描述的属性只被少量的浏览器所支持。而到了本文发表之时,这些属性已被广泛地支持。</p> <p>也许到了现在,当你上下翻阅本文之时,(之前不支持某些属性的)浏览器已经支持了该属性,这使得你编程更容易,并使你的应用打包出来体积更小。</p> <div class="blog-author media"><a class="media-object" href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank"><img src="/sites/default/files/blogs/author/airen.jpg"></a><div class="media-body"><h3 class="media-heading"><a href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank">大漠</a></h3><div class="media-des">常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等韦德1946手机版客户端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/book-comment.html"" target="_blank">图解CSS3:核心技术与案例实战</a>》。</div></div></div> <p>如需转载,烦请注明出处:<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/scroll-to-the-future-modern-javascript-css-scrolling-implementations.html">https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/scroll-to-the-future-modern-javascript-css-scrolling-implementations.html</a></p> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/translations"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">译文</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/645.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">overscroll-behavior</a></div></div></div> Sun, 13 May 2018 09:53:39 +0000 Airen 2409 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/where-lines-break-is-complicated-heres-all-the-related-css-and-html.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><blockquote> <p>特别声明,本文来源于<a href="//css-tricks.com/author/chriscoyier/">@CHRIS COYIER</a>的《<a href="//css-tricks.com/where-lines-break-is-complicated-heres-all-the-related-css-and-html/">Where Lines Break is Complicated. Here’s all the Related CSS and HTML</a>》。</p> </blockquote> <p>在Web页面上常常可以看到某个元素内部有一个很长的单词,元素的宽度不足以容纳它。一个常见的现象就是有一个很长的URL。这会发生什么呢?这取决于CSS,那么CSS如何控制布局,以及CSS如何让文本更合理的方式展示。</p> <p>比如下图的一种效果,在Web中是很常见的:</p> <p><img src="/sites/default/files/blogs/2018/1805/break-out-text-1.webp" alt="" /></p> <p>由于URL文本过长,已经超出容器的宽度,影响了整个视觉效果,甚至很多时候还会影响Web页面的布局效果。</p> <p>最暴力的做法就是在容器上添加<code>overflow:hidden;</code>,可以阻止文字(或其他任何东西)溢出容器。虽然这可以解决视觉上的效果,但它使文本变得不可访问。在一些使用鼠标的桌面浏览器中,你可能可以连续三次单击该行来选择URL并复制它,但是你不能指望每个人都知道,或者在所有场景中都有可能这么做。</p> <p>溢出流在这里也是自确的,因为这就是正在发生的事情。我们使用<code>overflow:auto</code>同样可以处理溢出流,它会触发一个水平滚动条。或许有的场景使用这种方案是合适的,但我想这不是一个普遍接受的解决方案。</p> <p>接下来我们来看看怎么将长文本换行,看看有哪些解决方式,或者更适合的方式。</p> <h2>实验场</h2> <p>我的想法是,要有一个可调整的内容面板,加上各种CSS属性和值,你可以在上面进行切换,以查看对应的内容效果。</p> <blockquote> <p>我确信这不是全面的或是最完美的。这只是我所知道的一些属性</p> </blockquote> <div style="margin-bottom: 20px;"><iframe id="QrxEeQ" src="//codepen.io/airen/embed/QrxEeQ?height=500&amp;theme-id=0&amp;slug-hash=QrxEeQ&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="500" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>在上面的示例中,你可以尝试着选择每个属性,得到对应的效果:</p> <p><img src="/sites/default/files/blogs/2018/1805/break-out-text-6.gif" alt="" /></p> <h2><code>word-break: break-all</code></h2> <p>允许单词在任何地方被打破(这里所说的打破是强行折断换行)。<code>word-break</code>属性可以“解决”这个问题:</p> <p><img src="/sites/default/files/blogs/2018/1805/break-out-text-2.gif" alt="" /></p> <pre><code>p { word-break: break-all; } </code></pre> <p>和<a href="http://www.xysjxj.com/quot;//fantasai.inkedblade.net/">@fantasi</a>电子交流过,她解释说,这主要是因为单词分割生新定义一个词是什么。<code>break-all</code>值本质上将非CJK视为CJK,它可以在任何地方(除了句号和括号外)断开。<code>keep-all</code>是反向的,将CJK作为非CJK处理。</p>" <blockquote> <p><strong>CJK</strong>:中日韩统一表意文字(CJK Unified Ideographs),目的是要把分别来自中文、日文、韩文、越文中,本质、意义相同、形状一样或稍异的表意文字(主要为汉字,但也有仿汉字如日本国字、韩国独有汉字、越南的喃字)于ISO 10646及Unicode标准内赋予相同编码 —— <a href="http://www.xysjxj.com/quot;//zh.wikipedia.org/wiki/CJK%E5%AD%97%E4%BD%93%E5%88%97%E8%A1%A8">维基百科</a></p>" </blockquote> <h2><code>overflow-wrap: break-word</code></h2> <p>对于我们已经列出的问题,<code>overflow-wrap</code>特性似乎是最有效的解决方案:</p> <p><img src="/sites/default/files/blogs/2018/1805/break-out-text-3.gif" alt="" /></p> <pre><code>p { word-wrap: break-word; /* old name */ overflow-wrap: break-word; } </code></pre> <p>乍一看,它看起来很像<code>word-break: break-all</code>。在上面的演示中,仔细观察URL,又并不像<code>word-break:break-all</code>一样,把单词<code>pen</code>打破<code>pe\n</code>,真正的效果是在<code>pen</code>单词末尾折断换行。这样的处理方式是我们更期待的一种。</p> <p>@fantasai是这样解释的:</p> <blockquote> <p>如果一个单词不能中断,因此它会溢出,那么它可以在任何地方断开,以避免溢出。</p> </blockquote> <h2><code>hyphens:auto</code></h2> <p><code>hyphens</code>属性做了你可能会想到的事情,比如断字中添加断线(<code>-</code>)。连字符有时可以在URL和长单词中使用,但不能保证。举个例子,一个很长的数字会把它绊倒。此外,连字符会影响所有文字,更随意地打破文字,以帮助文本均匀地贴在右边。</p> <p><img src="/sites/default/files/blogs/2018/1805/break-out-text-5.gif" alt="" /></p> <pre><code>p { hyphens: auto; } </code></pre> <p>@fantasai告诉我:</p> <blockquote> <p>如果一个单词横跨在一行的末尾,我们可以使用连字符将它们连接起来了。</p> </blockquote> <p>我想单词(“word”)可以帮助你解决这个问题。有些问题是长字符串不是“单词”,所以不能指望它来解决所有的溢出问题。</p> <h2><code>line-break: anywhere</code></h2> <p>还有一个属性叫做<code>line-break</code>。很明显,这主要是为了标点符号,但我似乎无法在任何浏览器中看到它。@fantasai告诉我,将会有一个新的属性值<code>anywhere</code>:</p> <blockquote> <p>"like word-break: break-all; except it actually breaks everything like a dumb terminal client"。</p> </blockquote> <h2>总结</h2> <p>这篇文章主要简单的介绍了CSS中几个处理超常文本溢出容器的处理方式。比如说,单词被打破折断换还;还是整个单词在容器边缘整体换行;或者说单词打破,但打破处添加连接符<code>-</code>。虽然文章中提到过的几个属性能解决我们常见的场景,但对于一些特殊的场景还是要特殊考虑,尤其是长字符(特别是恶意长字符),上面的CSS属性估计都无法能解决。</p> <h2>扩展阅读</h2> <ul> <li><a href="//www.w3.org/TR/css-text-3/">CSS Text Module Level 3</a></li> <li><a href="//www.w3.org/TR/css-text-4/">CSS Text Module Level 4</a></li> <li><a href="//css-tricks.com/handling-long-unexpected-content-css/">Handling Long and Unexpected Content in CSS</a></li> </ul> <div class="blog-author media"><a class="media-object" href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank"><img src="/sites/default/files/blogs/author/airen.jpg"></a><div class="media-body"><h3 class="media-heading"><a href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank">大漠</a></h3><div class="media-des">常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等韦德1946手机版客户端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/book-comment.html"" target="_blank">图解CSS3:核心技术与案例实战</a>》。</div></div></div> <p>如需转载,烦请注明出处:<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/where-lines-break-is-complicated-heres-all-the-related-css-and-html.html">https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/css/where-lines-break-is-complicated-heres-all-the-related-css-and-html.html</a></p> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/translations"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">译文</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/tags/68.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">CSS</a></div></div></div> Sun, 13 May 2018 04:18:06 +0000 Airen 2408 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/animation/how-you-can-use-simple-trigonometry-to-create-better-loaders.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><blockquote> <p>特别声明,本文来源于<a href="http://www.xysjxj.com/quot;//uxdesign.cc/@nashvail">@Nas" Vail</a>的《<a href="http://www.xysjxj.com/quot;//uxdesign.cc/how-you-can-use-simple-trigonometry-to-create-better-loaders-32a573577eb4">Ho" you can use simple Trigonometry to create better loaders</a>》。</p> </blockquote> <p>最近,在研究登陆页面的时候,遇到了一个网站。对使用它的人非常有用。网站上的一些东西吸引了我的眼球,让我有点不安。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-1.gif" alt="" /></p> <p>不自然的和不顺滑的动画促使我有了写这篇文章的想法。</p> <p>在这篇文章中,将使用三角函数的基本概念,重新创建一个更平滑的Loading效果。我知道这听起来很奇怪,但请相信我,这里一定会很有趣。你会惊讶地发现,要编写的代码很少。当然,你可能会担心三角函数相关的知识,事实上是你不需要知道三角学或数学你能理解这篇文章。我将会解释这里的每个圆相关的事情。</p> <p>这就是我们要做的!</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-2.gif" alt="" /></p> <h2>我们开始吧</h2> <p>我们要做的Loading效果是由三个小的圆圈组成,他们有一个周期的上下运动,每一个都和其他的不同步。</p> <p>我把它分解成几个部分,首先,我们会得到一个小圆在一个周期上下做平滑的运动。稍后我们将计算其余部分。开始撸码吧。</p> <h3>圆的定位</h3> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-3.png" alt="" /></p> <p>以上的代码展示的是在<code>&lt;svg&gt;</code>元素的中心绘制了一个小圆。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-4.png" alt="" /></p> <p>来理解一下它是怎么做的。</p> <p>其中<code>width</code>和<code>height</code>正如你想象的一样。为了简单起见,它们就是<code>svg</code>元素的<code>width</code>和<code>height</code>,然后构成一个盒子(大家懂的,HTML中的任何一个元素都是一个盒子)。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-5.png" alt="" /></p> <p>默认情况之下,<code>SVG</code>盒子具有传统的坐标系统,其原点位于左上角,以及<code>x</code>,<code>y</code>值分别向右和向下递增。而且,每个单元对应一个像素,这样盒子的四个角就会根据给定的<code>width</code>和<code>height</code>得到合适的坐标。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-6.png" alt="" /></p> <p>下一步,将需要做一个简单的小学数学的游戏。盒子的中心根据一定的公式(<code>width/2</code>,<code>height/2</code>)可以计算出来其坐标是<code>(150,75)</code>。我们将这个值分配给<code>cx</code>和<code>cy</code>,并且将其设置为圆的圆心。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-7.png" alt="" /></p> <h3>让圆动起来</h3> <p>接下来的目标是要让圆动起来。不是任何形式的动起来。我们需要圆的运动是在一个周期内做上下运动。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-8.png" alt="" /></p> <h3>周期性的计算</h3> <p>周期是发生在有规律间隔的任何东西。周期性的最简单的例子是每天日出日落。无论现在是什么时候,比如下午6:30,24小时后,又会是下午6:30,从那时起24小时将会是下午6:30。这是正常的,这是24小时内发生的事情。</p> <p>如果是中午,太阳在天空的最高点,24小时后它会再次出现。或者,如果是傍晚,太阳刚刚接触地平线,24小时后,它又会重新开始。你明白我的意思了吗?</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-9.png" alt="" /></p> <p>这是一个非常简单的描述,有些人会说,甚至是不准确,不科学,但我想它仍然会让太阳重复它的之前的位置,这很好。</p> <p>如果我们把太阳的垂直位置与白天的时间画在一起,我们就能更清楚地看到它的周期性。</p> <p>为了绘制任何二维曲线,我们需要两个值,<code>x</code>和<code>y</code>。在我们的示例中就对应的是<code>time</code>和<code>positionOfTheSun</code>。我们收集一组值,然后把它们放在一个图上,看到的效果就如下:</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-10.png" alt="" /></p> <p>纵轴<code>y</code>是太阳在天空中的垂直位置(<code>positionOfTheSun</code>),横轴<code>x</code>代表的是时间(<code>time</code>)。随着时间的推移,太阳的位置会改变,并且会在24小时后重复同样的一组值。</p> <p>有了这个图形,就能算出太阳在天空中的位置。接下来看看如何做到这一点,首先,让我们来给我们的图命名,比如叫作<code>sunsVerticalPositionAt</code>。</p> <p>一旦我们有了这个,我们就可以形成一个等式:</p> <pre><code>verticalPositionInTheSky = sunsVerticalPositionAt( [time] ) </code></pre> <p>我们只需要在图(或者从数学上说,到我们的函数)中输入时间,就可以计算出太阳的位置。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-11.png" alt="" /></p> <p>我们选择一个时间的点(比如<code>t1</code>),画一条直线(与<code>y</code>轴平行)将与曲线相交的地方就是我们想要知道的太阳位置。</p> <p>我们继教往下吧,直接进入数学。从图中除去太阳和其他的装饰物,这就得到如下的图:</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-12.png" alt="" /></p> <p>这幅图代表了周期性。一个实体(在我们的示例中是太阳的垂直位置)重复它作为另一个实体(在我们的例子中的时间)的值。</p> <p>数学中不止有几个周期函数,但我们坚持最基本的和周期函数的特征,我们将用它来创建完美的Loading效果,比如我们所知道的正弦函数<code>y = sin(x)</code>。</p> <p><code>y = sin(x)</code>的曲线图如下:</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-13.png" alt="" /></p> <p>你能看出这里的方程和我们计算出太阳在天空中位置的方程有相似之处吗?</p> <p>我们可以通过<code>x</code>得到<code>y</code>的值,就像通过时间计算太阳在天空中的位置。整个过程都不离开整个曲线。</p> <p>如果你在想什么是<a href="http://www.xysjxj.com/quot;//en.wikipedia.org/wiki/Sine"><code>sin</code></a>?这只是一个函数的名字,就像我们在图(函数)中给出了<code>sunsVerticalPositionAt</code>的名称。</p>" <p>这里的重点是<code>y</code>和<code>x</code>。看看,随着<code>x</code>的变化,<code>y</code>的值是怎样变化的(你能把这与我们的太阳在天空中改变其垂直位置的例子联系起来吗?)。</p> <p>你可能也注意到了,<code>y</code>的最大值是<code>1</code>,其最小值是<code>-1</code>。这是正弦函数的一个特征。<code>y=sin(x)</code>的取值范围是<code>-1</code>至<code>1</code>。</p> <p>但是这个范围可以通过一个简单的操作来改变。这是我们要做的。但在此之前,让我们把所学到的东西都拿出来,不管我们能做多少,都要让这个圆动起来。</p> <h3>代码中的数学</h3> <p>到目前为止,在<code>&lt;svg&gt;</code>中的<code>&lt;circle&gt;</code>元素有一个<code>id</code>名为<code>c</code>。我们可以通过JavaScript选择到这个圆,并让它动起来。</p> <pre><code>let c = document.getElementbyId('c'); animate(); function animate() { requestAnimationFrame(animate); } </code></pre> <p>上面的代码很简单,首先,我们把目标存储在一个变量<code>c</code>中。</p> <p>接下来,我们使用<code>requestAnimationFrame</code>和一个名为<code>animate</code>的函数。<code>animate</code>使用<code>requestAnimationFrame</code>递归调用自己,同时使用<code>requestAnimationFrame</code>来运行任何动画,并且让动画达到<code>60FPS</code>。有关于<code>requestAnimationFrame</code>更多的内容<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame">可以点击这里阅读</a>。</p>" <ul> <li><a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/requestAnimationFrame.html">被誉为神器的<code>requestAnimationFrame</code></a></li> <li><a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/animaton/animations-you-should-know.html">关于动画,你需要知道的</a></li>" <li><a href="http://www.xysjxj.com/quot;//www.paulirish.com/2011/requestanimationframe-for-smart-animating/"><code>requestAnimationFrame</code>" for Smart Animating</a></li> <li><a href="http://www.xysjxj.com/quot;//jinlong.github.io/2013/06/24/better-performance-with-requestanimationframe/"><code>requestAnimationFrame</code>" 性能更好</a></li> <li><a href="http://www.xysjxj.com/quot;//www.html5rocks.com/zh/tutorials/canvas/performance/">提" HTML5 画布性能</a></li> </ul> <p>来看代码,更有意义:</p> <pre><code>let c = document.getElementById('c'); let currentAnimationTime = 0; const centreY = 75; animate(); function animate() { c.setAttribute('cy', centreY + (Math.sin(currentAnimationTime))); currentAnimationTime += 0.15; requestAnimationFrame(animate); } </code></pre> <p>就只增加了四行代码。如果你运行这个,你会看到圆在它的中心慢慢地移动,整个称动效果如下:</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-14.gif" alt="" /></p> <p>这里发生了什么?</p> <p>一旦我们得到了圆心的坐标<code>cx</code>和<code>cy</code>,也就是<code>50%</code>的地方。我们要做的就是把<code>cx</code>保持不变,因为我们不想改变圆的水平位置。只需要周期性地从<code>cy</code>中加减相等的数,使圆在<code>y</code>轴上下移动。这就是我们在代码中所做的事情。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-15.png" alt="" /></p> <p><code>centreY</code>存储的值就是圆的中心(<code>75</code>)的值,因此可以将数字加到它上面或从它上面减去。这样就改变了圆在<code>y</code>轴上的位置。</p> <p><code>currentAnimationTime</code>是一个初始化为<code>0</code>的数字,它决定了动画的速度,我们在每次调用中增加的次数越多,动画就会越快。这里是<code>0.15</code>,因为它看起来是一个足好的速度值。</p> <p><code>currentAnimationTime</code>是正弦曲线(函数)中的<code>x</code>。当<code>currentAnimationTime</code>的值增加时,我们将它传递给<code>Math.sin</code>,然后将生成的数字添加到<code>centreY</code>。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-16.png" alt="" /></p> <p>然后使用<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en/docs/Web/API/Element/setAttribute"><code>setAttribute</code></a>将这个数字分配到<code>cy</code>。</p>" <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-17.png" alt="" /></p> <p>正如我们所知道的,<code>sin(x)</code>产生的<code>x</code>的值都在<code>-1</code>至<code>1</code>之间。因此<code>cy</code>的值的范围是从最小的<code>centreY - 1</code>到最大的<code>centreY + 1</code>。这使得圆在其位置上以<code>1</code>像素的幅度垂直抖动。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-18.png" alt="" /></p> <p>我们想要增加移动的距离。这意味着需要一个大于<code>1</code>的数。怎么做呢?我们需要一个新函数吗?并不是。</p> <p>还记得前面介绍的内容吗?这很简单,我们要做的就是反<code>sin(x)</code>乘以我们想要的间距数。用一个常数乘以一个函数的运算叫做缩放。注意图形是如何改变形状的,也注意乘法对正弦最大值和最小值的影响。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-19.png" alt="" /></p> <p>现在我们知道怎么修改我们的代码了。</p> <pre><code>let c = document.getElementById('c'); let currentAnimationTime = 0; const centreY = 75; animate(); function animate() { c.setAttribute('cy', centreY + (20 *(Math.sin(currentAnimationTime)))); currentAnimationTime += 0.15; requestAnimationFrame(animate); } </code></pre> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-20.gif" alt="" /></p> <p>这样一来就产生了一个非常平滑的圆形动画。是不是很可爱。我们所做的是通过一个数字乘以一个数字来增加正弦函数的幅度。接下来我们要做的是在这个圆的两边加上两个新的圆,让它们以同样的方式动起来。</p> <pre><code>&lt;svg width="300" height="150"&gt; &lt;circle id="cLeft" cx="120" cy="75" r="10" /&gt; &lt;circle id="cCentre" cx="150" cy="75" r="10" /&gt; &lt;circle id="cRight" cx="180" cy="75" r="10" /&gt; &lt;/svg&gt; </code></pre> <p>上面的代码让我们在<code>svg</code>中重新添加了两个圆<code>circle</code>,其中一个放置在原来圆左侧的<code>30px</code>处(<code>150 - 30 = 120</code>),另一个放置在原来圆右侧的<code>30px</code>处(<code>150 + 30 = 180</code>)。</p> <p>前面的示例中只有一个圆,所以用一个<code>id</code>为<code>c</code>是有效的,因为这是唯一的圆。但是我们现在总共有<code>3</code>个圆,所以可以给它们一个描述性的<code>id</code>。这样从左到右,就可以命名为<code>cLeft</code>、<code>cCentre</code>和<code>cRight</code>。原来<code>id</code>为<code>c</code>的变成了现在的<code>cCentre</code>。</p> <p>运行上面的代码,你所看到的效果如下:</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-21.gif" alt="" /></p> <p>新添加的圆根本不动!接下来,咱们就要让他们动起来。</p> <pre><code>let cLeft= document.getElementById('cLeft'), cCenter = document.getElementById('cCenter'), cRight = document.getElementById('cRight'); let currentAnimationTime = 0; const centreY = 75; const amplitude = 20; animate(); function animate() { cLeft.setAttribute('cy', centreY + (amplitude *(Math.sin(currentAnimationTime)))); cCenter.setAttribute('cy', centreY + (amplitude * (Math.sin(currentAnimationTime)))); cRight.setAttribute('cy', centreY + (amplitude * (Math.sin(currentAnimationTime)))); currentAnimationTime += 0.15; requestAnimationFrame(animate); } </code></pre> <p>有一些额外的代码是针对新圆的,并将相同的动画代码应用到<code>cCentre</code>中,得到的效果如下:</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-22.gif" alt="" /></p> <p>现在圆是动起来了,但这并不是我们想要的Loading动效。</p> <p>虽然圆在周期性的动,但问题是所有圆都在同步运动。这不是我们想要的。我们希望每个连续的圆都有一些延迟。所以它起起来像一个圆,而不是开始的那个圆是在它之前的圆的运动。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-23.gif" alt="" /></p> <p>你是否注意到,在第一个圆之后,每个圆与左边的圆稍微有点不同步。如果你用手掌来隐藏其他两个圆圈,你会注意到可见的圆仍然在执行我们最前面的动效。</p> <p>现在,为了让圆的动画不同步,我们需要对代码做一些微小的调整。但要理解这种微小的变化是如何起作用的。</p> <p>如果我们把每个圆的动效画成之前的图,那图就如下所示:</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-24.png" alt="" /></p> <p>这里没有啥惊喜,因为我们知道每个圆都在以同样的方式运动。要知道,既然我们用正弦函数来做动画,上面所有的曲线都只是正弦函数的图。为了让这些图不同步,需要理解平移/图形的数学概念。</p> <p>位移是一种硬性的转换,它不会改变函数图形的形状或大小。一个移位所要做的就是改变图形的位置。位移可以是水平的,也可以是垂直的。对于我们的意图和目的,我们感兴趣的是水平位移。</p> <p>注意,下面的动图演示了如何改变<code>y = sin(x)</code> 曲线图水平移动。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-25.gif" alt="" /></p> <p>为了理解这是如何运作的,让我们回到太阳升起和日落的比喻。</p> <p>我们的函数又是什么?<code>sunsVerticalPositionAt(t)</code>。这是正确的!可以把任何时间都传递到这个函数,在特定的时间位置将得到太阳在天空中的垂直位置。因此,要想知道上午9点钟的太阳位置,我们就可以通过<code>sunsVerticalPositionAt(9)</code>来得到。</p> <p>现在来考虑一下函数<code>sunsVerticalPositionAt(t - 3)</code>。注意这里,无论<code>time(t)</code>传递到这个新函数(它取<code>t-3</code>,而不是<code>t</code>),将得到太阳位置的值要比<code>t</code>早3小时。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-26.png" alt="" /></p> <p>也就是说,<code>t=9</code>时的值是<code>6</code>,<code>t=12</code>的值是<code>9</code>,依此类推。我们已经用这种方式连接了函数。换句话说,函数返回的值要比<code>t</code>的值更早。</p> <p>我们也可以说,已经把函数的图形平移到<code>x</code>轴上。注意,下图中<code>t=6</code>的旧图给出了<code>B</code>的值。当图形移位后,同样的值<code>B</code>在<code>t=9</code>时被移位的图。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-27.png" alt="" /></p> <p>同样的,如果我们加<code>3</code>而不是做减法。<code>sunsVerticalPosition(t + 3)</code>图形就会向左平移,或者换句话说,它会在<code>3</code>小时后给出数值。</p> <p>有了这个概念的知识,我们现在可以做的是改变图形来决定最后两个圆的动画。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-28.png" alt="" /></p> <p>要做到这一点,需要对代码做一些小调整:</p> <pre><code>let cLeft= document.getElementById('cLeft'), cCenter = document.getElementById('cCenter'), cRight = document.getElementById('cRight'); let currentAnimationTime = 0; const centreY = 75; const amplitude = 20; animate(); function animate() { cLeft.setAttribute('cy', centreY + (amplitude *(Math.sin(currentAnimationTime)))); cCenter.setAttribute('cy', centreY + (amplitude * (Math.sin(currentAnimationTime - 1)))); cRight.setAttribute('cy', centreY + (amplitude * (Math.sin(currentAnimationTime - 2)))); currentAnimationTime += 0.15; requestAnimationFrame(animate); } </code></pre> <p>这就是我们想要的。我们移动了<code>cCentre</code>和<code>cRight</code>动画的图形。</p> <p><img src="/sites/default/files/blogs/2018/1805/css-loader-animation-29.gif" alt="" /></p> <p>在这里。我们Loading动画的圆周运动绝对精确。你可以一直使用不同的值,比如增加到<code>currentAnimationFrame</code>,以控制速度或幅度来控制偏移量,但Loading能按你想要的方式进行。</p> <div style="margin-bottom: 20px;"><iframe id="gzzypR" src="//codepen.io/airen/embed/gzzypR?height=400&amp;theme-id=0&amp;slug-hash=gzzypR&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <div class="blog-author media"><a class="media-object" href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank"><img src="/sites/default/files/blogs/author/airen.jpg"></a><div class="media-body"><h3 class="media-heading"><a href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank">大漠</a></h3><div class="media-des">常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等韦德1946手机版客户端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/book-comment.html"" target="_blank">图解CSS3:核心技术与案例实战</a>》。</div></div></div> <p>如需转载,烦请注明出处:<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/animation/how-you-can-use-simple-trigonometry-to-create-better-loaders.html">https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/animation/how-you-can-use-simple-trigonometry-to-create-better-loaders.html</a></p> rel="nofollow" </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/translations"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">译文</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/tags/29.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Animation</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/532.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Web动画</a></div></div></div> Sat, 12 May 2018 09:48:08 +0000 Airen 2407 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/vue/how-to-create-data-driven-user-interfaces-in-vue.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><blockquote> <p>特别声明,本文来源于<a href="http://www.xysjxj.com/quot;//blog.rangle.io/author/evan/">@Eva" Schultz</a>的《<a href="http://www.xysjxj.com/quot;//blog.rangle.io/how-to-create-data-driven-user-interfaces-in-vue/">D" it with Elegance: How to Create Data-Driven User Interfaces in Vue</a>》。</p> </blockquote> <p>虽然我们通常知道在应用程序中构建大多数视图(View)时需要哪些韦德娱乐平台(Component),但很多时候我们并不知道这些韦德娱乐平台在运行时是什么。这意味着我们需要基于应用程序状态、用户首选项或API的响应来构建一个页面。一个常见的情况是构建动态表单,其中需要整清楚的是:<strong>韦德娱乐平台是由JSON对象配置的,还是基于用户的答案而更改的字段来构建</strong>。</p> <p>所有现代JavaScript框架(比如我们熟悉的Vue、React等)都有处理动态韦德娱乐平台的方法。这篇文章将向您展示如何在Vue中实现它。JavaScript为上述场景提供了一个非常优雅和简单的解决方案。</p> <p>一旦你看到使用Vue是这么的容易,你可能会受到启发,看到你从未考虑过的动态韦德娱乐平台的应用程序!</p> <p>在运行之前,我们需要先走一步,先介绍动态韦德娱乐平台的基础知识,然后深入了解如何使用这些概念来构建你自己的动态表单构造器。</p> <h2>基础知识</h2> <p>Vue有一个内置韦德娱乐平台,称为<code>&lt;component&gt;</code>。你可以在<a href="http://www.xysjxj.com/quot;//vuejs.org/v2/guide/components.html#Dynamic-Components">Vue指南</a>中看到有关于动态韦德娱乐平台的完整细节。</p>" <p>Vue指南是这样描述的:</p> <blockquote> <p>你可以使用相同的挂载点,并使用保留元素动态地在多个韦德娱乐平台之间切换,并动态绑定到它的属性。</p> </blockquote> <p>这意味着在韦德娱乐平台之间进行交换变得很简单:</p> <pre><code>&lt;component :is="componentType"&gt; </code></pre> <p>让我们把它具体化一些,看看会发生什么。首先创建两个韦德娱乐平台,分别名为<code>DynamicOne</code>和<code>DynamicTwo</code>。这两个韦德娱乐平台中的代码是相同的,只是<code>name</code>的值不一样:</p> <pre><code>&lt;template&gt; &lt;div&gt;Dynamic Component One&lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name: 'DynamicOne', } &lt;/script&gt; </code></pre> <p>在我们的<code>App.vue</code>中实现快速切换这两个韦德娱乐平台:</p> <pre><code>import DynamicOne from './components/DynamicOne.vue' import DynamicTwo from './components/DynamicTwo.vue' export default { name: 'app', components: { DynamicOne, DynamicTwo }, data() { return { showWhich: 'DynamicOne' } } } </code></pre> <blockquote> <p>注意,<code>data</code>中的<code>showWhich</code>属性值是<code>DynamicOne</code>的字符串值,这是<code>components</code>对象中创建的<code>name</code>的值。</p> </blockquote> <p>在我们的模板中,我们将设置两个按钮来切换两个动态韦德娱乐平台:</p> <pre><code>&lt;button @click="showWhich = 'DynamicOne'"&gt;Show Component One&lt;/button&gt; &lt;button @click="showWhich = 'DynamicTwo'"&gt;Show Component Two&lt;/button&gt; &lt;component :is="showWhich"&gt;&lt;/component&gt; </code></pre> <p>点击按钮将会用<code>DynamicTwo</code>替换<code>DynamicOne</code>。</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/o5n874qynz?autoresize=1&hidenavigation=1&view=preview" style="width:100%; height:400px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <p>这个时候你可能会说,这又怎么样?是很方便,但也可以使用<code>v-if</code>来实现同样的效果。</p> <p>当你意识到<code>&lt;component&gt;</code>和其他韦德娱乐平台一样工作时,这个示例就开始发挥作用了,它可以与<code>v-for</code>这样的Vue指令结合起来使用,用于对迭代集合,或者使用<code>:is</code>可以绑定到一个<code>input</code>、<code>data</code>的<code>props</code>或者是<code>computed</code>属性。</p> <h2>props和events</h2> <p>韦德娱乐平台不是孤立存在的 —— 它们需要一种与周围世界交流的方式。如果使用Vue的话,那么这种交流是通过<code>props</code>和<code>events</code>来完成的。</p> <p>你可以在动态韦德娱乐平台上指定与其他韦德娱乐平台相同的属性和事件绑定,如果加载的韦德娱乐平台不需要该属性,Vue将不会抱怨未知的"Attributes”或“properties”。</p> <p>让我们修改韦德娱乐平台显示的内容。<code>DynamicOne</code>韦德娱乐平台只接受<code>firstName</code>和<code>lastName</code>,而另一个韦德娱乐平台<code>DynamicTwo</code>接受<code>firstName</code>、<code>lastName</code>和<code>title</code>。</p> <p>对于事件,我们将在<code>DynamicOne</code>中添加一个按钮,它将发出(<code>emit</code>)一个名为<code>upperCase</code>的事件;而在<code>DynamicTwo</code>中将发出一个名为<code>lowerCase</code>事件。</p> <p>把它放在一起,动态韦德娱乐平台看起来像这样:</p> <pre><code>&lt;component :is="showWhich" :firstName="person.firstName" :lastName="person.lastName" :title="person.title" @upperCase="switchCase('upperCase')" @lowerCase="switchCase('lowerCase')"&gt; &lt;/component&gt; </code></pre> <p>事实上并不是每个属性或事件都需要定义在我们正在切换的动态韦德娱乐平台上。</p> <h2>先要了解props</h2> <p>此时,你可能会想,“如果韦德娱乐平台是动态的,而不是每个韦德娱乐平台都需要知道所有可能的<code>props</code> —— 需要知道<code>props</code>之前,并在模板中声明它们吗?”</p> <p>值得庆幸的是,答案是否定的。Vue提供了一个快捷方式,可以使用<code>v-bind</code>将对象的所有键绑定到韦德娱乐平台的<code>props</code>。</p> <p>这个时候模板可以简化为:</p> <pre><code>&lt;component :is="showWhich" v-bind="person" @upperCase="switchCase('upperCase')" @lowerCase="switchCase('lowerCase')"&gt; &lt;/component&gt; </code></pre> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/m3yyr40489?autoresize=1&hidenavigation=1&view=preview" style="width:100%; height:400px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <h2>表单形式</h2> <p>现在我们已经有了一个动态韦德娱乐平台区块,接下来可以开始构建一个表单生成器。</p> <p>让我们从一个基本的表单模式开始 —— 一个JSON对象,它描述了表单的字段、标签和选项等:</p> <ul> <li>文本和数字输入域</li> <li>一个下拉列表</li> </ul> <p>开始的<code>schema</code>看起来像这样:</p> <pre><code>schema: [ { fieldType: "SelectList", name: "title", multi: false, label: "Title", options: ["Ms", "Mr", "Mx", "Dr", "Madam", "Lord"] }, { fieldType: "TextInput", placeholder: "First Name", label: "First Name", name: "firstName" }, { fieldType: "TextInput", placeholder: "Last Name", label: "Last Name", name: "lastName" }, { fieldType: "NumberInput", placeholder: "Age", name: "age", label: "Age", minValue: 0 } ] </code></pre> <p>非常简单。在本例中,将保持这些简单的韦德娱乐平台实现。</p> <p><code>TextInput.vue</code>:</p> <pre><code>&lt;template&gt; &lt;div&gt; &lt;label&gt;{{ label }}&lt;/label&gt; &lt;input type="text" :name="name" :placeholder="placeholder" /&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name: 'TextInput', props: ['placeholder', 'lable', 'name'] } &lt;/script&gt; </code></pre> <p><code>SelectList.vue</code>:</p> <pre><code>&lt;template&gt; &lt;div&gt; &lt;label&gt;{{ label }}&lt;/label&gt; &lt;select :multiple="nulti"&gt; &lt;option v-for="option in options" :key="option"&gt;{{ option }}&lt;/option&gt; &lt;/select&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { name:'SelectList', props: ['multi', 'options', 'name', 'label'] } &lt;/script&gt; </code></pre> <p>基于这个<code>schema</code>要生成表单,还要加加下面的内容:</p> <pre><code>&lt;component v-for="(field, index) in schema" :key="index" :is="field.fieldType" v-bind="field"&gt; &lt;/component&gt; </code></pre> <p>结果如下:</p> <div style="margin-bottom: 20px;"> <iframe src="//codesandbox.io/embed/kywqj5o1v?autoresize=1&hidenavigation=1&view=preview" style="width:100%; height:400px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <h2>数据绑定</h2> <p>如果表单生成但不绑定数据,估计也没啥有用。上面生成了一个表单,但没有绑定数据。你可以第一反应就是在韦德娱乐平台中使用<code>v-model</code>来绑定<code>schema</code>中的<code>value</code>属性。</p> <pre><code>&lt;input type="text" :name="name" v-model="value" :placeholder="placeholder"&gt; </code></pre> <p>这种方法有一些潜在的缺陷,但是Vue会给我们反馈相应的错误或警告信息:</p> <p><img src="/sites/default/files/blogs/2018/1805/vue-data-interface-1.png" alt="" /></p> <p>虽然Vue确实给韦德娱乐平台提供双向数据绑定,但框架仍然倾向于单向数据流。我们试图在韦德娱乐平台中直接改变父类的数据,所以Vue会警告我们,如上图所示。</p> <p>仔细看看<code>v-model</code>,其实他没有那么多的魔力,所以让我们按照<a href="http://www.xysjxj.com/quot;//vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events">Vue指南中关于表单输入韦德娱乐平台</a>的描述来分解它。</p>" <pre><code>&lt;input v-model="something"&gt; </code></pre> <p>它等同于:</p> <pre><code>&lt;input v-bind:value="something" v-on:input="something = $event.target.value"&gt; </code></pre> <p>我们想要完成的是:</p> <ul> <li>让父元素为子韦德娱乐平台提供值</li> <li>让父节点知道一个值已被更新</li> </ul> <p>通过绑定到<code>:value</code>并发出一个<code>@input</code>事件来通知父元素,进程发生了一些变化。</p> <p>根据这个描述对<code>TextInput</code>韦德娱乐平台进行修改:</p> <pre><code>&lt;div&gt; &lt;label&gt;{{label}}&lt;/label&gt; &lt;input type="text" :name="name" :value="value" @input="$emit('input',$event.target.value)" :placeholder="placeholder"&gt; &lt;/div&gt; </code></pre> <p>由于父类负责提供值,所以它也负责处理绑定到它自己的韦德娱乐平台状态。为此,我们可以在<code>&lt;component&gt;</code>签上使用<code>v-model</code>:</p> <p><code>FormGenerator.vue</code>:</p> <pre><code>&lt;component v-for="(field, index) in schema" :key="index" :is="field.fieldType" v-model="formData[field.name]" v-bind="field"&gt; &lt;/component&gt; </code></pre> <p>注意,我们如何使用<code>v-model="formDtata[field.name]"</code>。我们需要在数据属性上提供一个对象:</p> <pre><code>export default { data() { return { formData: { firstName: 'Evan' }, } } } </code></pre> <p>我们可以让对象为空,或者如果我们有一些我们想要设置的初始字段,就可以在这里指定它们。</p> <p>现在我们已经完成了生成表单的过程,这个韦德娱乐平台承担了相当多的责任。虽然代码不是很复杂,但是表单生成器自身要是可复用的韦德娱乐平台,那就更好了。</p> <h2>让生成器可重用</h2> <p>对于这个表单生成器,我们希望将<code>schema</code>传递给<code>prop</code>,并能够在韦德娱乐平台之间设置数据绑定。</p> <p>当使用生成器时,模板<code>GeneratorDemo.vue</code>变为:</p> <pre><code>&lt;form-generator :schema="schema" v-model="formData"&gt;&lt;/form-generator&gt; </code></pre> <p>这对父韦德娱乐平台进行了相当大的清理。它只关心<code>FormGenerator</code>,并不关心可以使用的每个输入类型、连接事件等。</p> <p>接下来,创建一个名为<code>FormGenerator</code>的韦德娱乐平台。这将最初可复制的代码变得并不重要,重要的是做了一些关键的调整:</p> <ul> <li>从<code>v-model</code>改变<code>:value</code>和<code>@input</code>事件处理</li> <li>在<code>props</code>中添加<code>value</code>和<code>schema</code></li> <li>实现<code>updateForm</code></li> </ul> <p><code>FormGenerator</code>韦德娱乐平台变成这样:</p> <pre><code>&lt;component v-for="(field, index) in schema" :key="index" :is="field.fieldType" :value="formData[field.name]" @input="updateForm(field.name, $event)" v-bind="field"&gt; &lt;/component&gt; import NumberInput from '@/components/v5/NumberInput' import SelectList from '@/components/v5/SelectList' import TextInput from '@/components/v5/TextInput' export default { name: "FormGenerator", components: { NumberInput, SelectList, TextInput }, props: ['schema', 'value'], data() { return { formData: this.value || {} }; }, methods: { updateForm(fieldName, value) { this.$set(this.formData, fieldName, value); this.$emit('input', this.formData) } } }; </code></pre> <p>由于<code>formData</code>属性并不知道我们可以传入所有可能的字段,所以希望使用<code>this.$set</code>让Vue可以跟踪任何变化,并允许<code>FormGenerator</code>韦德娱乐平台跟踪它自己的内部状态。</p> <p>现在我们有了一个基本的可重用的表单生成器。可以在一个韦德娱乐平台内使用它:</p> <p><code>GeneratorDemo.vue</code>:</p> <pre><code>&lt;form-generator :schema="schema" v-model="formData"&gt;&lt;/form-generator&gt; import FormGenerator from '@/components/v5/FormGenerator' export default { name: "GeneratorDemo", components: { FormGenerator }, data() { return { formData: { firstName: 'Evan' }, schema: [{ /* .... */ }, } } } </code></pre> <div style="margin-bottom: 20px;"> <iframe src="https://codesandbox.io/embed/921693y3mw?autoresize=1&hidenavigation=1&view=preview" style="width:100%; height:400px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe> </div> <p>现在,你已经了解了表单生成器。通过这个简单的示例学习了如何利用Vue的动态韦德娱乐平台的基础来创建一些高度动态,数据驱动的UI。文章中整个示例的代码可以在<a href="http://www.xysjxj.com/quot;//github.com/e-schultz/vue-dynamic-components">GitHub</a>上获取到,或者在<" href="http://www.xysjxj.com/quot;//codesandbox.io/s/github/e-schultz/vue-dynamic-components/tree/master/?initialpath=%2Fdemo-5&amp;module=%2Fsrc%2Fcomponents%2FDemoFive.vue">CodeSandbox</a>上进行修改。如果你有任何问题或想要聊的地方,可以通过<" href="http://www.xysjxj.com/quot;//twitter.com/@e_p82">Twitter</a>、<" href="http://www.xysjxj.com/quot;//blog.rangle.io/how-to-create-data-driven-user-interfaces-in-vue/github.com/e-schultz">GitHub</a>或<" href="http://www.xysjxj.com/quot;//evan@rangle.io">电子邮件</a>联系我。你也可以在下面的评论中与我们一起探讨相关的话题。</p>" <div class="blog-author media"><a class="media-object" href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank"><img src="/sites/default/files/blogs/author/airen.jpg"></a><div class="media-body"><h3 class="media-heading"><a href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank">大漠</a></h3><div class="media-des">常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等韦德1946手机版客户端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/book-comment.html"" target="_blank">图解CSS3:核心技术与案例实战</a>》。</div></div></div> <p>如需转载,烦请注明出处:<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/vue/how-to-create-data-driven-user-interfaces-in-vue.html">https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/vue/how-to-create-data-driven-user-interfaces-in-vue.html</a></p> rel="nofollow" </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/translations"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">译文</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/vue"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Vue</a></div><div class="field-item odd"><a href="http://www.xysjxj.com/quot;/blog/tags/642.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Vue韦德娱乐平台</a></div></div></div> Fri, 11 May 2018 10:51:28 +0000 Airen 2406 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/lazy-loading-images-using-intersection-observer.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><blockquote> <p>特别声明:本文转载<a href="http://www.xysjxj.com/quot;//c7sky.com/">@小影</a>的译文《<" href="http://www.xysjxj.com/quot;//c7sky.com/lazy-loading-images-using-intersection-observer.html">使" Intersection Observer 实现图片延迟加载</a>》一文。</p> </blockquote> <p>如果你还没有机会看过,@Paul Lewis 发布了一个精彩的视频系列,演示了如何构建一个媒体播放器以及渐进式 Web 应用(Progressive Web App)的一些重要功能。在 <a href="http://www.xysjxj.com/quot;//www.youtube.com/playlist?list=PLNYkxOF6rcIBykcJ7bvTpqU7vt-oey72J">YouTub" 上的这一系列视频</a> 包括了他在建立网站时的各个部分。观看开发者展示他们工作的视频绝对是我最喜欢的学习新事物的方式!</p> <p><img src="/sites/default/files/blogs/2018/1805/paul-lewis-intersection-observer.png" alt="" /></p> <p>当他在演示他的代码时,我注意到他创建了一个很棒的小型辅助库来延迟加载图片。我一直在寻找方法来改进我的网站性能,所以这无疑引起了我的兴趣。</p> <h2>延迟加载有什么大不了的?</h2> <p>延迟加载图片的目的在于,在发出对应的网络请求之前你要一直等到用户滚动页面让图片进入可见区域。如果你的网页包括了多张图片,但你只在每张图片滚动到视区时才加载,你将最终会节省带宽并确保你的网页加载的更快。</p> <p>为了让你在实际中了解这一点,让我们想象下面这个页面,他有三张图片。</p> <p><img src="/sites/default/files/blogs/2018/1805/intersection-observer.png" alt="" /></p> <p>如果用户访问页面时仅看到了第一张图,我们并不想加载网页底部的比萨图片,除非用户向下滚动直到图片真正进入视区。如果我们延迟加载图片,这意味着用户只在他们需要时下载他们需要的东西,这让你的网页更加精简。</p> <p>对于更有经验的开发者,你也许很熟悉延迟加载图片,毕竟这个概念已经有了一段时间。那么有什么新东西呢?!现在有很多延迟加载库可以完成这项工作。我在这个博客上曾经写过一篇(<a href="http://www.xysjxj.com/quot;//deanhume.com/Home/BlogPost/lazy-loading-images-with-jquery/22">很多年前</a>)。问题在于,几乎所有这些库都是利用滚动事件或者使用定时器来检查一个元素是否在视区内。这个方法的问题是它强制浏览器重新布局整个页面,而且在特定条件下会导致你的网站卡顿(jank)。我们可以做的更好!</p>" <h2>救星 Intersection Observer!</h2> <p>这就是 Intersection Observer 的用武之地。Intersection Observer 内置于大多数现代浏览器,可以让你知道一个被观察元素何时进入或离开了浏览器视口。这使得它很理想,因为它可以异步传输数据,并且不会影响主线程,这让它成为一个提供反馈的有效手段。</p> <p>在 <a href="http://www.xysjxj.com/quot;//github.com/googlechrome/sample-media-pwa">@Pau" 的示例</a>中,它展示了如何使用 Intersection Observer 来实现当图片进入视口时延迟加载。我使用了他的初始代码,并稍作修改让它更容易理解。在这篇文章中,我将过一遍 Intersection Observer 的基础知识并向你展示你该如何用一个超级高效的方法来延迟加载图片。</p> <h2>入门</h2> <p>想象一个有三张图片的基本的 HTML 页面,就像上面那个一样,在这个网页上,你将有如下代码的图片元素:</p> <pre><code>&lt;img class="js-lazy-image" data-src="burger.png"&gt; </code></pre> <p>你也许注意到上面的代码中,图片文件没有 <code>src</code> 属性么。这是因为它使用了称为 <code>data-src</code> 的 <code>data</code> 属性来指向图片源。我们将使用这来加载图片,当它们进入视口时。你也许还注意到图片元素还有一个称为 <code>js-lazy-image</code> 的 <code>class</code> —— 我们将很快在 JavaScript 代码中用它来确定我们想延迟加载的元素。</p> <p>接下来,我们需要创建延迟加载页面中图片的代码。在我们的 JavaScript 文件中(需要在页面中引用),我们需要创建一个新的 Intersection Observer。</p> <pre><code>// Get all of the images that are marked up to lazy load const images = document.querySelectorAll('.js-lazy-image'); const config = { // If the image gets within 50px in the Y axis, start the download. rootMargin: '50px 0px', threshold: 0.01 }; // The observer for the images on the page let observer = new IntersectionObserver(onIntersection, config); images.forEach(image =&gt; { observer.observe(image); }); </code></pre> <p>上面的示例中看起来像是一堆代码,让我们一步一步来分解。首先,我选择了页面中所有包含 <code>js-lazy-image</code> 类的图片。然后我创建了一个新的 <code>IntersectionObserver</code>,并用它来观察所有我们已经选择的带有 <code>js-lazy-image</code> 类的图片。使用 <code>IntersectionObserver</code> 的默认选项,你的回调会在元素部分进入视区和完全离开视口时被调用。在这个例子中,我会传递一些额外的配置选项给 <code>IntersectionObserver</code>。使用 <code>rootMargin</code> 允许你指定根元素的 <code>margin</code> 值,让你可以扩展或者缩减 <code>intersections</code> 的使用区域。我们想确保如果图片进入了 <code>Y</code> 轴的 <code>50px</code> 内,我们将开始下载。</p> <p>现在我们已经创建了一个 Intersection Observer 并且正在观察页面上的图片,我们现在来了解 <code>intersection</code> 事件,它将在元素进入视区时触发。</p> <pre><code>function onIntersection(entries) { // Loop through the entries entries.forEach(entry =&gt; { // Are we in viewport? if (entry.intersectionRatio &gt; 0) { // Stop watching and load the image observer.unobserve(entry.target); preloadImage(entry.target); } }); } </code></pre> <p>在上面的代码中,每当我们正在观察的元素进入到用户视口内,<code>onIntersection</code> 函数将被触发。此时,我们可以循环我们所观察的图片并确定哪一个在视口中。如果当前元素处在交叉比例(intersection ratio)之中,我们就知道这图片在用户视口之中并且我们可以加载它了。一旦图片加载完毕,我们不在需要观察它,使用 <code>unobserve()</code> 将它从 Intersection Observer 的观察列表中移除。</p> <p>就是这样!一旦用户滚动并且图片进入视区,对应的图片将被加载。这个代码最棒的地方在于 Intersection Observer 比 Barry White 更加流畅。我试图让上面的代码尽可能的简练,但是如果你想查看完整版本,我也创建了一个 <a href="http://www.xysjxj.com/quot;//github.com/deanhume/lazy-observer-load">Githu" 仓库</a> 和一个在线 <a href="http://www.xysjxj.com/quot;//deanhume.github.io/lazy-observer-load/">实例</a>。</p>" <h2>浏览器支持</h2> <p>此时此刻,你也许想知道关于这项特性的浏览器支持情况。Intersection Observer 现在已被 <a href="http://www.xysjxj.com/quot;//caniuse.com/#feat=intersectionobserver">Edge、Firefox、Chrom" 和 Opera</a> 支持,这是一个好消息。</p> <p>然而,为了确保我们的代码不会在不支持它的浏览器中造成破坏,我们可以使用特性检测来确定我们应该如何来加载图片。让我们看看下面的代码。</p> <pre><code>// If we don't have support for intersection observer, load the images immediately if (!('IntersectionObserver' in window)) { Array.from(images).forEach(image =&gt; preloadImage(image)); } else { // It is supported, load the images observer = new IntersectionObserver(onIntersection, config); images.forEach(image =&gt; { observer.observe(image); }); } </code></pre> <p>在上面的代码中,我们检查了 <code>IntersectionObserver</code> 是否在当前浏览器中可用,如果不可用我们就立即加载这些图片,否则使用我们的默认行为。</p> <p>如果你真的喜欢 Intersection Observer API 的易用性并想使用 polyfill 来替代。WICG 创建了一个放在了他们的 <a href="http://www.xysjxj.com/quot;//github.com/WICG/IntersectionObserver/tree/gh-pages/polyfill">Githu" 仓库</a>。唯一的缺点是你无法获取到原生实现给你带来的性能优势。</p> <p>你甚至可以更进一步,像 <a href="//www.robinosborne.co.uk/2016/05/16/lazy-loading-images-dont-rely-on-javascript/#a-no-JavaScript">@Robin Osborne</a> 建议的那样,为不启用 JavaScript 的用户添加支持。</p> <h2>总结</h2> <p>在这篇文章汇总,我们使用 Intersection Observer 来延迟加载图片,但你可以用它来做的更多。它可以用于判断某人是否正在查看广告,或者甚至是 <code>iframe</code> 中的元素是否在视区中。易于理解的 API 使它获得了更多的可能性。</p> <p>如果你想学习更多关于 Intersection Observer 的信息,我推荐阅读 Google Developers 网站上 <a href="http://www.xysjxj.com/quot;//developers.google.com/web/updates/2016/04/intersectionobserver">这篇内容丰富的文章</a>。我也强烈推荐观" Youtube 上 <a href="http://www.xysjxj.com/quot;//www.youtube.com/watch?v=ncYQkOrKTaI&amp;list=PLNYkxOF6rcIBykcJ7bvTpqU7vt-oey72J&amp;index=7">@Pau" Lewis 的视频系列</a>,它包含了许多很棒的技巧,你绝对会学到些东西。</p> <p>Oh,非常感谢 <a href="http://www.xysjxj.com/quot;//aerotwist.com/">@Paul</a>" 复查这篇文章!</p> <div class="blog-author media"><a class="media-object" href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank"><img src="/sites/default/files/blogs/author/airen.jpg"></a><div class="media-body"><h3 class="media-heading"><a href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank">大漠</a></h3><div class="media-des">常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等韦德1946手机版客户端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/book-comment.html"" target="_blank">图解CSS3:核心技术与案例实战</a>》。</div></div></div> <p>如需转载,烦请注明出处:<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/lazy-loading-images-using-intersection-observer.html">https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/lazy-loading-images-using-intersection-observer.html</a></p> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/translations"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">译文</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div></div></div> Wed, 09 May 2018 13:15:19 +0000 Airen 2405 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/different-types-of-observers-supported-by-modern-browsers.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><blockquote> <p>特别声明:本文来源于<a href="http://www.xysjxj.com/quot;//jkasaudhan.github.io/">@Jitendr" Kasaudhan</a>的《<a href="http://www.xysjxj.com/quot;//www.zeolearn.com/magazine/different-types-of-observers-supported-by-modern-browsers">Differen" Types Of Observers Supported By Modern Browsers</a>》一文。</p> </blockquote> <p>在深入了解现代浏览器支持的观察者之前,让我们试着先了解什么观察者是什么。</p> <h2>观察者是什么</h2> <p>观察者(Observer)是一个观察或注意事物的程序。观察者可以观察浏览器中发生的某些活动并做出相应的响应。观察者类似于狗,观察某些活动,并提醒我们发生了一些不寻常的事情。一旦我们为某些活动获取到狗发出的警告时,我们有责任采取相应的行动。</p> <p><img src="/sites/default/files/blogs/2018/1805/observers-type-1.jpeg" alt="" /></p> <p>使用观察者我们可以观察浏览器中发生的不同类型的活动(Activity),并采取必要的行动。比如,可以观察一个视频是否在视窗中显示,并启用自动播放;比如从父DOM元素中添加或删除子元素;比如一个盒了元素的大小尺寸发生变化等等。</p> <p>以下是现代浏览器支持的四种不同类型的观察者:</p> <ul> <li><strong>Intersection Observer</strong></li> <li><strong>Mutation Observer</strong></li> <li><strong>Resize Observer</strong></li> <li><strong>Performance Observer</strong></li> </ul> <h2>IntersectionObserver</h2> <p>它用于观察两个<a href="//www.zeolearn.com/html5-and-css3-training">HTML DOM</a>元素之间的交集。当DOM进入或离开可见的视窗(Visible Viewport)时,在DOM中观察一个元素是很有用的。下面描述了<a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API"><code>IntersectionObserver</code></a>的一些用例。</p>" <ul> <li>当一个元素在视窗中可见时,延迟加载图像或其他资淅</li> <li>识别广告的可见性并计算广告收入</li> <li>实现网站的无限滚动。当用户向下滚动页面时,不必要浏览不同的页面</li> <li>当一个元素在视窗中时,加载和自动播放、视频或动画</li> </ul> <p>目前,Firefox和Chrome支持<code>IntersectionObserver</code>,但对于不支持的浏览器,可以找到对应的Polyfill。</p> <p><img src="/sites/default/files/blogs/2018/1805/observers-type-2.png" alt="" /></p> <p>使用<code>IntersectionObserver</code> API主要需要三个步骤:</p> <ul> <li>创建观察者</li> <li>定义要观察的目标对象</li> <li>定义回调事件</li> </ul> <h3>创建观察者</h3> <pre><code>const options = { root: document.querySelector('.scrollContainer'), rootMargin: '0px', threshold: [0.3, 0.5, 0.8, 1] } const observer = new IntersectionObserver(handler, options) </code></pre> <p><code>[0.3]</code>的阈值意味着,当目标元素在根元素指定的元素内可见<code>30%</code>时,调用处理函数。上面意味着当元素被<code>30%</code>、<code>50%</code>、<code>80%</code>和<code>100%</code>可见时,将调用处理程序、回调函数。</p> <h3>定义要观察的目标对象</h3> <p>我们可以定义多个目标对象来观察。正如前面的例子所提到的,狗应该知道在提醒每个人之前应该观察什么。</p> <p>任何目标元素都可以通过调用<code>.observer(target)</code>方法来观察。</p> <pre><code>const target = document.querySelector(“.targetBox”); observer.observe(target); </code></pre> <h3>定义回调事件</h3> <p>这是当一个人注意到某件不寻常的事情发生时的反应。当目标元素与根元素通过阈值相交时,就会触发回调函数。</p> <pre><code>function handler (entries, observer) { entries.forEach(entry =&gt; { // 每个entry描述一个观察到的目标元素的交集变化 // entry.boundingClientRect // entry.intersectionRatio // entry.intersectionRect // entry.isIntersecting // entry.rootBounds // entry.target // entry.time }); }; </code></pre> <p>我已经准备了一个Demo,它使用<code>IntersectionObserver</code>来改变目标框的颜色。当用户滚动时,在可见区域内看到它。</p> <div style="margin-bottom: 20px;"><iframe id="deJJjQ" src="//codepen.io/airen/embed/deJJjQ?height=400&amp;theme-id=0&amp;slug-hash=deJJjQ&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <h2>MutationObserver</h2> <p><a href="http://www.xysjxj.com/quot;//developer.mozilla.org/en-US/docs/Web/API/MutationObserver"><code>MutationObserver</code></a>用于观察DOM元素的变化。可以观察到在父节点中添加或删除子节点、属性值或数据内容等变化。在<code>MutationObserver</code>之前,DOM更改事件由<code>Mutation</code>事件处理,比如<code>DOMAttrModified</code>、<code>DOMAttributeNameChanged</code>和<code>DOMNodeInserted</code>。</p>" <p><code>MutationObserver</code>被DOM3中定义的<code>Mutation</code>事件所替代。避免这些突变事件的实际原因是性能问题和跨浏览器支持。</p> <p>这个新<a href="http://www.xysjxj.com/quot;//www.zeolearn.com/magazine/creating-a-restful-api-service-with-flask">API</a>的最大受众可能是构建<" href="//www.zeolearn.com/magazine/most-popular-and-trending-javascript-frameworks-2017">JS框架</a>的人员,主要是解决问题和创建交互。另一个用例将是使用框架来操作DOM,并且需要对这些修改进行有效的响应(而不需要设置<code>setTimeout</code>)。</p> <p>这个API在不同的浏览器上都得到很好的支持。</p> <p><img src="/sites/default/files/blogs/2018/1805/observers-type-3.png" alt="" /></p> <p>同样的,<code>MutationObserver</code>的实现也需要三个步骤:</p> <h3>创建观察者</h3> <p>它可以通过调用它的构造函数和传递处理函数和配置选项来创建。我们有一个选项来指定我们想要跟踪或观察什么样的变化。</p> <pre><code>const config = { childList: true, attributes: true, characterData: true }; const observer = new MutationObserver(handler); </code></pre> <ul> <li><code>childList: true</code>,表示观察与子节点相关的变化</li> <li><code>attributes:true</code>,表示观察属性更改</li> <li><code>characterData; true</code>,表示观察目标元素的数据内容的变化</li> </ul> <h3>定义要观察的目标对象</h3> <p><code>observer.observe(...)</code>方法接受应该被观察到的目标元素。</p> <pre><code>const parent = document.querySelector(“.parent”); observer.observe(parent, config); </code></pre> <h3>定义回调事件</h3> <p>根据在观察者创建过程中使用的配置,当目标元素中发生更改时,会执行回调函数。回调函数是用突变记来对象触发的,该对象包含目标元素中发生的突变类型。</p> <pre><code>function handler(mutationRecords, observer) { mutationRecords.forEach((mutationRecord) =&gt; { switch(mutationRecord.type) { case “childList”: //child node added or removed break; case “attributes”: // attribute changed break; default: } }); } </code></pre> <p>我已经准备了一个Demo。该Demo将触发<code>MutationObserver</code>的回调处理程序,每当添加或删除新的节点时,都会使用属性更改。</p> <div style="margin-bottom: 20px;"><iframe id="eryVvK" src="//codepen.io/airen/embed/eryVvK?height=400&amp;theme-id=0&amp;slug-hash=eryVvK&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <h2>ResizeObserver</h2> <p><code>ResizeObserver</code>允许我们观察DOM元素的内容矩形大小(<code>width</code>、<code>height</code>)的变化,并做出相应的响应。它就像给元素添加<code>document.onresize()</code>或<code>window.resize()</code>事件。不调整视窗大小而只改变元素的大小,这是很有用的。例如,添加新的子元素,将元素的<code>display</code>设置为<code>none</code>或类似可以改变元素大小的的事件。事实上,它只关注内容框(Content Box)变化。比如下面这些行为将会触发<code>ResizeObserver</code>的行为:</p> <ul> <li>当观察到的元素被插入或从DOM中删除时,观察将会触发</li> <li>当观察到的元素<code>display</code>值为<code>none</code>时,观察都会触发</li> <li>观察不会对未替换的内联元素(non-replaced inline element)触发</li> <li>观察不会由CSS的<code>transform</code>触发</li> <li>如果元素有显示,而且元素大小不是<code>0,0</code>,观察将会触发</li> </ul> <p><code>ResizeObserver</code>通知内容框的大小,如下图所示:</p> <p><img src="/sites/default/files/blogs/2018/1805/resize-observer-7.png" alt="" /></p> <p>不幸的是,到目前为止仅有Chrome64+支持它,大多数浏览器都还不支持它。</p> <p><img src="/sites/default/files/blogs/2018/1805/observers-type-4.png" alt="" /></p> <p>同样的,<code>ResizeObserver</code>的使用也分为三步:</p> <h3>创建观察者</h3> <p>可以通过调用它的构造函数和传递处理函数来创建它。</p> <pre><code>const observer = new ResizeObserver(handler); </code></pre> <h3>定义要观察的目标对象</h3> <p>定义目标对象,其大小的变化应该被观察到。</p> <pre><code>const child = document.querySelector(“.child”); observer.observe(child); </code></pre> <h3>定义回调事件</h3> <pre><code>function handler (entries) { entries.forEach((entry) =&gt; { const size = entry.target.contentRect; console.log(`Element’s size: width: ${size.width} , height: ${size.height}`); }); } </code></pre> <p>比如下面这个Demo:</p> <div style="margin-bottom: 20px;"><iframe id="VxQbaR" src="//codepen.io/airen/embed/VxQbaR?height=400&amp;theme-id=0&amp;slug-hash=VxQbaR&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <blockquote> <p>有关于<code>ResizeObserver</code>更详细的介绍,<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/ResizeObserver-api.html">可以点击这里进行了解</a>。</p> </blockquote> <h2>PerformanceObserver</h2> <p>它主要用于观察性能时间轴(Performance Timeline),并在浏览器记录时通知新的性能条目。它可以用来度量浏览器和Node.js应用程序中某些性能指标。在浏览器中,我们可以使用<code>Window</code>对象作为<code>window.PerformanceObserver</code>,在Node.js应用程序中需要<code>perf_hooks</code>获得性能对象。比如,<code>const {performance} = require('perf_hooks')</code>。它在下列情况下是有用的。</p> <ul> <li>测量请求(Request)和响应(Response)之间的处理时间。(浏览器)</li> <li>在从数据库中检索数据时计算持续时间。(Node.js应用程序)</li> <li>抽象精确的时间信息,使用Paint Timing API,比如First Paint或First Contentful Paint时间</li> <li>使用“User Timing API”、"Navigation Timing API"、“Network Information API”、“Resource Timing API”和“Paint Timing API”访问性能指标</li> </ul> <p>执行<code>PerformanceObserver</code>需要三个步骤:</p> <h3>创建观察者</h3> <p>可以通过调用它的构造函数和传递处理函数来创建它。</p> <pre><code>const observer = new PerformanceObserver(logger); </code></pre> <h3>定义要观察的目标对象</h3> <p><code>observer.observe(...)</code>方法接受可以观察到的有效的入口类型。这些输入类型可能属于各种性能API,比如User tming或Navigation Timing API。有效的<code>entryType</code>值:</p> <ul> <li><code>"mark”</code>: [USER-TIMING]</li> <li><code>"measure"</code>: [USER-TIMING]</li> <li><code>"navigation"</code>: [NAVIGATION-TIMING-2]</li> <li><code>"resource"</code>: [RESOURCE-TIMING]</li> </ul> <p>代码:</p> <pre><code>// subscribe to User-Timing events const config = { entryTypes: [“mark”, “measure”] }; observer.observe(config); </code></pre> <h3>定义回调函数事件</h3> <p>当应用程序中使用观察到的事件时,会触发回调处理程序。</p> <pre><code>function getDataFromServer() { performance.mark(“startWork”); // see [USER-TIMING] doWork(); // Some developer code performance.mark(“endWork”); performance.measure(“start to end”, “startWork”, “endWork”); const measure = performance.getEntriesByName(‘start to end’)[0]; } function logger(list, observer) { const entries = list.getEntries(); entries.forEach((entry) =&gt; { console.log(“Name: “ + entry.name + “, Type: “ + entry.entryType + “, Start: “ + entry.startTime + “, Duration: “ + entry.duration + “\n”); }); } </code></pre> <p>比如下面这个Demo:</p> <div style="margin-bottom: 20px;"><iframe id="zjRwLM" src="//codepen.io/airen/embed/zjRwLM?height=400&amp;theme-id=0&amp;slug-hash=zjRwLM&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>你可以随意使用一个<code>PerformanceObserver</code> API。当一个用户点击一个按钮时,它将标志着开始和结束的瞬间,并测量在<code>3000ms</code>之后的持续时间。可以在控制台中查看输出结果。</p> <p><img src="/sites/default/files/blogs/2018/1805/observers-type-5.png" alt="" /></p> <p>@Irina Shestak在2018年Asia的JSConf上就分享了有关于<code>PerformanceObservers</code>相关的主题,比如<a href="http://www.xysjxj.com/quot;//www.w3.org/TR/performance-timeline-2/#dfn-performance-timeline"><code>PerformanceObserver</code>" API</a>、<a href="http://www.xysjxj.com/quot;//nodejs.org/api/perf_hooks.html#perf_hooks_performance_timing_api">NodeJs的<code>perf_hooks</code>" API</a>。</p> <div style="margin-bottom: 20px;"> <iframe style="width: 100%;" width="100%" height="315" src="//www.youtube.com/embed/dpxq4QWVhc0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe> </div> <p>如果你对这些观察者有任何想法、建议或相关改进,欢迎与我们一起分享。</p> <h2>扩展阅读</h2> <ul> <li><a href="http://www.xysjxj.com/quot;//developers.google.com/web/updates/2012/02/Detect-DOM-changes-with-Mutation-Observers">Detec" DOM changes with Mutation Observers</a></li> <li><a href="http://www.xysjxj.com/quot;//developers.google.com/web/updates/2016/04/intersectionobserver">IntersectionObserver’" Coming into View</a></li> <li><a href="http://www.xysjxj.com/quot;//developers.google.com/web/updates/2016/06/performance-observer">Performanc" Observer: Efficient Access to Performance Data</a></li> <li><a href="http://www.xysjxj.com/quot;//davidwalsh.name/intersection-observers">Observin" Intersection Observers</a></li> <li><a href="http://www.xysjxj.com/quot;//deanhume.com/Home/BlogPost/lazy-loading-images-using-intersection-observer/10163">Laz" loading images using Intersection Observer</a></li> </ul> <div class="blog-author media"><a class="media-object" href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank"><img src="/sites/default/files/blogs/author/airen.jpg"></a><div class="media-body"><h3 class="media-heading"><a href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank">大漠</a></h3><div class="media-des">常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等韦德1946手机版客户端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/book-comment.html"" target="_blank">图解CSS3:核心技术与案例实战</a>》。</div></div></div> <p>如需转载,烦请注明出处:<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/different-types-of-observers-supported-by-modern-browsers.html">https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/different-types-of-observers-supported-by-modern-browsers.html</a></p> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/translations"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">译文</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div></div></div> Wed, 09 May 2018 12:32:04 +0000 Airen 2404 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com w3cplus_引领web前沿,打造前端精品教程 - 韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权) https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/debouncing-throttling-explained-examples.html <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><blockquote> <p>特别声明,本文转载<a href="http://www.xysjxj.com/quot;//weibo.com/newwave">@涂鸦码龙</a>翻译的《<" href="http://www.xysjxj.com/quot;//jinlong.github.io/2016/04/24/Debouncing-and-Throttling-Explained-Through-Examples/">实例解析防抖动(Debouncing)和节流阀(Throttling)</a>》一文,原文《<" href="//css-tricks.com/debouncing-throttling-explained-examples/">Debouncing and Throttling Explained Through Examples</a>》。</p> </blockquote> <p><strong>防抖(Debounce)</strong>和节流<strong>(Throttle)</strong>都是用来控制某个函数在一定时间内执行多少次的技巧,两者相似而又不同。</p> <p>当我们给 DOM 绑定事件的时候,加了防抖和节流的函数变得特别有用。为什么呢?因为我们在事件和函数执行之间加了一个控制层。记住,我们是无法控制 DOM 事件触发频率的。</p> <p>看下滚动事件的例子:</p> <div style="margin-bottom: 20px;"><iframe id="XqVRNP" src="//codepen.io/airen/embed/XqVRNP?height=400&amp;theme-id=0&amp;slug-hash=XqVRNP&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>当使用触控板,滚动滚轮,或者拖拽滚动条的时候,一秒可以轻松触发<code>30</code>次事件。经我的测试,在智能手机上,慢慢滚动一下,一秒可以触发事件<code>100</code>次之多。这么高的执行频率,你的滚动回调函数压力大吗?</p> <p>早在2011年,Twitter 网站抛出了一个问题:向下滚动 Twitter 信息流的时候,变得很慢,很迟钝。@John Resig 发表了<a href="http://www.xysjxj.com/quot;//ejohn.org/blog/learning-from-twitter">一篇博客解释这个问题</a>,文中解释到直接" <code>scroll</code> 事件关联昂贵的函数,是多么糟糕的主意。</p> <p>@John(5年前)建议的解决方案是,在 <code>onScroll</code> 事件外部,每 <code>250ms</code> 循环执行一次。简单的技巧,避免了影响用户体验。</p> <p>现如今,有一些稍微高端的方式处理事件。我来结合用例介绍下 <code>Debounce</code>,<code>Throttle</code> 和 <code>requestAnimationFrame</code> 吧。</p> <h2>防抖动(Debounce)</h2> <p>防抖技术可以把多个顺序地调用合并成一次。</p> <p><img src="/sites/default/files/blogs/2018/1805/debounce.webp" alt="" /></p> <p>假想一下,你在电梯中,门快要关了,突然有人准备上来。电梯并没有改变楼层,而是再次打开梯门。电梯延迟了改变楼层的功能,但是优化了资源。</p> <p>在顶部按钮上点击或移动鼠标试一下:</p> <div style="margin-bottom: 20px;"><iframe id="OZzmgb" src="//codepen.io/airen/embed/OZzmgb?height=400&amp;theme-id=0&amp;slug-hash=OZzmgb&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>你可以看到连续快速的事件是如何被一个 <code>debounce</code> 事件替代的。但是如果事件触发的时间间隔过长,<code>debounce</code> 则不会生效。</p> <h3>前缘(或者“immediate”)</h3> <p>你会发现,直到事件停止快速执行以后,<code>debounce</code> 事件才会触发相应功能。为何不立即触发呢?那样的话就跟原本的非 <code>debounce</code> 处理无异了。</p> <p>直到两次快速调用之间的停顿结束,事件才会再次触发。</p> <p>这是带 <code>leading</code> 标记的例子:</p> <p><img src="/sites/default/files/blogs/2018/1805/debounce-leading.webp" alt="" /></p> <p>在 <code>underscore.js</code> 中,选项叫 <code>immediate</code> ,而不是 <code>leading</code>:</p> <div style="margin-bottom: 20px;"><iframe id="OZzmxW" src="//codepen.io/airen/embed/OZzmxW?height=400&amp;theme-id=0&amp;slug-hash=OZzmxW&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <h3>Debounce 实现</h3> <p>我首次看到 <code>debounce</code> 的 JavaScript 实现是在 2009 年的 <a href="//unscriptable.com/2009/03/20/debouncing-javascript-methods/">@John Hann 的博文</a>。</p> <p>不久后,@Ben Alman 做了个 <a href="http://www.xysjxj.com/quot;//benalman.com/projects/jquery-throttle-debounce-plugin/">jQuer" 插件</a>(不再维护),一年后 @Jeremy Ashkenas 把<a href="http://www.xysjxj.com/quot;//github.com/jashkenas/underscore/commit/9e3e067f5025dbe5e93ed784f93b233882ca0ffe">它加入" <code>underscore.js</code></a>。而后加入了 <code>Lodash</code> 。</p> <div style="margin-bottom: 20px;"><iframe id="rvpmYq" src="//codepen.io/airen/embed/rvpmYq?height=400&amp;theme-id=0&amp;slug-hash=rvpmYq&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p><code>Lodash</code> 给 <code>_.debounce</code> 和 <code>_.throttle</code> 添加了<a href="http://www.xysjxj.com/quot;//lodash.com/docs#debounce">不少特性</a>。之前" <code>immediate</code> 被 <code>leading</code>(最前面) 和 <code>trailing</code>(最后面) 选项取代。你可以选一种,或者都选,默认只有 <code>trailing</code> 启用。</p> <p>新的 <code>maxWait</code> 选项(仅 <code>Lodash</code> 有)本文未提及,但是也很有用。事实上,<code>throttle</code> 方法是用 <code>_.debounce</code> 加 <code>maxWait</code> 实现的,你可以看 <a href="http://www.xysjxj.com/quot;//github.com/lodash/lodash/blob/4.7.0/lodash.js#L9840"><code>lodash</code>" 源码</a>。</p> <h3>Debounce 实例</h3> <h4>调整大小的例子</h4> <p>调整桌面浏览器窗口大小的时候,会触发很多次 <code>resize</code> 事件。</p> <p>看下面 Demo:</p> <div style="margin-bottom: 20px;"><iframe id="MGrmVB" src="//codepen.io/airen/embed/MGrmVB?height=400&amp;theme-id=0&amp;slug-hash=MGrmVB&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>如你所见,我们为 <code>resize</code> 事件使用了默认的 <code>trailing</code> 选项,因为我们只关心用户停止调整大小后的最终值。</p> <h4>基于 AJAX 请求的自动完成功能,通过 <code>keypress</code> 触发</h4> <p>为什么用户还在输入的时候,每隔<code>50ms</code>就向服务器发送一次 AJAX 请求?<code>_.debounce</code> 可以帮忙,当用户停止输入的时候,再发送请求。</p> <p>此处也不需要 <code>leading</code> 标记,我们想等最后一个字符输完。</p> <div style="margin-bottom: 20px;"><iframe id="zjpwjb" src="//codepen.io/airen/embed/zjpwjb?height=400&amp;theme-id=0&amp;slug-hash=zjpwjb&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p>相似的使用场景还有,直到用户输完,才验证输入的正确性,显示错误信息。</p> <h2>如何使用 <code>debounce</code> 和 <code>throttle</code> 以及常见的坑</h2> <p>自己造一个 <code>debounce</code> / <code>throttle</code> 的轮子看起来多么诱人,或者随便找个博文复制过来。<strong>我是建议直接使用 <code>underscore</code> 或 <code>Lodash</code></strong> 。如果仅需要 <code>_.debounce</code> 和 <code>_.throttle</code> 方法,可以使用 <code>Lodash</code> 的自定义构建工具,生成一个 <code>2KB</code> 的压缩库。使用以下的简单命令即可:</p> <pre><code>npm i -g lodash-cli lodash-cli include=debounce,throttle </code></pre> <p>常见的坑是,不止一次地调用 <code>_.debounce</code> 方法:</p> <pre><code>// 错误 $(window).on('scroll', function() { _.debounce(doSomething, 300); }); // 正确 $(window).on('scroll', _.debounce(doSomething, 200)); </code></pre> <p><code>debounce</code> 方法保存到一个变量以后,就可以用它的私有方法 <code>debounced_version.cancel()</code>,<code>lodash</code> 和 <code>underscore.js</code> 都有效。</p> <pre><code>var debounced_version = _.debounce(doSomething, 200); $(window).on('scroll', debounced_version); // 如果需要的话 debounced_version.cancel(); </code></pre> <h2>Throttle(节流阀)</h2> <p>使用 <code>_.throttle</code> 的时候,只允许一个函数在 <code>X</code> 毫秒内执行一次。</p> <p>跟 <code>debounce</code> 主要的不同在于,<code>throttle</code> 保证 <code>X</code> 毫秒内至少执行一次。</p> <h3>节流阀实例</h3> <h4>无限滚动</h4> <p>用户向下滚动无限滚动页面,需要检查滚动位置距底部多远,如果邻近底部了,我们可以发 AJAX 请求获取更多的数据插入到页面中。</p> <p>我们心爱的 <code>_.debounce</code> 就不适用了,只有当用户停止滚动的时候它才会触发。只要用户滚动至邻近底部时,我们就想获取内容。</p> <p>使用 <code>_.throttle</code> 可以保证我们不断检查距离底部有多远。</p> <div style="margin-bottom: 20px;"><iframe id="LmeLPG" src="//codepen.io/airen/embed/LmeLPG?height=400&amp;theme-id=0&amp;slug-hash=LmeLPG&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <h2>requestAnimationFrame(rAF)</h2> <p><code>requestAnimationFrame</code> 是另一种限速执行的方式。</p> <p>跟 <code>_.throttle(dosomething, 16)</code> 等价。它是高保真的,如果追求更好的精确度的话,可以用浏览器原生的 API 。</p> <p>可以使用 <code>rAF</code> API 替换 <code>throttle</code> 方法,考虑一下优缺点:</p> <h3>优点</h3> <ul> <li>动画保持 <code>60fps</code>(每一帧 <code>16ms</code>),浏览器内部决定渲染的最佳时机</li> <li>简洁标准的 API,后期维护成本低</li> </ul> <h3>缺点</h3> <ul> <li>动画的开始/取消需要开发者自己控制,不像 <code>.debounce</code> 或 <code>.throttle</code>由函数内部处理。</li> <li>浏览器标签未激活时,一切都不会执行。</li> <li>尽管所有的<a href="http://www.xysjxj.com/quot;//caniuse.com/#feat=requestanimationframe">现代浏览器都支" <code>rAF</code></a> ,IE9,Opera Mini 和 老的 Android 还是<a href="http://www.xysjxj.com/quot;//www.paulirish.com/2011/requestanimationframe-for-smart-animating/">需要打补丁</a>。</li>" <li>Node.js 不支持,无法在服务器端用于文件系统事件。</li> </ul> <p>根据经验,如果 JavaScript 方法需要绘制或者直接改变属性,我会选择 <code>requestAnimationFrame</code>,只要涉及到重新计算元素位置,就可以使用它。</p> <p>涉及到 AJAX 请求,添加/移除 <code>class</code> (可以触发 CSS 动画),我会选择 <code>_.debounce</code> 或者 <code>_.throttle</code> ,可以设置更低的执行频率(例子中的<code>200ms</code> 换成<code>16ms</code>)。</p> <h3>rAF 实例</h3> <p>灵感来自于 <a href="http://www.xysjxj.com/quot;//www.html5rocks.com/en/tutorials/speed/animations/">@Pau" Lewis 的文章</a>,我将用 <code>requestAnimationFrame</code> 控制 <code>scroll</code> 。</p> <p><code>16ms</code> 的 <code>_.throttle</code> 拿来做对比,性能相仿,用于更复杂的场景时,<code>rAF</code> 可能效果更佳。</p> <div style="margin-bottom: 20px;"><iframe id="WJdOQK" src="//codepen.io/airen/embed/WJdOQK?height=400&amp;theme-id=0&amp;slug-hash=WJdOQK&amp;default-tab=result&amp;user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="width: 100%; overflow: hidden;"></iframe></div> <p><code>headroom.js</code> 是个<a href="http://www.xysjxj.com/quot;//github.com/WickyNilliams/headroom.js/blob/3282c23bc69b14f21bfbaf66704fa37b58e3241d/src/Debouncer.js">更高级的例子</a>。</p>" <h2>结论</h2> <p>使用 <code>debounce</code>,<code>throttle</code> 和 <code>requestAnimationFrame</code> 都可以优化事件处理,三者各不相同,又相辅相成。</p> <p>总之:</p> <ul> <li><strong><code>debounce</code></strong>:把触发非常频繁的事件(比如按键)合并成一次执行。</li> <li><strong><code>throttle</code></strong>:保证每 <code>X</code> 毫秒恒定的执行次数,比如每<code>200ms</code>检查下滚动位置,并触发 CSS 动画。</li> <li><strong><code>requestAnimationFrame</code></strong>:可替代 <code>throttle</code> ,函数需要重新计算和渲染屏幕上的元素时,想保证动画或变化的平滑性,可以用它。注意:IE9 不支持。</li> </ul> <div class="blog-author media"><a class="media-object" href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank"><img src="/sites/default/files/blogs/author/airen.jpg"></a><div class="media-body"><h3 class="media-heading"><a href="http://www.xysjxj.com/quot;//weibo.com/韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权)"" target="_blank">大漠</a></h3><div class="media-des">常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等韦德1946手机版客户端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《<a href="http://www.xysjxj.com/quot;//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/book-comment.html"" target="_blank">图解CSS3:核心技术与案例实战</a>》。</div></div></div> <p>如需转载,烦请注明出处:<a href="//www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/debouncing-throttling-explained-examples.html">https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com/javascript/debouncing-throttling-explained-examples.html</a></p> </div></div></div><div class="field field-name-field-taxonomy field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/blog/tags/624.html"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">转载</a></div></div></div><div class="field field-name-field-blog-tag field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="http://www.xysjxj.com/quot;/JavaScript"" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">JavaScript</a></div></div></div> Tue, 08 May 2018 12:31:28 +0000 Airen 2402 at https://www.韦德1946手机版客户端_韦德娱乐平台_伟德1946手机版(官方唯一授权).com