虽然现代化的 web 开发更多地依赖各类 MVC 框架,但开发者仍须要熟练掌握 HTML 与 DOM 方面的基础知识。不过,即便是有着多年经验的前端开发者,也会遇到一些不明因此的状况。本文首先将为初学者介绍 HTML 与 DOM 的基本常识,随后为你们介绍15个比较冷门的 HTML 元素的方法。html
初学者指导
首先让咱们来讨论一下 HTML 与 DOM 之间的区别。前端
显然,普通的 <table> 元素就是一段 HTML 代码,它能够应用在任何一个以 .html 为扩展名的文件中。元素自带一系列特性(attribute),以控制它的显示方式与行为。web
这就是元素的所有内容,它与 JavaScript 没有任何关联。chrome
而 DOM 的做用是将你的 JavaScript 代码与文档中的 HTML 元素关联在一块儿,让你可以以对象的方式与这些元素进行交互。数组
这就是所谓的文档对象模型。浏览器
在 HTML 中的每一个元素都对应着一个 DOM ‘接口’,其中定义了若干属性(property,一般会映射至 HTML 元素上的特性)与方法。举例来讲,一个 <table> 元素对应着一个 HTMLTableElement 接口。app
你能够按如下方式获取某个元素的引用:框架
const searchBox = document.getElementById('search-box');
如今,你就能够访问该元素上定义的全部属性与方法了。打个比方,你能够经过 searchBox.value
访问它的 value 属性,也能够调用 searchBox.focus()
方法让光标移至输入框上。chrome-devtools
感谢你参加这个58秒的 DOM 入门培训课程,哈哈。布局
如今的问题在于,大多数元素都没有提供什么有趣的方法。所以,除非你特地到官方文档规范上去搜索那些可能永远都用不到的东西,不然很容易忽略掉那些零散的小技巧。
幸运的是,浏览规范与整理小技巧正是我用于避免陷入困境的两种最喜欢的方式。那么,让咱们开始吧……
若是你也但愿尝试一下这些技巧,又刚好有一些浏览器 DevTools 可使用的话,能够在元素树型结构中先选中某个元素,而后在控制台中输入 $0
,它会返回给你一个所选中元素的引用。若是你须要返回该元素的对象,请输入 dir($0)
。
原始的 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)
的做用是同样的,但除此以外,你还能够指定 beforebegin
、afterbegin
或 afterend
这几个参数值,元素将按这些值的名称所示插入相应的位置。
多么强大的控制能力!
多棒的点子。
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接口,该接口提供了一系列辅助属性,例如 prototol
与 hash
等等,以展示与连接的目标相关的值。
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()
感谢你耐心阅读本文,但愿本文能为你带来一些新知识,给你的工做带来实际的用途。