前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【JS】328- 8个你不知道的DOM功能

【JS】328- 8个你不知道的DOM功能

作者头像
pingan8787
发布2019-08-26 17:36:21
1.4K0
发布2019-08-26 17:36:21
举报
文章被收录于专栏:前端自习课前端自习课

最近一直在关注工具,从 React 和 npm-install-everything 中休息一下,看看一些原生的 DOM 和 Web API 的功能,他们可以在没有任何依赖库的浏览器中直接运行。

这篇文章将介绍八个鲜为人知的 DOM 功能,浏览器已经支持了这些功能。为了帮助解释每个功能的工作原理,我将为您用代码来演示这些功能。

这些方法没有陡峭的学习曲线,并且可以为你的项目所用。

你肯定习惯于使用 addEventListener() 将事件添加到Web中的元素,一般情况下, addEventListener() 调用起来是这样的:

代码语言:javascript
复制
element.addEventListener('click',doSomething,false);

第一个元素是我们要监听的事件,第二个元素是事件触发时的回调函数,第三个参数是一个布尔值用来标识事件在捕获还是冒泡阶段触发。

很多时候我们都知道前两个参数,但是也许你不知道 addEventListener() 也接受传其他参数来代替布尔值。这个新的参数是一个可选的对象,就像这样:

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

语法允许定义三个不同的属性。以下是每个属性的简介:

  • capture -- 布尔值,和上文提到的作用一样
  • once -- 布尔值,如果设置为 true 事件只会执行一次,然后就会被移除掉
  • passive -- 最后一个布尔值,如果设置为 true, 将永远不会调用 preventDefault() ,即使在函数体中。

这里面最有意思的是 once 选项。在很多情况下我们都需要这个功能,并且不会使用 removeEventListener() 或使用其他的复杂技术来强制只能点击一次。如果你使用过 jQuery,那你就知道 .one() 的功能。

你能在 CodePen 里来验证它:

代码语言:javascript
复制
let btn = document.querySelector('button'),
    op = document.querySelector('.output');

btn.addEventListener('click', function () {
  op.innerHTML += 'Button was clicked';
}, {
  capture: false,
  once: true,
  passive: false
});

请注意,上面页面上的按钮只会添加一次文本。如果将 once 值改为 false ,则可以点击多次,每次点击都可以添加一行文本。

浏览器中对 options 对象支持的非常好:除了IE11及更早的版本外,所有浏览器都支持它,因此如果你不担心微软浏览器,那就可以使用它。

scrollTo() 用于窗口或元素是否平滑滚动

平滑滚动是必要的。当前页面链接跳转到制定位置时(如果你不注意,就一闪而过),看起来就很卡。平滑滚动是不仅看起来不错,而且还能改进页面用户体验。

虽然过去使用 jQuery 插件已经做到了,但现在只需要使用一行 JavaScript 也能达到类似的效果, 那就是 window.scrollTo()。

该方法应用于 Window 对象,告知浏览器滚动到页面上的制定位置。例如,这里有个简单示例: scrollTo()。

代码语言:javascript
复制
window.scrollTo(0, 1000);

这将滚动到横坐标 0px 和纵坐标 1000px 的页面位置。但这种情况下,滚动并不是平滑的,页面会突然滚动,就是用哈希到本地链接一样。

也许这就是你想要的,为了获得平滑滚动,你必须加入 ScrollToOptions 对象,就像这样:

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

这个代码和前面的示例等效,但是是在对象内添加了 behavior值为 smooth 的 options。

代码语言:javascript
复制
let btn = document.querySelector('button'),
    btn2 = document.querySelectorAll('button')[1],
    input = document.querySelector('input'),
    select = document.querySelector('select');

btn.addEventListener('click', function () {
  window.scrollTo({
    left: 0,
    top: input.value,
    behavior: select.value
  });
}, false);

btn2.addEventListener('click', function () {
  window.scrollTo({
    left: 0,
    top: 0,
    behavior: select.value
  });
}, false);

试试在输入框输入一个数字(最好是一个大数),并且更改 options 选项框里的值 smooth 或 auto (这也是 behaviro 属性的唯一两个选项)。

关于这个功能的一些说明:

  • 浏览器基本上都支持 scrollTo() ,但部分浏览器依然不支持 options 对象
  • 即使不作用于 window ,也可以使用该方法
  • scroll() 和 scrollBy() 也可以使用

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

很多数情况下,使用 window.setTimeout() 和 window.setInterval() 来开发基于时间的动画已经被性能更加友好的 window.requestAnimationFrame() 所取代。但是在某些情况下,setTimeout()或 setInterval() 才是正确的选择,因此,了解一些函数的特性还是有好处的。

通常,你会看到这里面的其中一种方法,语法如下:

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

这里 setInterval() 传递两个参数:回调函数和时间间隔。对于 setTimeout() 来说,这个只会运行一次,而在这种情况下,他将无限期运行,直到我在传入计时器的时候调用 window.clearTimeout()。

很容易吧,但是如果我希望回调函数接受参数呢?好吧,对计时器方法添加新的内容试一下:

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

注意,我在 setInterval() 中添加了两个参数。然后我在 doSomething() 中接受这些参数,并根据需要对其操作。

下面演示如何使用 setTimeout() :

代码语言:javascript
复制
let btn = document.querySelector('button'),
    op = document.querySelector('output'),
    a = 5,
    b = 7;

btn.addEventListener('click', () => {
  op.innerHTML = 'Calculating...';
  setTimeout(doSomething, 2000, a, b);
});

function doSomething (a, b) {
  op.innerHTML = a + b;
}

单击按钮时,将使用两个传入值进行运算。然后数字展示在页面上。

至于浏览器支持情况似乎不太一样,但几乎所有正在使用的浏览器都支持可选参数功能,包括 IE10。

单选按钮和复选框的默认选中属性

就像你所直到的,对于单选框和复选框,如果你想获取或者设置 checked 属性,你可以使用 checked 属性,就像这样(假设 radioButton 是输入框的引用):

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

这个也叫 defaultChecked, 用来设置单选框或者复选框的 checked。

这里有一些 HTML 示例:

代码语言:javascript
复制
<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>

这样的话,即使选中的单选框已经更改,我也可以通过循环找到最初的那个,如下所示:

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

下面是代码演示:

代码语言:javascript
复制
let myForm = document.getElementById('form'),
    btn = document.querySelector('button'),
    btn2 = document.querySelectorAll('button')[1],
    op = document.querySelector('output'),
    i;

btn.addEventListener('click', function () {
  for (i of myForm.setOne) {
    if (i.checked === true) {
      op.innerHTML = `Current selected option is: ${i.value}`;
    }
  }
}, false);

btn2.addEventListener('click', function () {
  for (i of myForm.setOne) {
    if (i.defaultChecked === true) {
      op.innerHTML = `The default selected option is: ${i.value}`;
    }
  }
}, false);

defaultChecked 总是返回 "Two" 的选项。如前所述,也可以通过复选框组来完成,尝试更改HTML中的默认选中选项,然后重试按钮。

这里是另外一个使用复选框按钮的例子:

代码语言:javascript
复制
let myForm = document.getElementById('form'),
    btn = document.querySelector('button'),
    btn2 = document.querySelectorAll('button')[1],
    op = document.querySelector('output'),
    op2 = document.querySelectorAll('output')[1],
    i;

btn.addEventListener('click', function () {
  op.innerHTML = 'Current selected option(s): ';
  for (i of myForm.setOne) {
    if (i.checked === true) {
      op.innerHTML += `${i.value} `;
    }
  }
}, false);

btn2.addEventListener('click', function () {
  op2.innerHTML = 'Default selected option(s): ';
  for (i of myForm.setOne) {
    if (i.defaultChecked === true) {
       op2.innerHTML += `${i.value} `;
    }
  }
}, false);

在这个例子中,你看到了两个默认的选中复选框的按钮,当使用 defaultChecked 时候选中的返回了 true。

使用 normalize() 和 wholeText 来操作文本节点

HTML 文档中的文本节点可能很不容易操作,特别是动态插入或者创造节点时。例如一下HTML:

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

然后我向该段落添加文本节点:

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

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

在某些情况下,将文本视为单个文本节点会更有用,这可以使得文本更容易操作。Normalize() 和 WholeText() 就是做这个的。

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

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

对元素调用 normalize() 将合并该元素内的任何相邻文本节点。如果在相邻的文本节点之间碰巧有一些 HTML 散布,则 HTML 将保持原样,而所有相邻的文本节点将被合并。

但是,如果出于某种原因,我希望将文本节点分开,但我仍然希望能够将文本作为一个单独的单元来获取,那么 wholeText 就是有用的。因此,我可以在相邻的文本节点上执行此操作,而不是调用 normalize() :

代码语言:javascript
复制
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分隔它们。

你可以在下面的演示代码中看到这两个特性以及 splitText() 方法,打开 codepen 控制台或者浏览器的控制台查看生成的日志。

代码语言:javascript
复制
let b = document.body;

// length of child nodes is "1"
console.log(b.firstElementChild.childNodes.length);

// I split the child text nodes at character 15
b.firstElementChild.firstChild.splitText(15);

// Now the length is "2"
console.log(b.firstElementChild.childNodes.length);

// Read the text as a single text node using "wholeText"
console.log(b.firstElementChild.childNodes[0].wholeText);

// It's still 2 nodes
console.log(b.firstElementChild.childNodes.length);

// Then I can normalize as needed
b.firstElementChild.normalize();

// Back to "1" text node
console.log(b.firstElementChild.childNodes.length);

insertAdjacentElement() and insertAdjacentText()

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

但您可能不知道规范还包含两个在类似的环境中工作的相关方法

那就是: insertAdjacentElement() and insertAdjacentText()。

insertAdjacentHTML() 的缺陷之一是插入的内容必须是字符串形式。因此,如果包含HTML,则必须这样声明:

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

但是,使用 insertAdjacentElement() ,第二个参数可以是元素引用:

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

这个方法的有趣之处在于,它不仅将引用的元素添加到指定的位置,而且还将从文档中的原始位置删除元素。因此,这是一种将元素从DOM中的一个位置传输到另一个位置的简单方法。

这是一个使用 insertAdjacentElement() 的代码演示。点击按钮可有效“移动”目标元素:

代码语言:javascript
复制
let p1 = document.getElementById('p1'),
    btn = document.querySelector('button');

btn.addEventListener('click', function () {
  p1.insertAdjacentElement('beforebegin', p2);
  this.disabled = true;
}, false);

insertAdjacentText() 方法的工作原理类似,但所提供的文本字符串将以文本的形式插入,即使它包含HTML。请注意以下演示:

代码语言:javascript
复制
let p1 = document.getElementById('p1'),
    btn = document.querySelector('button');


btn.addEventListener('click', function () {
  p1.insertAdjacentText('beforeend', document.getElementById('textIns').value);
}, false);

您可以将自己的文本添加到输入框中,然后使用按钮将其添加到文档中。请注意,任何特殊字符(如HTML标记)都将作为HTML实体插入,与 insertadjacenthl() 相比,该方法的行为有所不同。

这三个方法第一个参数都是一样的,取值为:

  • beforebegin: 插入到调用方法的元素之前
  • afterbegin: 插入元素中,在其第一个子元素之前
  • beforeend: 插入元素内部,在元素的最后一个子元素之后
  • afterend: 插入元素之后

事件详细信息

如前所述,事件使用熟悉的 addEventListener() 方法添加到网页上的元素。例如:

代码语言:javascript
复制
btn.addEventListener('click', function () {
  // do something here...
}, false);

使用 addEventListener() 时,可能需要防止函数调用中出现默认浏览器行为。例如,您可能希望截获对 <a> 元素的单击,并使用 javascript 处理这些单击。你可以这样做:

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

这里使用了 preventDefault() 这是和老方法 return false 等价的。要求将事件传递到函数中,因为调用了 preventDefault() 方法。

但是你可以用那个 event 对象做更多的事情。事实上,当使用某些事件(例如 click、 dbclick 、mouseup、mousedown )时,这些事件称为 uievent 接口的内容。正如MDN指出的那样,这个接口上的许多特性被弃用或不标准化。但最有趣和最有用的是 detail 属性,它是官方规范的一部分。

以下是同一个事件监听的示例:

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

我设置了一些代码演示,它返回不同事件的结果:

代码语言:javascript
复制
let btns = document.querySelector('.btns').querySelectorAll('button'),
    btnT = document.querySelector('.trpl');

for(i of btns) {
  (function(index) {
    index.addEventListener(index.innerHTML, function(e) {
      document.getElementById(index.innerHTML).value = e.detail + ' event(s) occured.';
    });
  })(i);
}

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

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

  • WebKit 浏览器允许无限制的点击次数,dbclick 除外,dbclick 总是两个。火狐只允许点击三次,然后计数再次开始。
  • 我将 blur 和 focus 包括在内,以证明这些不符合条件,并且始终返回0(即不单击)
  • 像IE11这样的老浏览器有非常不一致的行为

请注意,该演示包含了一个很好的用例,用于演示-模拟三次单击事件的能力:

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

如果所有浏览器的点击次数都超过三次,那么您也可以检测到更高的点击次数,但我认为在大多数情况下,三次点击事件就足够了。

ScrollHeight 和 ScrollWidth 属性

ScrollHeight 和 ScrollWidth 属性听起来可能很熟悉,因为您可能会将它们与宽度和高度相关的 DOM 特性混淆。例如,offsetwidth 和 offsetheight 属性将返回元素的高度或宽度,而不考虑溢出。

这里有个例子:

代码语言:javascript
复制
let op = document.querySelector('output');

op.innerHTML = `Left column offsetHeight value:
  ${document.querySelector('.col1').offsetHeight}px<br>
  Right column offsetHeight value:
  ${document.querySelector('.col1').offsetHeight}px
 `;

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

另一方面,适当命名的 ScrollHeight 属性将计算元素的完整高度,包括可滚动(或隐藏)区域:

代码语言:javascript
复制
let op = document.querySelector('output');

op.innerHTML = `Left column scrollHeight value: 
  ${document.querySelector('.col1').scrollHeight}px<br>
  Right column scrollHeight value: 
  ${document.querySelector('.col1').scrollHeight}px
`;

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

上面的例子集中在元素高度上,这是最常见的用例,但是您也可以使用 offsetwidth 和 scrollwidth,这将以与水平滚动相同的方式应用。

结论

这就是 DOM 特性列表,这些可能是我在过去几年中遇到的最有趣的特性之一,所以我希望其中至少有一个特性能在不久的将来在项目中使用。

如果您以前使用过其中一个,或者您能想到其中任何一个有趣的用例,请在评论中告诉我。

原文地址:

https://blog.logrocket.com/8-dom-features-you-didnt-know-existed-ec2a0a28fd89/

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端自习课 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档