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

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

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

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

element.addEventListener('click',doSomething,false);

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

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

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

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

  • capture -- 布尔值,和上文提到的作用一样

  • once -- 布尔值,如果设置为 true 事件只会执行一次,然后就会被移除掉

  • passive -- 最后一个布尔值,如果设置为 true, 将永远不会调用 preventDefault() ,即使在函数体中。

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

你能在 CodePen 里来验证它:

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()。

window.scrollTo(0, 1000);

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

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

window.scrollTo({
top: 0,
left: 1000,
behavior: 'smooth'
})

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

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() 才是正确的选择,因此,了解一些函数的特性还是有好处的。

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

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

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

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

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

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

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

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 是输入框的引用):

console.log(radioButton.checked); // true
radioButton.checked = false;
console.log(radioButton.checked); // false

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

这里有一些 HTML 示例:

<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>

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

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

下面是代码演示:

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中的默认选中选项,然后重试按钮。

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

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:

<p id="el">This is the initial text.</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分隔它们。

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

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,则必须这样声明:

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() 的代码演示。点击按钮可有效“移动”目标元素:

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

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

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() 方法添加到网页上的元素。例如:

btn.addEventListener('click', function () {
// do something here...
}, false);

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

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

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

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

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

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

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

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这样的老浏览器有非常不一致的行为

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

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

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

ScrollHeight 和 ScrollWidth 属性

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

这里有个例子:

let op = document.querySelector('output');	

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

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

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

let op = document.querySelector('output');	

op.innerHTML = `Left column scrollHeight value: 
${document.querySelector('.col1').scrollHeight}px&lt;br&gt;
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/

如果您觉得本文不错,

请点击文章底部广告,支持一下我啦!

原创系列推荐

1. JavaScript 重温系列(22篇全)

2. ECMAScript 重温系列(10篇全)

3. JavaScript设计模式 重温系列(9篇全)

4. 正则 / 框架 / 算法等 重温系列(16篇全)

5. Webpack4 入门手册(共 18 章)(上)

6. Webpack4 入门手册(共 18 章)(下)

7. 59篇原创系列汇总

点这,与大家一起分享本文吧~

【JS】328- 8个你不知道的DOM功能的更多相关文章

  1. 利用原生JS实现类似浏览器查找高亮功能(转载)

    利用原生JS实现类似浏览器查找高亮功能 在完成 Navify 时,增加一个类似浏览器ctrl+f查找并该高亮的功能,在此进行一点总结: 需求 在.content中有许多.box,需要在.box中找出搜 ...

  2. jquery.cookie.js 操作cookie实现记住密码功能的实现代码

    jquery.cookie.js操作cookie实现记住密码功能,很简单很强大,喜欢的朋友可以参考下.   复制代码代码如下: //初始化页面时验证是否记住了密码 $(document).ready( ...

  3. Lining.js - 为CSS提供 ::nth-Line 选择器功能

    在CSS中,我们使用 ::first-line 选择器来给元素第一行内容应用样式.但目前还没有像 ::nth-line.::nth-last-line 甚至 ::last-line 这样的选择器.实际 ...

  4. js仿手机端九宫格登录功能

    js仿手机端九宫格登录功能 最近闲来无事把以前无聊时开发的小东西拿出来和大家分享下,写的不好的请指出,我会及时修改.谢谢. 功能及方法逻辑都注释在代码中.所以麻烦大家直接看代码. 效果如下: 话不多说 ...

  5. js/jQuery实现类似百度搜索功能

    一.页面代码:<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www. ...

  6. vue+node.js+webpack开发微信公众号功能填坑——v -for循环

    页面整体框架实现,实现小功能,循环出数据,整体代码是上一篇 vue+node.js+webpack开发微信公众号功能填坑--组件按需引入 修改部门代码 app.vue <yd-flexbox&g ...

  7. js实现类型jq的dom加载完成

    有时候我们只想在 dom 加载完成后运行 js ,而不是等所有图片加载完成.所以不需要 onload , onload 会加载图片等其他媒体.很消耗时间. 原:http://blog.csdn.net ...

  8. 原生JS操作iframe里的dom

    转:http://www.css88.com/archives/2343 一.父级窗口操作iframe里的dom JS操作iframe里的dom可是使用contentWindow属性,contentW ...

  9. JS实现下拉菜单的功能

    <!DOCTYPE html> <html> <head> <meta charset = "utf8"> <title> ...

随机推荐

  1. hdu 1263 水果 (嵌套 map)

    水果Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submissio ...

  2. nyoj 67-三角形面积 (海伦公式, 叉积)

    67-三角形面积 内存限制:64MB 时间限制:3000ms 特判: No 通过数:8 提交数:13 难度:2 题目描述: 给你三个点,表示一个三角形的三个顶点,现你的任务是求出该三角形的面积 输入描 ...

  3. hdu 1863 畅通工程 (prim)

    畅通工程Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  4. 【并发编程】Java中的原子操作

    什么是原子操作 原子操作是指一个或者多个不可再分割的操作.这些操作的执行顺序不能被打乱,这些步骤也不可以被切割而只执行其中的一部分(不可中断性).举个列子: //就是一个原子操作 int i = 1; ...

  5. Entity Framework Core For MySql查询中使用DateTime.Now的问题

    背景 最近一直忙于手上澳洲线上项目的整体迁移和升级的准备工作,导致博客和公众号停更.本周终于艰难的完成了任务,借此机会,总结一下项目中遇到的一些问题. EF Core一直是我们团队中中小型项目常用的O ...

  6. find_all的用法 Python(bs4,BeautifulSoup)

    find_all()简单说明: find_all() find_all() 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件 用法一: rs=soup.find_all('a') 将返 ...

  7. css优先级之important

    css优先级之important

  8. Linux -- 进程管理之僵尸进程

    UNIX 存在一种机制:在每个进程退出的同时,操作系统释放该进程所有资源,但仍然保留一定的信息(PID / Status / runtime),直到父进程执行 wait() / waitpid(),以 ...

  9. Java基础面试题及答案(三)

    多线程 35. 并行和并发有什么区别? 并行是指两个或者多个事件在同一时刻发生:而并发是指两个或多个事件在同一时间间隔发生. 并行是在不同实体上的多个事件,并发是在同一实体上的多个事件. 在一台处理器 ...

  10. HTML标签-->HTML5新增

    HTML的Form新增属性 <form method="post" name="myForm"> <p> 邮箱:<input ty ...