01、DOM(document)
DOM(Document Object Model)文档对象模型,将文档document
解析为一个由节点和对象(包含属性和方法的对象)组成的逻辑树,每个节点都是一个对象,document
对象就是整个DOM树的根节点。DOM提供的API方法用来管理这棵树,用JavaScript 等脚本语言操作文档结构、样式和内容。
1.1、DOM结构-节点/元素
DOM树是由很多节点构成,分为下面几种类型,注意节点是包含文本、注释的。
节点类型 | 描述 | 示例 |
---|---|---|
document 文档节点 |
document 文档本身,DOM的入口(根) |
document |
element 元素节点 |
所有HTML元素Element /ˈelɪmənt/ i |
html、body、a、p |
attribute 属性节点 |
元素的属性 | id、name、href、class |
文本节点 | 元素内的文本形成文本节点 | 文本字符、换行、空格 |
注释节点 | 注释 | <!--注释--> |
一颗完整的DOM树,除了显而易见的元素,还有注释、换行符构成的文本类节点。
<html> | |
<body> | |
The truth about elk. | |
<ol> | |
<li>An elk is a smart</li> | |
<!-- comment --> | |
<li>...and cunning animal!</li> | |
</ol> | |
</body> | |
</html> |
当前选择元素
$0
:在浏览器控制台中,$0
标识当前选中的元素。$0.style.background='red';
节点和元素在DOM树上的不同视图(导航方式)如下图,现代JS中主要使用元素导航属性。
✅节点-导航属性 | 描述 | ✅元素-导航属性 | 描述 |
---|---|---|---|
parentNode |
父节点 | parentElement |
父元素,一般和parentNode一样 ,只有document.documentElement不同 |
firstChild |
第一个子节点 | firstElementChild |
第一个子元素 |
lastChild |
最后一个子节点 | lastElementChild |
最后一个子元素 |
childNodes |
子节点集合 | children |
子元素集合 |
nextSibling |
下一个兄弟节点 | nextElementSibling |
下一个兄弟元素( Sibling/ˈsɪblɪŋ/ 兄弟) |
previousSibling |
上一个兄弟节点 | previousElementSibling |
上一个兄弟元素(/ˈpriːviəs/以前 ) |
document.documentElement.parentNode; //HTMLDocument | |
document.documentElement.parentElement; //null | |
document.body.childNodes.length; //33 | |
document.body.children.length; //16 | |
document.body.firstElementChild; |
❓元素节点:这里的导航属性都是只读的,不可用于插入、修改元素;都是实时的;集合是可迭代的,用
for..of
,不要用for(in)
1.2、节点Node/元素Element
节点/元素的继承层次结构如下,最基础的EventTarget
又继承自Object
,因此可以自由添加属性、方法。
- EventTarget:—— 是一切的根(“抽象(abstract)”类),定义了基础的事件。
- Node:也是一个“抽象”类,充当 DOM 节点的基础,提供了节点导航功能,如parentNode,nextSibling,childNodes 等。
- Document:表示整个文档,全局变量
document
就是他。 - CharacterData:一个文本“抽象”类,被Text(文本节点) 、Comment (注释节点)继承。(Character/ˈkærəktər/ 符号文字)
- Element:是 DOM 元素的基础类,提供了元素导航功能,如nextElementSibling,children等,以及搜索方法
getElementsByTagName
和querySelector
等。 - HTMLElement:所有 HTML 元素的基础类,常用的如:HTMLInputElement、HTMLBodyElement、HTMLAnchorElement、HTMLDivElement、HTMLTableElement
document.body.querySelector("div").constructor.name; //HTMLDivElement | |
document.body.querySelector("div").toString(); //[object HTMLDivElement] |
?Node节点
Node节点对象的属性:
Node属性/方法 | 描述 |
---|---|
data | 文本/注释节点的文本值,可修改,注释节点可用来存放一些待处理内容 |
nodeName | 节点名称 |
textContent | 元素内文本,不含标签。可修改,可安全的写入文本。 |
appendChild(node) | 从末尾追加一个子节点 |
removeChild(child) | 删除一个子节点 |
replaceChild(new,old) | 替换一个子节点 |
insertBefore(new,ref) | 在ref节点前插入一个节点 |
cloneNode(deep) | 克隆当前节点副本,deep=true会克隆其所有后代,否则只是节点本身 |
contains( node) | 是否包含指定的后代节点,返回bool |
hasChildNodes() | 是否包含子节点,返回bool |
// <div id="div">div内容<p>p内容</p></div> | |
let div = document.querySelector('#div'); | |
console.log(div.innerHTML,div.textContent,div.innerText); //div内容<p>p内容</p> div内容p内容 div内容\n\np内容 | |
console.log(div.nodeName, div.localName, div.tagName); //DIV div DIV |
?Element 元素
Element 基类元素对象提供的属性方法:
✅Element属性 | 描述 |
---|---|
id | 唯一id ,基础通用属性还要name 、class 、style |
localName | 本地化名称,名称相关的还有nodeName 、tagName |
classList、className | class样式集合、class样式值,集合可以新增、删除。 |
innerHTML | 元素标签内的所有HTML内容,可以修改,注意innerHTML+= 会进行完全重写 |
outerHTML | 包含元素本身及其内部的所有HTML内容,可以修改(删除并替换) |
innerText | 元素标签内的所有文本节点值,包含所有后代节点的文本。 |
hidden | 是否隐藏的bool属性, |
✅方法 | |
addEventListener() | 注册实践到EventTarget,还有配套的removeEventListener() |
append(node...) | 在末尾追加多个子节点(或DOMString=text节点) |
prepend(node...) | 在开头插入多个子节点(或DOMString) |
after(node...) | 在后面插入节点(或DOMString) |
before(node...) | 在前面插入节点(或DOMString) |
replaceWith(node) | 将 node 替换为给定的节点或字符串 |
remove(element) | 把对象从它所属的 DOM 树中删除 |
scroll()/scrollBy() | 元素内滚动 |
✅事件(event) | |
blur(event)、focusout() | 元素失去焦点,blur不会冒泡 (blue /blɜːr/ 模糊) |
focus、focusin | 元素获取焦点时触发,focus不会冒泡 |
keydown、keyup | 键盘按键按下、松开时触发 |
click(event) | 点击触发,通常是鼠标左键在一个元素上被按下并放开时 |
dblclick | 双击触发事件 |
mousedown、mouseup | 鼠标按下、弹起时触发 |
mousemove | 鼠标在元素上移动时触发 |
onmouseover、mouseout | 鼠标移入、移出元素区域时触发 |
mouseenter、mouseleave | 鼠标移入、移出元素区域时触发,与上面不同的是不会冒泡 |
<div class="h1 c2" id="firstDiv" myatt="自定义特性" order="asc">Element</div> | |
<script> | |
let div = document.querySelector('#firstDiv'); | |
div.classList.add("c5"); | |
div.setAttribute("att2", 3) //添加、设置属性值 | |
div.addEventListener("click", | |
(event) => { | |
console.log(event) | |
}); | |
div.hidden = true; | |
</script> |
?注意:DOM操作必须等节点初始化完成后才能进行。可以在html尾部执行JS,或者在
window
的onload
事件中处理
1.3、元素特性Attribute
元素特性的操作方法:
Element-特性方法 | |
---|---|
attributes | 所有属性节点的一个实时集合,支持移除项、属性值操作,ele.attributes["href"].value |
getAttribute(attName) | 获取属性值 |
getAttributeNames() | 所有属性名数组 |
setAttribute(attName,value) | 设置属性值,可以新增自定义特性,常用于设置css |
removeAttribute(attName) | 从指定的元素中删除一个属性 |
hasAttribute(attName) | 判断是否包含指定属性,返回一个布尔值 |
hasAttributes() | 判断是否包含属性,返回一个布尔值 |
- 特性(attribute)—— 写在 HTML 中的内容,值都是字符串,大小写不明感。
- 属性(property)—— DOM 对象中的内容,大小写敏感。
❗特性attribute与属性property:元素上的特性
attribute
与属性property
基本上都是一致相对应的。也有个别例外,如input.value
只能单向从特性attribute
同步到属性property
。
<style> | |
span[data-status="success"] { | |
color: green; | |
} | |
span[data-status="failure"] { | |
color: red; | |
} | |
</style> | |
<div> | |
<span data-status="success">操作结果</span> <!--自定义特性用来设置元素状态样式--> | |
</div> | |
<script> | |
const espan = document.querySelector("span[data-status]"); | |
console.log(espan.attributes); //所有属性集合 | |
console.log(espan.getAttributeNames()); //['data-status', 'class', 'style'] | |
console.log(espan.hasAttribute("data-status")); //true | |
//切换样式 | |
espan.attributes["data-status"].value="failure"; | |
espan.dataset.status="failure"; | |
espan.setAttribute("data-status","failure"); | |
espan.class="success"; | |
</script> |
dataset属性用来存放自定义的非标准特性,自定义的特性都以“data-”开头,这是专门保留给程序猿门使用的。如“data-status”,通过elem.dataset.status
访问(属性上没有前缀date-
)。这样可以避免HML语言升级后造成的名称冲突。
1.4、元素的大小位置
Element-大小位置 | 描述 |
---|---|
clientHeight、clientWidth | 元素内部高度、宽度,包括内边距(不含边框) |
offsetWidth/offsetHeight | 元素完整的宽度、高度,为元素的完整大小,包括border |
clientTop/Left | 元素边框宽度,同border宽度 |
offsetLeft/Top | 元素相对于 offsetParent 左上角的 x/y 坐标 |
scrollHeight 、scrollWidth | 元素滚动视图的高度、宽度,包含滑动隐藏部分。 |
scrollLeft/scrollTop | 元素滚出去(隐藏)的部分宽度、高 |
<style> | |
#sdiv { | |
border: 5px solid pink; | |
padding: 5px; | |
margin: 5px; | |
width: 200px; | |
height: 50px; | |
overflow: auto; | |
} | |
</style> | |
<div id="sdiv"> | |
光圈效应:会导致爱屋及乌的认知偏差,名人效应就是典型的光环效应。与之相反的是音叉效应(恶魔效应)。 | |
</div> | |
<script> | |
let sdiv = document.querySelector('#sdiv'); | |
console.log(sdiv.clientWidth, sdiv.clientHeight); //193 60 | |
console.log(sdiv.offsetWidth, sdiv.offsetHeight); //214 70 | |
console.log(sdiv.scrollWidth, sdiv.scrollHeight); //193 115 | |
console.log(sdiv.clientLeft,sdiv.clientHeight); //5 60 | |
console.log(sdiv.offsetLeft,sdiv.offsetTop); //13 261 | |
console.log(sdiv.scrollLeft,sdiv.scrollTop); //0 0 | |
</script> |
1.5、document
✅document属性 | 描述 |
---|---|
documentElement | 对应 <html> 标签的 顶层节点 |
all、head、body | 所有元素的集合、head节点、body节点 |
images | 所有<image> 图片元素集合 |
forms | 所有表单元素 <form> 的列表 |
links | 所有超链接的列表 |
scripts | 所有的 <script> 元素 |
✅扩展属性 | 继承自 HTMLDocument 接口 |
title | 文档标题(浏览器页签显示的名称)来自<title> 标签,可以动态修改。 |
readyState | document 的加载状态:loading( 文档正在被加载)、interactive( 文档被全部读取,/ˌɪntərˈæktɪv/ 交互)、complete(包括资源都已加载完成) |
domain、URL | 网址域名、完整URL地址。还有个location 属性同window.location |
cookie | 前页面的Cookie |
defaultView | 当前window的引用,window===document.defaultView; //true |
console.log(document.documentElement.childNodes); //[head, text, body] | |
console.log(document.documentElement.children); //[head, body] | |
//京东首页 | |
console.log(document.all.length, document.images.length, ); //8438 433 | |
console.log(document.links.length, document.scripts.length); //2771 34 | |
console.log(document.title); //京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物! | |
console.log(document.readyState); //complete | |
console.log(document.domain, document.URL); //www.jd.com https://www.jd.com/ | |
console.log(document.cookie); | |
console.log(document.defaultView === window); //true |
02、查找DOM元素
查找元素、节点的相关方法,最常用、好用的就是querySelector(cssSelector)
了,参数就是css选择器,这个方法设计的不错?。
属性/方法 | 描述 |
---|---|
querySelector(css) | 根据选择器查询返回第一个元素,selectors同CSS选择器 |
querySelectorAll(css) | 根据选择器查询返回NodeList集合(静态集合) |
id 全局变量 |
元素id 作为全局变量访问:hid.innerText="33";``window['hid'] 不推荐使用:容易命名冲突,不容易分辨变量来源 |
elem.matches(css) | 判断是否匹配css选择器,返回true 、false |
elem.closest(css) | 查找离元素最近的+css匹配的+祖先(/klosɪst/靠近),包含elem自己 |
document.getElementById(id) | 根据id获取一个元素,id重复则取第一个 |
document.getElementsByName(name) | 根据name 属性获取元素集合(动态),多用于单选框、复选框 |
getElementsByClassName(class) | 根据class 属性值获取元素集合(动态,会实时更新) |
getElementsByTagName(tag) | 根据元素标签名元素集合(动态),getElementsByTagName("div") |
//id=sdiv,可以直接使用 | |
console.log(sdiv.matches('.cname')) //false | |
console.log(sdiv.closest(':not(div)')); //body | |
document.body.getElementsByTagName("div"); //HTMLCollection | |
document.body.querySelectorAll("div"); //NodeList | |
document.body.querySelector("div") === document.body.querySelectorAll("div")[0]; //true | |
console.log(sdiv.closest(':not(div)')); //body |
03、增删改DOM元素
属性/方法 | 描述 |
---|---|
createElement(tagName) | 根据标签名创建节点元素:document.createElement("div") |
createTextNode(text) | 创建一个文本节点。也可用innerText 直接设置文本内容。 |
ele.append(child...) | 在末尾追加多个子节点(或DOMString=text节点) |
ele.prepend(child...) | 在开头插入多个子节点(或DOMString) |
ele.after(node...) | 在后面插入节点(或DOMString) |
ele.before(node...) | 在前面插入节点(或DOMString) |
ele.replaceWith(node, ... nodes) | 将 ele 替换为给定的节点或字符串 |
ele.remove(element) | 把对象从它所属的 DOM 树中删除 |
elem.insertAdjacentHTML(where, html) | 插入html字符,不会影响现有元素,性能比innerHTML 更好! |
node.cloneNode(deep) | 克隆当前节点副本,deep=true会克隆其所有后代,否则只是节点本身 |
ele.innerHTML | 设置标签HTML内容,也可以达到创建节点内容的目的 |
ele.innerText | 设置标签文本内容,兼容性最好? |
node.textContent | 设置节点文本内容,效果同上 |
?注意:
- 移动插入:所有插入方法,如果插入的是已存在DOM树上节点,都会自动从旧的位置删除再插入到新的位置。
- 下拉框
select
的选项option
可以通过其属性options
添加。obj1.options.add(new Option("abc",3))
let parent = document.createElement("div"); //这两个命名(parent、child)和window有冲突的,虽然可以用 | |
let child = document.createElement("p"); | |
child.innerText = "p-innertext"; | |
parent.appendChild(child); | |
parent.append("sam1", "sam2"); //插入多个文本节点 | |
child.replaceWith(document.createElement("span"), "text-"); //替换节点 | |
console.log(parent.outerHTML); //<div><span></span>text-sam1sam2</div> | |
parent.innerHTML = "<p>ptext</p>"; | |
console.log(parent.outerHTML); //<div><p>ptext</p></div> | |
parent.innerText = "<p>ptext</p>"; //会被转义 | |
console.log(parent.outerHTML); //<div><p>ptext</p></div> | |
sdiv.append(parent); |
3.1、远古的node方法
远古时期流传下来的node
提供的插入节点的方法,返回参数都是Node
。和element
方法主要区别是element
的方法参数可多个,方法也更简洁。
属性/方法 | 描述 |
---|---|
node.appendChild(child) | 从末尾追加一个子节点,支持字符串参数 |
node.insertBefore(new,ref) | 在ref节点前插入一个节点 |
node.replaceChild(new,old) | 替换一个子节点 |
node.removeChild(child) | 删除一个子节点 |
3.2、insertAdjacentHTML/Text/Element
插入HTML字符、文本、元素节点的通用方法:(adjacent /əˈdʒeɪs(ə)nt/ 相邻的)。
属性/方法 | 描述 |
---|---|
elem.insertAdjacentHTML(where, html) | 插入html字符,不会影响现有元素 |
elem.insertAdjacentElement(where, element) | 插入元素 |
elem.insertAdjacentText(where, text) | 插入文本 |
参数where是一个枚举字符,这个谁取的名真头疼:
- 'beforebegin':元素自身的前面。
- 'afterbegin':插入元素内部的第一个子节点之前。
- 'beforeend':插入元素内部的最后一个子节点之后。
- 'afterend':元素自身的后面。
<div> | |
<span id="adjacent">div</span> | |
</div> | |
<script> | |
const adiv = document.getElementById("adjacent"); | |
adiv.insertAdjacentHTML("beforebegin", "-beforebegin-"); | |
adiv.insertAdjacentText("afterbegin","-afterbegin-") | |
adiv.insertAdjacentText("beforeend","-beforeend-") | |
adiv.insertAdjacentText("afterend","-afterend-") | |
console.log(adiv.parentElement.innerHTML); | |
//-beforebegin-<span id="adjacent">-afterbegin-div-beforeend-</span>-afterend- | |
</script> |
3.3、DocumentFragment包装器
DocumentFragment 是一个特殊的节点,像一个容器,用来附加其他节点,然后传输给其他方法使用,当然好像也可以用数组代替。(Fragment /ˈfræɡmənt/ 片段)
const adiv=document.getElementById("adjacent"); | |
let fram=new DocumentFragment(); | |
fram.append(document.createElement("li")); | |
fram.append(document.createElement("li")); | |
adiv.append(fram); | |
console.log(adiv.outerHTML); |
04、DOM样式管理
属性/方法 | 描述 |
---|---|
HTMLElement.style | style是一个对象,对应于css中的属性,属性是驼峰命名的,注意值带单位! |
style.cssText | 设置style 样式字符串值,同html 标签中的style ,注意会覆盖已有style样式 |
ele.className | 获取、设置class样式值(字符串) |
ele.classList | 样式class 列表,DOMTokenList集合,支持add/remove/toggle方法 |
length、value | 数量,值(同className ) |
contains(css) | 是否包含指定样式,返回bool |
add(...css) | 添加(多个)样式 |
remove(...css) | 移除(多个)样式 |
replace(old, new) | 替换 |
toggle(css) | 不存在就添加,存在就移除(toggle /ˈtɑːɡl/ 切换) |
windwo.getComputedStyle(ele) | 计算/获取一个元素的已解析(resolved)样式style 对象(同elem.style ) |
重置style属性样式的方式:
- 值置空:
cdiv.style.backgroundColor="";
- 移除属性值:
cdiv.style.removeProperty("backgroundColor");
<div id="cssDiv" class="c1 c2 c3 c4">css</div> | |
<script> | |
const cdiv = document.getElementById("cssDiv"); | |
cdiv.style.backgroundColor = window.prompt("input background color", "red"); | |
cdiv.style.backgroundColor = ""; | |
cdiv.style.removeProperty("backgroundColor"); | |
cdiv.style.lineHeight = "30px"; //注意要带单位 | |
cdiv.style.cssText = "color:red;font-weight:bold;" //会覆盖前面的样式 | |
//计算样式 | |
const cdstyle = getComputedStyle(cdiv); | |
console.log(cdiv.className); //c1 c2 c3 c4 | |
console.log(cdiv.classList); //DOMTokenList(4) [ "c1", "c2", "c3", "c4" ] | |
cdiv.classList.toggle("c1"); | |
cdiv.classList.remove("c1", "c2"); | |
</script> |
05、滚动和坐标
有效的文档窗口区域高度、宽度,可以使用 document.documentElement
的 clientWidth/clientHeight
,不含滚动条。window.innerWidth/innerHeight 包括了滚动条。
文档完整大小(包含滚动隐藏的部分):documentElement.scrollWidth/scrollHeight,如果内容没有铺满,某些浏览器会小于clientWidth/clientHeight。因此获取完整文档高度,可以取多个值的最大值:
let scrollHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight, | |
document.body.offsetHeight, document.documentElement.offsetHeight, | |
document.body.clientHeight, document.documentElement.clientHeight); |
?滚动位置:
- window.scrollX/scrollY,一般就用这个就可以了。
- window.pageXOffset /pageYOffset ,是上面的别称。
- document.documentElement.scrollLeft/scrollTop
?滚动方法:方法更通用,兼容性更好。
- window.scrollBy(x,y) ,移动的距离。
- window.scrollTo(pageX,pageY),移动到坐标。
- ele.scrollIntoView(top),滚动到元素
ele
可见,参数top为bool,true(元素显示在顶部)、false(底部)
?禁止滚动:设置css属性overflow
:document.body.style.overflow = "hidden"
,可用于元素、document。
?坐标:clientX/clientY
为相对于窗口的坐标,就是能看到的位置。pageX/pageY
为相对于文档的坐标,包括滚动隐藏的部分。
?元素坐标:
- 相对于窗口的坐标:elem.getBoundingClientRect(),返回一个 DOMRect 对象,包含了坐标位置宽(x、y、widht、height、top、left)等属性。
- 相对于文档的坐标:elem.getBoundingClientRect() 加上当前页面滚动
window.scrollX/scrollY
- 获取指定坐标的元素:document.elementFromPoint(x, y),获取坐标 (x, y) 处嵌套最多的元素。
const cele= document.elementFromPoint(window.innerWidth/2,window.innerHeight/2); | |
cele.style.backgroundColor='red'; | |
cele.scrollIntoView(true); | |
const rect = sdiv.getBoundingClientRect(); | |
console.log(rect.x, rect.y, rect.left, rect.top); //13 260.875 13 260.875 |