写点什么

你或许从未听说过的 15 个 HTML 元素方法

  • 2018-08-16
  • 本文字数:8340 字

    阅读完需:约 27 分钟

虽然现代化的 web 开发更多地依赖各种 MVC 框架,但开发者仍需要熟练掌握 HTML 与 DOM 方面的基础知识。不过,即使是有着多年经验的前端开发者,也会遇到一些不明所以的情况。本文首先将为初学者介绍 HTML 与 DOM 的基本常识,随后为大家介绍 15 个比较冷门的 HTML 元素的方法。

初学者指导

首先让我们来讨论一下 HTML 与 DOM 之间的区别。

显然,普通的 <table> 元素就是一段 HTML 代码,它可以应用在任何一个以 .html 为扩展名的文件中。元素自带一系列特性(attribute),以控制它的显示方式与行为。

这就是元素的全部内容,它与 JavaScript 没有任何关联。

而 DOM 的作用是将你的 JavaScript 代码与文档中的 HTML 元素关联在一起,让你能够以对象的方式与这些元素进行交互。

这就是所谓的文档对象模型。

在 HTML 中的每个元素都对应着一个 DOM ‘接口’,其中定义了若干属性(property,通常会映射至 HTML 元素上的特性)与方法。举例来说,一个 <table> 元素对应着一个 HTMLTableElement 接口。

你可以按以下方式获取某个元素的引用:

复制代码
const searchBox = document.getElementById('search-box');

现在,你就可以访问该元素上定义的所有属性与方法了。打个比方,你可以通过 searchBox.value 访问它的 value 属性,也可以调用 searchBox.focus() 方法让光标移至输入框上。

感谢你参加这个 58 秒的 DOM 入门培训课程,哈哈。

现在的问题在于,大多数元素都没有提供什么有趣的方法。因此,除非你特意到官方文档规范上去搜索那些可能永远都用不到的东西,否则很容易忽略掉那些零散的小技巧。

幸运的是,浏览规范与整理小技巧正是我用于避免陷入困境的两种最喜欢的方式。那么,让我们开始吧……

如果你也希望尝试一下这些技巧,又恰好有一些浏览器 DevTools 可以使用的话,可以在元素树型结构中先选中某个元素,然后在控制台中输入 $0,它会返回给你一个所选中元素的引用。如果你需要返回该元素的对象,请输入 dir($0)

在控制台中,你可以实现各种丰富的功能。

图片来自 Unsplash ,由 Holger Link 上传

#1 table 的方法

原始的 table 元素(时至今日仍然是网站布局方法里的第一名)本身自带许多精巧的方法,使用这些方法创建表格就像搭建宜家里的桌子一样简单。

以下是部分实用的方法。

复制代码
const tableEl = document.querySelector('table');
const headRow = tableEl.createHead().insertRow();
headerRow.insertCell().textContent = 'Make';
headerRow.insertCell().textContent = 'Model';
headerRow.insertCell().textContent = 'Color';
const newRow = tableEl.insertRow();
newRow.insertCell().textContent = 'Yes';
newRow.insertCell().textContent = 'No';
newRow.insertCell().textContent = 'Thank you';

整段代码里完全用不着使用 document.createElement() 方法。

如果你在一个 table 元素上直接调用 .insertRow() 方法,它甚至会自动为你插入一个 <tbody> 元素,是不是很棒?

#2 scrollIntoView()

你知道吗?当页面的 URL 中包含 #something 元素时,一旦页面加载,浏览器就会自动滚动至具有这个 ID 的元素之处。

这确实是一项很贴心的功能,但如果你在页面加载之后再渲染元素,这项功能就不起作用了。

不过,你也可以通过以下方式,手动地让这项功能重新生效:

复制代码
document.querySelector(document.location.hash).scrollIntoView();

#3 hidden

好吧,hidden 或许不是一个方法,但如果你提出抗议,那我也要争论一下:在 hidden 的背后很可能对应着一个 setter,这可是一个货真价实的方法,对不对?

不管怎样,你是否曾经为了隐藏某个元素而使用过 myElement.style.display = 'none' 这种方法呢?如果是的话,请别再这么做了!

只需调用 myElement.hidden = true ,即可实现元素隐藏的功能。

#4 toggle()

嗯,toggle 也不算是元素的方法,它实际上是元素属性上的一个方法。严格来说,这是一种为元素添加或删除某个 class 的方法,具体做法是 myElement.classList.toggle('some-class')

如果你曾经通过 if 条件语句为元素添加 class,那就应该赶紧改用这种做法。

正确的方式是为 toggle 方法传入第二个参数,如果该参数返回 true ,则指定的 class 就会添加至元素上。

复制代码
el.classList.toggle('some-orange-class', theme === 'orange');

我知道你在想些什么:这种写法违背了 'toggle' 这个词的本义(开关),那些从 IE 时代过来的开发者们都这么想,他们断言应当彻底摒弃使用第二个参数的做法。

所以,我收回我的话。不必坚持这种写法了,各位请随意!

#5 querySelector()

好吧,你当然知道这个方法,但据我推测,应该只有 17% 的开发者才知道,该方法可以使用在任意元素上。

打个比方,myElement.querySelector('.my-class') 的作用是返回在 myElement 的子代中包含 my-class 这个 class 的所有元素。

#6 closest

该方法可在任意元素上使用,它能够向上查找元素的树型结构,可以理解为 与 querySelector() 相反的方法。因此,我可以通过以下方法获取当前内容的对应标头:

复制代码
myElement.closest('article').querySelector('h1');

这段方法首先向上找到最近的 <article> 元素,然后再向下找到最近的 <h1> 元素。

#7 getBoundingClientRect()

在对 DOM 元素调用该方法时,将返回一个包含其空间结构详细信息的简单对象。

复制代码
{
x: 604.875,
y: 1312,
width: 701.625,
height: 31,
top: 1312,
right: 1306.5,
bottom: 1343,
left: 604.875
}

不过,在调用该方法时需要注意两点:

  • 调用该方法会导致元素的重绘,根据设备与页面复杂程度的不同,重绘的时间可能会占用几毫秒。因此,如果你需要重复地调用该方法,例如在使用动画的场景下,需要特别注意这一点。
  • 并非所有的浏览器都会返回这些值,他们有这个责任么?

#8 matches()

假设我需要检查某个元素是否包括一个特定的 class。

这是最复杂的方式:

复制代码
if (myElement.className.indexOf('some-class') > -1) {
// do something
}

比上面的好一点,但和本文没什么关系:

复制代码
if (myElement.className.includes('some-class')) {
// do something
}

最佳方式:

复制代码
if (myElement.matches('.some-class')) {
// do something
}

#9 insertAdjacentElement()

我今天才刚学到这一条!它的作用类似于 appendChild() ,但能够更好地控制插入子元素的具体位置。

parentEl.insertAdjacentElement('beforeend', newEl)parentEl.appendChild(newEl) 的作用是一样的,但除此之外,你还可以指定 beforebeginafterbeginafterend 这几个参数值,元素将按这些值的名称所示插入相应的位置。

多么强大的控制能力!

多棒的点子。

#10 contains()

你有没有遇到过这样的情形,需要知道某个元素是否被包含在另一个元素中?至少我本人经常会遇到这样的问题。

打个比方,假设我在处理一个鼠标点击事件时,需要知道它是发生在一个模态窗口中还是发生在外面(这样我才能够关闭这个窗口),我大概会这么做:

复制代码
const handleClick = e => {
if (!modalEl.contains(e.target)) modalEl.hidden = true;
};

代码中的 modalEl 是模态窗口的引用,而 e.target 则代表各种发生点击事件的元素。

有趣的是,每当遇到这种情形,在我第一遍写代码的时候,100% 的概率会将其中的判断逻辑写反。

哪怕是我提高了警惕,并在保存代码之前尝试将逻辑颠倒过来写,仍然还是写错。

#11 getAttribute()

这毫无疑问是所有元素方法中最没用的一个,但有一个场景除外。

你是否记得,我在本文的开头部分曾提到,对象的属性 property 通常也会映射到它的特性 attribute 中(我在上文中特别用粗体强调了这一点,注意不是斜体)?

但在某一个场景中,这种假设并不成立,这就是某个元素的 href 特性,例如 <a href="/animals/cat">Cat</a>

调用 el.href 不会返回 /animals/cat,这可能与你的猜测不符。原因在于 <a> 元素实现了 HTMLHyperlinkElementUtils 接口,该接口提供了一系列辅助属性,例如 prototolhash 等等,以展现与链接的目标相关的值。

href 就是其中一个实用的属性,它将返回完整的 URL,并去掉无用的空格,而不是返回在特性中所指定的相对 URL。

这样一来,如果你需要获取 href 特性中的字符串字面值,就只能使用 el.getAttribute('href') 方法了。

#12 dialog 元素的三大法宝

<dialog> 是一个相对较新的元素,它带来了两个还算能用的方法,和一个非常棒的方法。其中show()close() 方法的功能与你所想象的一样,我感觉还算可以。

showModal() 方法能够将 <dialog> 元素显示在页面的顶层,居中对齐,这正是所期望的模态窗口行为。你无需指定 z-index,或者手动添加一个灰色的背景,也不需要监听 esc 按键以关闭此窗口。浏览器能够理解模态窗口的工作方式,并自动完成你所期望的行为。

这真是太棒了。

#13 forEach()

某些情况下,当你获取到一个元素列表的引用时,可以通过 forEach() 方法进行迭代式调用。

for() 进行循环已经是 2014 年代的老古董了。

假设你需要记录页面中所有链接的 URL,可以输入以下代码,只要你不介意看到报错。

复制代码
document.getElementsByTagName('a').forEach(el ==> {
console.log(el.href);
});

也可以这么做:

复制代码
document.querySelectorAll('a').forEach(el ==> {
console.log(el.href);
});

问题出在 getElementsByTagName 与其他类似的 get... 方法返回的是一个 HTMLCollection 接口,而 querySelectorAll返回的是一个 NodeList 接口。

NodeList 接口为我们提供了 forEach() 方法(此外还包括 keys()values(),和 entries() 等方法 )。

理想的情况下,最好是每个方法都只返回简单的数组,而不是返回一些类似数组的对象。不过别担心,ECMA 大神为我们提供了 Array.from() 方法,它能够把所有这些类数组对象转化为一个真正的数组。

所以,这样的代码就能够正常工作:

复制代码
Array.from(document.getElementsByTagName('a')).forEach(el ==> {
console.log(el.href);
});

奖励关卡:创建了一个数组之后,你就能够对其使用 map()filter()reduce() 以及其他各种数组方法了。打个比方,先不管目的是什么,总之你可以按以下方式返回所有外部链接的数组:

复制代码
Array.from(document.querySelectorAll('a'))
.map(el => el.origin)
.filter(origin => origin !== document.origin)
.filter(Boolean);

我最喜欢的一个方法是 .filter(Boolean),它肯定会给将来的我在调试问题时带来无穷的烦恼,哈哈。

#14 表单

或许你已经知道,<form> 有一个 submit() 方法。但或许你不知道表单还有一个 reset() 方法,而且当你需要对表单元素进行验证时,还可以调用 reportValidity() 方法。

此外,你也可以通过对表单的 elements 属性加上元素的 name 特性 的方式调用它的属性。打个比方,myFormEl.elements.email 将返回属于某个 <form> 中的 <input name="email" /> 元素(‘属于’,并不代表它一定是一个‘子元素’)。

好吧,其实刚才我是骗你的。elements 并不会返回一个元素列表,而是返回一个控件列表(显然它不是一个数组,因为没必要这么做)。

举例来说:假设你有三个单选按钮,每个都有相同的名称 animal,那么 formEl.elements.animal 将返回一个单选按钮集的引用(一个控件,三个元素)。

formEl.elements.animal.value 将返回所选中的单选按钮的值。

这种语法看起来非常古怪,让我们来分解一下看看:formEl 是一个元素,elements 则对应 HTMLFormControlsCollection 接口,这并非一个真正的数组,其中的每一项内容也未必代表一个 HTML 元素。animal 是多个单选按钮的集合,只是因为他们具有相同的 name 特性才聚集在一起( RadioNodeList 接口就是为此而生的),而 value 则返回该集合中所选中的那个单选按钮的 value 特性。

非常直观,嗯……

#15 select()

我或许应该重新组织一下本文,让最后一条内容不会显得过于无足轻重。不管怎样,.select() 方法会将你指定的元素中的所有内容全选。

真方便。

感谢你耐心阅读本文,希望本文能为你带来一些新知识,给你的工作带来实际的用途。

请始终记住在使用这些技巧前先查阅一下浏览器的支持情况,万一遇到什么令你上火的情况,别揪自己的眉毛出气。

查看英文原文 https://hackernoon.com/15-html-element-methods-youve-potentially-never-heard-of-fc6863e41b2a

感谢覃云对本文的审校。

2018-08-16 18:152222
用户头像

发布了 428 篇内容, 共 177.9 次阅读, 收获喜欢 38 次。

关注

评论 1 条评论

发布
暂无评论
发现更多内容

行业大咖齐聚 多元主题碰撞 OpenHarmony技术日等你来

Geek_283163

OpenAtom OpenHarmony

预备,请关注!DingOS先导小视频发布!

鼎道智联

TASKCTL 资源树刷新、重载和重置的不同区别

敏捷调度TASKCTL

DevOps 分布式 敏捷开发 自动化运维 大数据运维

华为云GaussDB(for Influx)揭密:数据分级存储

华为云开发者联盟

华为云 GaussDB(for Influx) 时序数据 数据分级存储 Influx

TiDB 6.0 的「元功能」:Placement Rules in SQL 是什么?

PingCAP

2021年最新基于Spring Cloud的微服务架构分析

爱好编程进阶

Java 面试 后端开发

制约国内企业知识管理发展的因素

小炮

知识管理 企业知识管理 企业知识管理工具

区块链+数字资产,未来财富的新起点

易观分析

区块链 数字经济

CAT客户端如何从Apollo中读取配置?

爱好编程进阶

Java 面试 后端开发

OceanBase 参编金科联盟标准发布,推动数据库产业规范化发展

OceanBase 数据库

oceanbase

Build On 活动预告 | 构建你的第一个基于知识图谱的推荐模型

亚马逊云科技 (Amazon Web Services)

云计算

叮咚!请查收来自一线数据科学家和大数据工程师的实战经验| IDP Meetup No.02 回顾

Baihai IDP

Python 大数据 AI 算法 数据科学

2021银四特惠:分享字节跳动(提前批

爱好编程进阶

Java 面试 后端开发

985应届进大厂,后端开发两年被裁,对前路迷茫的我

爱好编程进阶

Java 面试 后端开发

湖仓一体,Hologres加速云数据湖DLF技术原理解析

阿里云大数据AI技术

sql 大数据 分布式计算 Cloud Native MaxCompute

【高并发】如何解决可见性和有序性问题?这次彻底懂了!

冰河

并发编程 多线程 协程 异步编程 精通高并发系列

GAIA-IR: GraphScope 上的并行化图查询引擎

6979阿强

大数据 并行计算 图计算 GraphScope 图分析

显卡是什么?显卡和Graphics的区别在哪里?

Finovy Cloud

人工智能 算法 云服务器 GPU服务器 显卡、gpu

Git技巧小能手

西门阿杰

git 版本控制

玩转LiteOS组件:玩转Librws

华为云开发者联盟

TCP websocket LiteOS LiteOS组件 Librws

报告解读下载 | 首份《中国数据库行业分析报告》重磅发布!

墨天轮

数据库 国产数据库 达梦 polarDB gbase8a

TASKCTL 消息订阅参数设置说明

敏捷调度TASKCTL

DevOps 分布式 敏捷开发 ETL任务 自动化运维

10年Java开发经验,超过500人面试阿里的同学,总结出这108道面试题

爱好编程进阶

Java 面试 后端开发

2021阿里技术官重磅推出“Java进阶必备宝典” 5大专题 6000字解析

爱好编程进阶

Java 面试 后端开发

Alibaba最新出品Java面试手册,号称金九银十面试“完美日记

爱好编程进阶

Java 面试 后端开发

BS-GX-016基于SSM实现教材管理系统

爱好编程进阶

Java 面试 后端开发

为什么需要可编程代理

Flomesh

负载均衡 代理 Pipy 可编程代理

百度小程序包流式下载安装优化

百度Geek说

小程序

Envoy熔断限流实践(二)Rainbond基于RLS服务全局限流

北京好雨科技有限公司

流程图太大? 来个流程收缩展开功能

相续心

前端 工作流 流程图 SOP

什么是域名(Domain Name ) ?

源字节1号

你或许从未听说过的15个HTML元素方法_语言 & 开发_David Gilbertson_InfoQ精选文章