原文:blog.logrocket.com/8-dom-featu…
做者:Louis Lazaris
翻译:前端小白javascript
这篇文章将考虑八个不为人知的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
useCapture
参数同样true
, 表示事件只在目标元素上运行一次,而后被删除true
, 表示函数不会调用 preventDefault()
, 即便它包含在函数体中这三个选项中最有趣的是 once
。这在不少状况下都很是有用,能够避免使用 removeEventListener()
或其余复杂的技术来强制触发单个事件。若是您曾经使用过jQuery,那么您可能熟悉该库中的相似特性.one()方法。web
您能够在下面的codepen中尝试使用options对象:npm
codepen: codepen.io/impressivew…api
注意演示页面上的按钮只会追加文本一次。若是将once值更改成false,而后屡次单击该按钮,则每次单击按钮时都会附加文本。
浏览器对options对象的支持很是友好:除了IE11和更早版本以外,全部浏览器都支持它,因此若是你不在意微软Edge以前的浏览器,那么使用它是很是安全的。
平滑滚动一直是须要的。当本地页面连接当即跳转到指定位置时(若是你眨眼,你甚至可能会错过跳转),这会让人很不舒服。 平滑滚动改进了用户页面体验。
虽然过去使用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
对象window
options
对象一样适用于 scroll()
和 scrollBy()
在许多状况下,使用 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。
您可能知道,对于单选按钮和复选框,若是但愿获取或设置 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
。
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]
)许多人可能熟悉 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()
的第一个参数相同,可取值为:
当使用 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…
演示中的每一个按钮都将以按钮文本描述的方式响应,并显示一条显示当前单击次数的消息。须要注意的一些事情:
blur
和 focus
,来证实这些不符合条件,并将始终返回0(即没有点击)注意,演示中包含了一个很实用的技巧,能够模拟三次单击事件:
btnT.addEventListener('click', function (e) {
if (e.detail === 3) {
trpl.value = 'Triple Click Successful!';
}
}, false);
复制代码
若是全部浏览器都计算过三次点击次数,那么您还能够检测到更高的点击次数,但我认为在大多数状况下,三次点击事件就足够了。
scrollHeight
和 scrollWidth
属性可能听起来很熟悉,由于您可能会将它们与其余与宽度和高度相关的DOM功能混淆。 例如,offsetWidth
和 offsetHeight
属性将返回元素的高度或宽度,而不会考虑溢出。
参考下面demo:
codepen: codepen.io/impressivew…
演示中的列具备相同的内容。左边列的 overflow
设置为 auto
,右边列的 overflow
设置为 hidden
。offsetHeight
属性为每一个属性返回相同的值,由于它不考虑可滚动区域或隐藏区域;它只测量元素的实际高度,包括任何垂直填充和边框。
另外一方面,名称恰当的 scrollHeight
属性将计算元素的所有高度,包括可滚动(或隐藏)区域:
codepen: codepen.io/impressivew…
上面的演示与前面的演示相同,只是它使用了 scrollHeightto
获取每一个列的高度。再次注意,这两列的值是相同的。但这一次,它是一个更高的数字,由于溢出面积也被算做高度的一部分。
上面的示例主要关注元素高度,这是最多见的用例,但您也可使用 offsetWidth
和 scrollWidth
,它们将以相同的方式应用于水平滚动。
以上就是我列出的DOM特性,这些多是我在过去几年遇到的最有趣的特性,因此我但愿在不久的未来,您至少可以在项目中使用其中的一个特性。