JavaScript Document
DOM节点类
每个 DOM 节点都属于相应的内建类。层次结构(hierarchy)的根节点是 EventTarget,Node 继承自它,其他 DOM 节点继承自 Node
- EventTarget — 是根的抽象类。该类的对象从未被创建。它作为一个基础,以便让所有 DOM 节点都支持所谓的“事件(event)”。
- Node — 也是一个抽象类,充当 DOM 节点的基础。它提供了树的核心功能:
parentNode
,nextSibling
,childNodes
等(它们都是 getter)。Node
类的对象从未被创建。但是有一些继承自它的具体的节点类,例如:文本节点的Text
,元素节点的Element
,以及更多异域(exotic)类,例如注释节点的Comment
。 - Element — 是 DOM 元素的基本类。它提供了元素级的导航(navigation),例如
nextElementSibling
,children
,以及像getElementsByTagName
和querySelector
这样的搜索方法。浏览器中不仅有 HTML,还会有 XML 和 SVG。Element
类充当更多特定类的基本类:SVGElement
,XMLElement
和HTMLElement
。 - HTMLElement— 最终是所有 HTML 元素的基本类。各种 HTML 元素均继承自它:
- HTMLInputElement —
<input>
元素的类, - HTMLBodyElement —
<body>
元素的类, - HTMLAnchorElement —
<a>
元素的类, - ……等,每个标签都有自己的类,这些类可以提供特定的属性和方法
- HTMLInputElement —
小技巧:
console.dir(elem)
与 console.log(elem)
对于 DOM 元素,它们是不同的:
console.log(elem)
显示元素的 DOM 树。console.dir(elem)
将元素显示为 DOM 对象,非常适合探索其属性。
搜索文档
方法名 | 搜索方式 | 可以在元素上调用? | 实时的? |
---|---|---|---|
querySelector | CSS-selector | ✔ | - |
querySelectorAll | CSS-selector | ✔ | - |
getElementById | id | - | - |
getElementsByName | name | - | ✔ |
getElementsByTagName | tag or '*' | ✔ | ✔ |
getElementsByClassName | class | ✔ | ✔ |
目前为止,最常用的是 querySelector
和 querySelectorAll
,但是 getElement(s)By*
可能会偶尔有用,或者可以在旧脚本中找到。
此外:
elem.matches(css)
用于检查elem
与给定的 CSS 选择器是否匹配。elem.closest(css)
用于查找与给定 CSS 选择器相匹配的最近的祖先。elem
本身也会被检查。
让我们在这里提一下另一种用来检查子级与父级之间关系的方法,因为它有时很有用:
- 如果
elemB
在elemA
内(elemA
的后代)或者elemA==elemB
,elemA.contains(elemB)
将返回 true
节点属性
nodetype
它提供了另一种“过时的”用来获取 DOM 节点类型的方法。
它有一个数值型值(numeric value):
- 对于元素节点
elem.nodeType == 1
, - 对于文本节点
elem.nodeType == 3
, - 对于 document 对象
elem.nodeType == 9
nodeName 和 tagName
获取标签名,差异:tagName
属性仅适用于 Element
节点,nodeName
是为任意 Node
定义的(如注释节点)
innerHTML
将元素中的 HTML 获取为字符串形式,我们也可以修改它。因此,它是更改页面最有效的方法之一。注意,修改时会将内容**“归零”**并从头开始重写,因此所有的图片和其他资源都将重写加载
outerHTML
将元素中的 HTML 获取为字符串形式,除了包含innerHTML的全部内容外, 还包含对象标签本身。写入 outerHTML
不会改变元素。而是在 DOM 中替换它
<div>Hello, world!</div>
<script>
let div = document.querySelector('div');
// 使用 <p>...</p> 替换 div.outerHTML
div.outerHTML = '<p>A new element</p>'; // (*)
// 蛤!'div' 还是原来那样!
alert(div.outerHTML); // <div>Hello, world!</div> (**)
// 但是这里HTML实际上已经变成了<p>A new element</p>
</script>
上面代码的过程可以理解为:
div
被从文档(document)中移除。- 另一个 HTML 片段
<p>A new element</p>
被插入到其位置上。 div
仍拥有其旧的值。新的 HTML 没有被赋值给任何变量。原来的值不在文档树中,但是仍在内存中!
nodeValue/data
innerHTML
属性仅对元素节点有效。其他节点类型,例如文本节点,具有它们的对应项:nodeValue
和 data
属性。这两者在实际使用中几乎相同,只有细微规范上的差异。
textContent
textContent
提供了对元素内的 文本 的访问权限:仅文本,去掉所有 <tags>
。与用户交互使用这个更加安全
hidden
从技术上来说,hidden
与 style="display:none"
做的是相同的事。但 hidden
写法更简洁。下面时设置元素闪烁的方法
<div id="elem">A blinking element</div>
<script>
setInterval(() => elem.hidden = !elem.hidden, 1000);
</script>
其他属性
DOM 元素还有其他属性,特别是那些依赖于 class 的属性:
value
—<input>
,<select>
和<textarea>
(HTMLInputElement
,HTMLSelectElement
……)的 value。href
—<a href="...">
(HTMLAnchorElement
)的 href。id
— 所有元素(HTMLElement
)的 “id” 特性(attribute)的值。- ……
修改文档
- 创建新节点的方法:
document.createElement(tag)
— 用给定的标签创建一个元素节点,document.createTextNode(value)
— 创建一个文本节点(很少使用)elem.cloneNode(deep)
— 克隆元素,如果deep==true
则与其后代一起克隆。如果为false则只会克隆标签和标签内的东西,如类,不包括子节点(含文本节点)
// 创建新节点
// 1. 创建 <div> 元素
let div = document.createElement('div');
// 2. 将元素的类设置为 "alert"
div.className = "alert";
// 3. 填充消息内容
div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";
-
插入和移除节点的方法:
node.append(...nodes or strings)
— 在node
末尾插入,node.prepend(...nodes or strings)
— 在node
开头插入,node.before(...nodes or strings)
— 在node
之前插入,node.after(...nodes or strings)
— 在node
之后插入,node.replaceWith(...nodes or strings)
— 替换node
。node.remove()
— 移除node
。
注意:所有插入方法都会自动从旧位置删除该节点。
参数为
strings
时,文本字符串被“作为文本”插入。 -
这里还有“旧式”的方法:
parent.appendChild(node)
parent.insertBefore(node, nextSibling)
parent.removeChild(node)
parent.replaceChild(newElem, node)
这些方法都返回
node
。 -
在
html
中给定一些 HTML,elem.insertAdjacentHTML(where, html)
会根据where
的值来插入它:"beforebegin"
— 将html
插入到elem
前面,"afterbegin"
— 将html
插入到elem
的开头,"beforeend"
— 将html
插入到elem
的末尾,"afterend"
— 将html
插入到elem
后面。
另外,还有类似的方法,elem.insertAdjacentText
和 elem.insertAdjacentElement
,它们会插入文本字符串和元素,但很少使用。
- 要在页面加载完成之前将 HTML 附加到页面:
document.write(html)
样式和类
className 、 classList 和 style
- className
<body class="main page">
<script>
alert(document.body.className); // main page
</script>
</body>
- classList
elem.classList.add/remove(class)
— 添加/移除类。elem.classList.toggle(class)
— 如果类不存在就添加类,存在就移除它。elem.classList.contains(class)
— 检查给定类,返回true/false
。
<body class="main page">
<script>
// 添加一个 class
document.body.classList.add('article');
alert(document.body.className); // main page article
</script>
</body>
-
style
- 普通属性:
elem.style.attr
- 前缀属性:如
button.style.MozBorderRadius = '5px';
。连字符-
表示大写 - 重置样式属性:置空即可,如
document.body.style.display = ""
- 普通属性:
-
style.cssText
不能直接通过改变style来进行批量更改,因为style对象是只读的(只有style是可读的因此可以改变引用指向地址上的内容,即attr),但是可以通过**
style.cssText
**进行完全的重写。注意,该方法会删除现有样式!
<div id="div">Button</div>
<script>
// 我们可以在这里设置特殊的样式标记,例如 "important"
div.style.cssText=`color: red !important;
background-color: yellow;
width: 100px;
text-align: center;
`;
alert(div.style.cssText);
</script>
也可以通过setAttribute('style','css')
达到效果,如div.setAttribute('style', 'color: red...')
计算样式
style
属性仅对"style"
特性(attribute)值起作用,而没有任何 CSS 级联(cascade),因此我们无法使用elem.style
读取来自 CSS 类(可以读取行内式)的任何内容:
<head>
<style> body { color: red; margin: 5px } </style>
</head>
<body>
The red text
<script>
alert(document.body.style.color); // 空的
alert(document.body.style.marginTop); // 空的
</script>
</body>
- 解决方案:
getComputedStyle(element, [pseudo])
- element:需要被读取样式值的元素
- pseudo:伪元素,例如
::before
。空字符串或无参数则意味着元素本身 - 返回值:结果是一个具有样式属性的对象。类似于
elem.style
。返回的值都需要完整的如paddingLeft
<head>
<style> body { color: red; margin: 5px } </style>
</head>
<body>
<script>
let computedStyle = getComputedStyle(document.body);
// 现在我们可以读取它的 margin 和 color 了
alert( computedStyle.marginTop ); // 5px
alert( computedStyle.color ); // rgb(255, 0, 0)
</script>
</body>
元素大小和滚动
几何
offsetParent、offsetLeft、offsetTop(只读)
-
offsetParent
是最接近的祖先(ancestor),在浏览器渲染期间,它被用于计算坐标。最近的祖先为下列之一:- CSS 定位的(
position
为absolute
,relative
或fixed
), - 或
<td>
,<th>
,<table>
, - 或
<body>
- CSS 定位的(
-
属性
offsetLeft/offsetTop
提供相对于offsetParent
左上角的 x/y 坐标。如:
- 有以下几种情况下,
offsetParent
的值为null
:- 对于未显示的元素(
display:none
或者不在文档中)。 - 对于
<body>
与<html>
。 - 对于带有
position:fixed
的元素
- 对于未显示的元素(
offsetWidth、offsetHeight(只读)
它们提供了元素的“外部” width/height。或者,换句话说,它的完整大小(包括边框)
检查元素是否被隐藏
function isHidden(elem) {
return !elem.offsetWidth && !elem.offsetHeight;
}
clientTop、clientLeft、clientWidth、clientHeight(只读)
-
clientLeft:表示一个元素的左边框的宽度,以像素表示。如果元素的文本方向是从右向左(RTL, right-to-left),并且由于内容溢出导致左边出现了一个垂直滚动条,则该属性包括滚动条的宽度。
clientLeft
不包括左外边距和左内边距 -
clientTop:一个元素顶部边框的宽度(以像素表示)。不包括顶部外边距或内边距。
-
clientWidth:内联元素以及没有 CSS 样式的元素的
**clientWidth**
属性值为 0。**Element.clientWidth**
属性表示元素的内部宽度,以像素计。该属性包括内边距 padding,但不包括边框 border、外边距 margin 和垂直滚动条(如果有的话) -
clientHeight:对于没有定义CSS或者内联布局盒子的元素为0,否则,它是元素内部的高度(单位像素),包含内边距,但不包括水平滚动条、边框和外边距
scrollWidth、scrollHeight(只读)scrollLeft、scrollTop(可写)
-
scrollwidth、scrollHeight:这些属性就像
clientWidth/clientHeight
,但它们还包括滚动出(隐藏)的部分
-
scrollLeft、scrollTop:(可写)属性
scrollLeft/scrollTop
是元素的隐藏、滚动部分的 width/height。在下图中,我们可以看到带有垂直滚动块的scrollHeight
和scrollTop
Window大小和滚动
窗口的width/height
document.documentElement
的clientWidth/clientHeight
(会去除滚动条的高宽,window.innerWidth/Height不
会 )
文档width/height
规定:
let scrollHeight = Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
);
alert('Full document height, with scrolled out part: ' + scrollHeight);
获取当前滚动
window.pageXOffset/pageYOffset
滚动
-
可以通过更改
scrollTop/scrollLeft
来滚动常规元素。我们可以使用document.documentElement.scrollTop/scrollLeft
对页面进行相同的操作(Safari 除外,而应该使用document.body.scrollTop/Left
代替) -
通用解决方案:
window.scrollBy(x,y)
和window.scrollTo(pageX,pageY)
其中x、y是相对于当前坐标的x、y值,pageX、pageY是绝对坐标
scrollIntoView
对 elem.scrollIntoView(top)
的调用将滚动页面以使 elem
可见。它有一个参数:
- 如果
top=true
(默认值),页面滚动,使elem
出现在窗口顶部。元素的上边缘将与窗口顶部对齐。 - 如果
top=false
,页面滚动,使elem
出现在窗口底部。元素的底部边缘将与窗口底部对齐
禁止滚动
document.body.style.overflow = "hidden"
坐标
两种坐标系
- **相对于窗口:**类似于
position:fixed
,从窗口的顶部/左侧边缘计算得出,我们将这些坐标表示为clientX/clientY
- 相对于文档:与文档根(document root)中的
position:absolute
类似,从文档的顶部/左侧边缘计算得出。我们将它们表示为pageX/pageY
。
元素坐标:getBoundingClientRect
方法 elem.getBoundingClientRect()
返回最小矩形的窗口坐标,该矩形将 elem
作为内建 DOMRect 类的对象。主要的 DOMRect
属性:
x/y
— 矩形原点相对于窗口的 X/Y 坐标,width/height
— 矩形的 width/height(可以为负)。
此外,还有派生(derived)属性:
top/bottom
— 顶部/底部矩形边缘的 Y 坐标,left/right
— 左/右矩形边缘的 X 坐标。
elementFromPoint(x,y)
对 document.elementFromPoint(x, y)
的调用会返回在窗口坐标 (x, y)
处嵌套最多(the most nested)的元素。
Element.getBoundingClientRect()
Element.getBoundingClientRect()
方法返回元素的大小及其相对于视口的位置。如果是标准盒子模型,元素的尺寸等于width/height
+ padding
+ border-width
的总和。如果box-sizing: border-box
,元素的的尺寸等于 width/height
。返回值是一个 DOMRect 对象,拥有left
, top
, right
, bottom
, x
, y
, width
, 和 height
这几个以像素为单位的只读属性
图片来源:https://zh.javascript.info