【译】8个你不知道的DOM的特性

原文:blog.logrocket.com/8-dom-featu…
做者:Louis Lazaris
翻译:前端小白javascript


随着最近对工具的关注愈来愈多,让咱们从React和npm-install-everything发布的全部文章中抽点时间来了解一下在现代浏览器中不须要依赖的纯DOM和Web API的特性。

这篇文章将考虑八个不为人知的DOM特性,这些特性具备强大的浏览器支持。 为了帮助解释每一个工做原理,我会提供大量的交互式演示,同时也便于你本身尝试html

这些方法和属性没有一个陡峭的学习曲线,能够很好地配合你的项目所使用的任何工具集。前端

你确定已经使用过 addEventListener() 将事件附加到web文档中的元素上。一般,addEventListener() 调用看起来是这样的:java

element.addEventListener('click', doSomething, false);
复制代码

第一个参数是我正在监听的事件。第二个参数是一个回调函数,它将在事件发生时执行。第三个参数是一个名为 useCapture 的布尔值,用于指示是否要使用事件冒泡或捕获jquery

这些都是众所周知的(尤为是前两个)。可是,您可能不知道 addEventListener() 也接受将第三个布尔值类型的参数替换为另外一个新参数。这个新参数是一个看起来像这样的 options 对象:git

element.addEventListener('click', doSomething, {
  capture: false,
  once: true,
  passive: false
});
复制代码

注意,语法容许定义三个不一样的属性。下面是每一个词的意思:github

  • capture: 一个布尔值和 useCapture 参数同样
  • once: 一个布尔值,若是设为 true, 表示事件只在目标元素上运行一次,而后被删除
  • passive: 一个布尔值,若是设为 true, 表示函数不会调用 preventDefault(), 即便它包含在函数体中

这三个选项中最有趣的是 once。这在不少状况下都很是有用,能够避免使用 removeEventListener() 或其余复杂的技术来强制触发单个事件。若是您曾经使用过jQuery,那么您可能熟悉该库中的相似特性.one()方法。web

您能够在下面的codepen中尝试使用options对象:npm

codepen: codepen.io/impressivew…api

注意演示页面上的按钮只会追加文本一次。若是将once值更改成false,而后屡次单击该按钮,则每次单击按钮时都会附加文本。

浏览器对options对象的支持很是友好:除了IE11和更早版本以外,全部浏览器都支持它,因此若是你不在意微软Edge以前的浏览器,那么使用它是很是安全的。

scrollTo() 在窗口或元素中平滑滚动

平滑滚动一直是须要的。当本地页面连接当即跳转到指定位置时(若是你眨眼,你甚至可能会错过跳转),这会让人很不舒服。 平滑滚动改进了用户页面体验。

虽然过去使用jQuery插件能够实现,但如今可使用 window.scrollTo() 方法,只须要使用一行JavaScript代码就能够实现。

scrollTo() 方法应用于 window 对象,以告诉浏览器滚动到页面上指定的位置。下面是一个基本语法的示例:

window.scrollTo(0, 1000);
复制代码

这将使窗口向右滚动0px(表示x坐标,或水平滚动),向下滚动1000px(垂直方向,这一般是您想要的)。但在这种状况下,滚动将不会是一个平滑的动画效果;页面将忽然滚动,就像使用本地连接到指定哈希URL同样。

虽然有时候这就是你想要的。可是为了得到平滑的滚动,必须使用不多有人知道的 ScrollToOptions 对象,就像这样:

window.scrollTo({
  top: 0,
  left: 1000,
  behavior: 'smooth'
});
复制代码

这段代码与前面的示例相同,可是在 options 对象中添加了 behavior 属性并将其值设为 smooth

codepen: codepen.io/impressivew…

关于这个特性的一些注意事项:

  • 虽然几乎全部的浏览器都支持 scrollTo(),但不是全部浏览器都支持 options 对象
  • 该方法一样做用于DOM元素上,而不只仅 window
  • options 对象一样适用于 scroll()scrollBy()

可选参数的setTimeout()和setInterval()

在许多状况下,使用 window.setTimeout()window.setInterval() 执行基于时间的动画如今已经被性能更友好的 window.requestAnimationFrame() 所取代。可是在某些状况下,setTimeout()setInterval() 是更好的的选择,所以有必要了解其中不多有人知道的一些特性。

一般,你会看到使用他们的语法是这样的:

let timer = window.setInterval(doSomething, 3000);
function doSomething () {
  // Something happens here...
}
复制代码

这里 setInterval() 调用传入两个参数:回调函数和时间间隔。对于 setTimeout(),它将运行一次,而在本例中,它将无限期地运行,直到我传入计时器ID,调用 window.clearTimeout() 清除定时器。

使用起来很简单。 可是,若是我但愿个人回调函数接受参数呢? 最近这些计时器方法容许添加如下内容:

let timer = window.setInterval(doSomething, 3000, 10, 20);
function doSomething (a, b) {
  // Something happens here…
}
复制代码

注意,我在 setInterval() 调用中又添加了两个参数。而后,个人 doSomething() 函数接受这些参数并根据须要操做它们。

如下的codepen演示了如何使用 setTimeout() 实现此功能:

codepen: codepen.io/impressivew…

当单击该按钮时,将执行两个传入值的计算。能够经过页面上的数字输入更改值。

至于浏览器支持,还不是很肯定,但彷佛全部正在使用的浏览器都支持可选参数功能,包括IE10。

单选按钮和复选框的defaultChecked属性

您可能知道,对于单选按钮和复选框,若是但愿获取或设置 checked 属性,可使用 checked 属性,以下所示(假设 radioButton 是对某个表单输入的引用)

console.log(radioButton.checked); // true
radioButton.checked = false;
console.log(radioButton.checked); // false
复制代码

但还有一个名为 defaultChecked 的属性,它能够应用于单选按钮或复选框组,来得到组中最初设置为checked的元素是哪一个

<form id="form">
  <input type="radio" value="one" name="setOne"> One
  <input type="radio" value="two" name="setOne" checked> Two<br />
  <input type="radio" value="three" name="setOne"> Three
</form>
复制代码

有了这个,即便在更改了选中的单选按钮以后,我也能够遍历单选框并找出初始为checked是哪个,以下所示:

for (i of myForm.setOne) {
  if (i.defaultChecked === true) {
    console.log(‘i.value’);
  }
}
复制代码

下面是CodePen演示,它将显示当前选中的单选框或默认选中的单选框,具体取决于你点击哪一个按钮:

codepen: codepen.io/impressivew…

该示例中的 defaultChecked 始终是单选框Two。如前所述,这也能够经过复选框组来完成。尝试更改HTML中的默认选中选项,而后再次尝试点击按钮。

这是复选框组的演示: codepen: codepen.io/impressivew…

在本例中,你将注意到默认状况下选中了两个复选框,所以当使用 defaultChecked 查询时,这两个复选框都将返回 true

使用 normalize() and wholeText() 操做文本节点

HTML文档中的文本节点可能比较复杂,特别是在动态插入或建立节点时。例如,若是我有如下HTML

<p id="el">This is the initial text.</p>
复制代码

我能够给p元素增长一个文本节点:

let el = document.getElementById('el');
el.appendChild(document.createTextNode(' Some more text.'));
console.log(el.childNodes.length); // 2
复制代码

注意,在添加文本节点以后,我将记录该段中子节点的长度,它表示有两个节点。这些节点是一个文本字符串,可是由于文本是动态附加的,因此它们被视为单独的节点。

在某些状况下,若是将文本做为单个文本节点处理将会更有帮助,这使得文本更容易操做。这就是 normalize()wholeText() 发挥做用的地方。

normalize() 方法可用于合并单独的文本节点

el.normalize();
console.log(el.childNodes.length); // 1
复制代码

对元素调用 normalize() 将合并该元素内的任何相邻文本节点。若是刚好有一些HTML穿插在相邻的文本节点中,HTML将保持原样,而全部相邻的文本节点将被合并。

可是,若是因为某种缘由我想保持文本节点分开,但我仍然但愿可以将文本做为单个单元抓取,那么这就是 wholeText() 有用的地方。 所以,我能够在相邻的文本节点上执行此操做,而不是调用 normalize()

console.log(el.childNodes[0].wholeText);
// This is the initial text. Some more text.
console.log(el.childNodes.length); // 2
复制代码

只要我没有调用 normalize(),文本节点的长度将保持为2,而且我可使用 wholeText() 记录整个文本。可是请注意几点:

  • 我必须在一个文本节点上调用 wholeText,而不是元素( el.childNodes[0] 或者 el.childNodes[1])
  • 文本节点必须相邻,中间没有HTML分隔它们

insertAdjacentElement() 和 insertAdjacentText()

许多人可能熟悉 insertAdjacentHTML() 方法,它容许您轻松地将一串文本或HTML添加到页面中与其余元素相关的特定位置。

可是,可能您没有注意到该规范还包含两个相关的方法,它们以相似的方式工做: insertAdjacentElement()insertAdjacentText()

insertAdjacentHTML() 的一个缺点是插入的内容必须是字符串的形式。 所以,若是您包含HTML,则必须将其声明为:

el.insertAdjacentHTML('beforebegin', '<p><b>Some example</b> text goes here.</p>');
复制代码

可是,使用 insertAdjacentElement(),第二个参数能够是一个元素的引用,以下:

let el = document.getElementById('example'),
addEl = document.getElementById('other');
el.insertAdjacentElement('beforebegin', addEl);
复制代码

这个方法的有趣之处在于,它不只将引用的元素添加到指定的位置,并且还将从文档中的原始位置删除元素。所以,这是将一个元素从DOM中的一个位置转移到另外一个位置的简单方法。

下面是一个使用 insertAdjacentElement() 的CodePen演示:

codepen: codepen.io/impressivew…

insertAdjacentText() 方法的工做原理相似,可是所提供的文本字符串将仅做为文本插入,即便它包含HTML。demo以下:

codepen: codepen.io/impressivew…

您能够将本身的文本添加到输入字段,而后使用按钮将其添加到文档中。注意,任何特殊字符(如HTML标记)都将做为HTML实体插入,这与 insertAdjacentHTML() 方法的行为不一样。

insertAdjacentHTML() , insertAdjacentElement(), 和 insertAdjacentText() 的第一个参数相同,可取值为:

  • beforebegin
  • afterbegin
  • beforeend
  • afterend

event.detail 属性

当使用 addEventListener() 时,您可能须要防止函数调用中的默认浏览器行为。例如,您可能想拦截 <a> 元素上的单击,并使用JavaScript处理这些单击。你会这样作:

btn.addEventListener('click', function (e) {
  // do something here...
  e.preventDefault();
}, false);
复制代码

preventDefault(),和之前的 return false 效果是同样的。这须要将事件对象传递到函数中,由于该对象会调用 preventDefault() 方法。

你还能够利用那个事件对象作更多。实际上,当触发某些事件(例如click、dbclick、mouseup、mousedown)时,他们会暴露出一个称为UIEvent接口。正如MDN所指出的,这个接口上的许多特性都被废除了。但有用的是detail属性,它是官方规范的一部分

他看起来像这样:

btn.addEventListener('click', function (e) {
  // do something here...
  console.log(e.detail);
}, false);
复制代码

cedepen: codepen.io/impressivew…

演示中的每一个按钮都将以按钮文本描述的方式响应,并显示一条显示当前单击次数的消息。须要注意的一些事情:

  • WebKit内核浏览器容许无限制的点击次数,dbclick除外,它老是两次。 Firefox只容许最多三次点击,而后计数再次开始
  • 我已经包括了 blurfocus ,来证实这些不符合条件,并将始终返回0(即没有点击)
  • IE11这类的旧浏览器可能会有问题

注意,演示中包含了一个很实用的技巧,能够模拟三次单击事件:

btnT.addEventListener('click', function (e) {
  if (e.detail === 3) {
    trpl.value = 'Triple Click Successful!';
  }
}, false);
复制代码

若是全部浏览器都计算过三次点击次数,那么您还能够检测到更高的点击次数,但我认为在大多数状况下,三次点击事件就足够了。

scrollHeight 和 scrollWidth 属性

scrollHeightscrollWidth 属性可能听起来很熟悉,由于您可能会将它们与其余与宽度和高度相关的DOM功能混淆。 例如,offsetWidthoffsetHeight 属性将返回元素的高度或宽度,而不会考虑溢出。

参考下面demo:

codepen: codepen.io/impressivew…

演示中的列具备相同的内容。左边列的 overflow 设置为 auto,右边列的 overflow 设置为 hiddenoffsetHeight 属性为每一个属性返回相同的值,由于它不考虑可滚动区域或隐藏区域;它只测量元素的实际高度,包括任何垂直填充和边框。

另外一方面,名称恰当的 scrollHeight 属性将计算元素的所有高度,包括可滚动(或隐藏)区域:

codepen: codepen.io/impressivew…

上面的演示与前面的演示相同,只是它使用了 scrollHeightto 获取每一个列的高度。再次注意,这两列的值是相同的。但这一次,它是一个更高的数字,由于溢出面积也被算做高度的一部分。

上面的示例主要关注元素高度,这是最多见的用例,但您也可使用 offsetWidthscrollWidth,它们将以相同的方式应用于水平滚动。

最后

以上就是我列出的DOM特性,这些多是我在过去几年遇到的最有趣的特性,因此我但愿在不久的未来,您至少可以在项目中使用其中的一个特性。

相关文章
相关标签/搜索