DOM基础
DOM概述
DOM(文档对象模型)是一个网络文档的编程接口,DOM将文档表示为节点和对象,因此编程语言可以和页面交互。
网页是既可以在浏览器窗口中显示,又可以作为HTML源代码的文档,用DOM使它可以被操作。
const paragraphs = document.querySelectorAll("p");
//paragraphs[0], paragraphs[1]...
alert(paragraphs[0].nodeName);
DOM是使用多个API构建的,核心DOM定义了描述任何文档和其中对象的实体,其他API对其进行扩展。
DOM不是一门编程语言,而是用于建立网站的API。所以DOM不一定非要JavaScript才能实现,Python也行。
访问DOM
可以使用document
或window
对象来操作文档本身
//创造一个新的h1元素,向元素添加文本,再将其添加到文档的树中
<html lang="zh-CN">
<head>
<script>
// 当文档加载时,运行这段函数
window.onload = () => {
// 在空 HTML 页面中创建一系列元素
const heading = document.createElement("h1");
const headingText = document.createTextNode("Big Head!");
heading.appendChild(headingText);
document.body.appendChild(heading);
};
</script>
</head>
<body></body>
</html>
基本的数据类型
通常把DOM中的节点成为元素,但严格来说不是每个节点都是一个元素。
数据类型 | 描述 |
---|---|
Document | 当一个成员返回 document 对象时,这个对象就是rootdocument 对象本身。参见文章 DOM document 参考 |
Node | 位于文档中的每个对象都是某种类型的节点。一个对象可以是一个元素节点,也可以是一个文本节点或属性节点 |
Element | 基于Node,指的是一个元素或由DOM API成员返回的element类型的节点 |
NodeList | 由元素组成的数组 |
Attr | attribute 通过成员函数返回时,是一个为属性暴露出专门接口的对象引用 |
NamedNodeMap | 和数组类似,但是条目是由名称或索引访问的 |
DOM中的核心接口
document.querySelector()
document.querySelectorAll()
document.createElement()
Element.innerHTML
Element.setAttribute()
Element.getAttribute()
EventTarget.addEventListener()
HTMLElement.style
Node.appendChild()
window.onload
window.scrollTo()
示例
设置文本内容
<div class="container">
<textarea class="story"></textarea>
<button id="set-text" type="button">设置文本内容</button>
<button id="clear-text" type="button">清除文本内容</button>
</div>
.container {
display: flex;
gap: 0.5rem;
flex-direction: column;
}
button {
width: 200px;
}
const story = document.body.querySelector(".story");
const setText = document.body.querySelector("#set-text");
setText.addEventListener("click", () => {
story.textContent = "It was a dark and stormy night...";
});
const clearText = document.body.querySelector("#clear-text");
clearText.addEventListener("click", () => {
story.textContent = "";
});
添加子元素
<div class="container">
<div class="parent">父元素</div>
<button id="add-child" type="button">添加子元素</button>
<button id="remove-child" type="button">移除子元素</button>
</div>
.container {
display: flex;
gap: 0.5rem;
flex-direction: column;
}
button {
width: 100px;
}
div.parent {
border: 1px solid black;
padding: 5px;
width: 100px;
height: 100px;
}
div.child {
border: 1px solid red;
margin: 10px;
padding: 5px;
width: 80px;
height: 60px;
box-sizing: border-box;
}
const parent = document.body.querySelector(".parent");
const addChild = document.body.querySelector("#add-child");
addChild.addEventListener("click", () => {
// 只在文本节点“父节点”没有子节点时添加一个子节点
if (parent.childNodes.length > 1) {
return;
}
const child = document.createElement("div");
child.classList.add("child");
child.textContent = "子节点";
parent.appendChild(child);
});
const removeChild = document.body.querySelector("#remove-child");
removeChild.addEventListener("click", () => {
const child = document.body.querySelector(".child");
parent.removeChild(child);
});
实例方法
document.querySelector
概要
返回文档中与指定选择器或选择器组匹配的第一个Element对象,找不到返回null。
匹配:使用深度优先先序遍历,从文档第一个元素开始按子节点的顺序遍历。
语法
element = document.querySelector(selectors);
/*
selectors: 包含一个或多个要匹配的选择器的DOM字符串,必须为CSS选择器,否则引发SYNTAX_ERR异常。
SYNTAX_ERR:指定selectors的语法无效
CSS伪类不会返回任何元素
*/
示例
//查找第一个匹配.myclass属性的元素
let e1 = document.querySelector(".myclass");
//查找一个元素,该元素是被一个class = "user-panel main"属性的div包裹的name = "login"的input
let e2 = document.querySelector("div.user-panel.main input[name = 'login']");
document.querySelectorAll()
概要
此方法基于ParentNode mixin的querySelectorAll()实现。
返回与指定的选择器组匹配的文档中的元素列表,返回NodeList
(可以为空)。
一旦返回了NodeList,就可以像数组一样使用。
匹配:深度优先先序遍历文档节点。
语法
querySelectorAll(selectors)
/*
selectors: 必须为CSS选择器的字符串,否则引发SyntaxError异常。
selectors中包含CSS伪元素会始终返回空值。
SyntaxError:指定的选择器不合法会抛出该错误。
*/
示例
//获取所有p元素
let e = document.querySelectorAll("p");
//获取所有class为note和alert的div元素
let e1 = document.querySelectorAll("div.note", "div.alert");
//获得一个p元素的列表,直接父元素是一个class = "highlighted"的div,位于ID为"test"的容器中
let container = document.querySelector("#test");
let matches = container.querySelectorAll("div.highlighted > p");
//获得文档中属性名为“data-src”的iframe元素列表
let matches = document.querySelectorAll("iframe[data-stc]");
//获得ID为userlist的列表中包含值为1的data-active属性的元素
let container = document.querySelector("#userlist");
let matcher = container.querySelector("li[data-active = '1']");
document.createElement
概要
用于创建一个由标签名称tagName指定的HTML元素,如果无法识别该tagName会生成一个未知HTML元素HTMLUnknownElement
。
会返回新建的元素。
语法
var element = document.createElement(tagName[, options]);
/*
tagName:指定要创建元素类型的字符串
*/
示例
//新建一个新的div并且插入到ID为div1的元素前
document.body.onload = addElement;
function addElement(){
//创造一个新的div元素
let newDiv = document.createElement("div");
//添加内容
let newtext = document.createTextNode("Hello World!");
//添加文本节点到这个div元素
newDiv.appendChlid(newtext);
//获取div1
let curDiv = document.getElementById("div1");
//添加到div1的元素前
document.body.insertBefore(newDiv, curDiv);
}
element.innerHTML
概要
设置或获取HTML语法表示的元素的后代。
语法
const content = element.innerHTML;
element.innerHTML = htmlString;
/*
设置元素的innerHTML将会删除所有该元素的后代并以htmlString替代。
SyntaxError:HTML没有正确标记时,设置innerHTML将抛出语法错误。
NoModificationAllowedError:当父元素是Document时设置innerHTML将抛出语法错误。
*/
示例
<div class="box">
<div><strong>Log:</strong></div>
<div class="log"></div>
</div>
.box{
width: 600px;
height: 300px;
border: 1px solid black;
padding: 2px 4px;
overflow-y: scroll;
overflow-x: auto;
}
.log{
margin-top: 8px;
font-family: monospace;
}
let s = document.querySelector(".box");
s.addEventListener("click", logEvent);
s.addEventListener("mousedown", logEvent); s.addEventListener("mouseup", logEvent);
s.addEventListener("mouseenter", logEvent);
s.addEventListener("mouseleave", logEvent);
function logEvent(){
let msg = "Event <strong>" + event.type + "</strong> at <em>" + event.clientX + "," + event.clientY + "</em>";
log(msg);
}
function log(msg){
let a = document.querySelector(".log");
let time = new Date();
let timestr = time.toLocaleTimeString();
a.innerHTML += timestr + ':' + msg + "<br />";
}
log("Logging mouse events inside this container...");
element.setAttribute
概要
设置指定元素上的某个属性值,若该属性存在则更新该值,否则使用指定的名称和值添加一个新的属性,返回undefined。
语法
element.setAttribute(name, value)
/*
name:表示属性名称的字符串
value:属性的值
*/
示例
//为HTML的button设置属性
let b = document.querySelector("button");
b.setAttribute("name", "helloButton");
b.setAttribute("disabled", "");
element.getAttribute
概要
返回元素上一个指定的属性值,若指定的属性不存在,返回null
或""
。
语法
let attribute = element.getAttribute(attributeName);
/*
attributeName:你想获取属性值的属性名称。
*/
示例
let div1 = document.getElementById("div1");
let align = div1.getAttribute("align");
alert(align);
EventTarget.addEventListener
概要
将指定的监听器注册到EventTarget
上,该对象触发指定的事件时,相应的回调函数会被执行,无返回值。
EventTarget
可以是Element
、Document
、Window
和任何支持事件的对象。
使用
addEventListener()
的优点:
- 它允许为一个事件添加多个监听器。特别是对库、JavaScript 模块和其他需要兼容第三方库/插件的代码来说,这一功能很有用。
- 相比于
onXYZ
属性绑定来说,它提供了一种更精细的手段来控制listener
的触发阶段。(即可以选择捕获或者冒泡)。- 它对任何事件都有效,而不仅仅是 HTML 或 SVG 元素。
原理
将EventListener
的函数或对象添加到调用它的EventTarget
的事件侦听器列表中,若已被添加则不会重复该操作。
当一个EventListener
在处理事件时被注册到EventTarget
上时,不会被立即触发。
如果先前向事件侦听器列表中添加过一个匿名函数,并且在之后的代码中调用
addEventListener
来添加一个功能完全相同的匿名函数,那么之后的这个匿名函数也会被添加到列表中。 实际上,即使使用完全相同的代码来定义一个匿名函数,这两个函数仍然存在区别,在循环中也是如此。在使用该方法的情况下,匿名函数的重复定义会带来许多麻烦。
语法
addEventLister(type, listener, [options], [useCapture]);
/*
type:监听类型
listener:一个实现了EventListener接口的对象,或函数
options:capture(事件捕获), once(最多只调用一次),passive(不会调用preventDefault), signal(abort()方法被调用时,监听器被移除)
useCapture:是否要先于它下面的EventTarget调用该Listener(不太懂这个...)
*/
示例
<table id="outside">
<tr>
<td id="t1">one</td>
</tr>
<tr>
<td id="t2">two</td>
</tr>
</table>
/*1.添加一个简单的监听器*/
// 为 table 添加事件监听器
const el = document.getElementById("outside");
el.addEventListener("click", modifyText, false);
// 改变 t2 内容的函数
function modifyText() {
const t2 = document.getElementById("t2");
const isNodeThree = t2.firstChild.nodeValue === "three";
t2.firstChild.nodeValue = isNodeThree ? "two" : "three";
}
/*2.添加一个可被移除的监听器*/
// 为 table 添加可被移除的事件监听器
const controller = new AbortController();
const el = document.getElementById("outside");
el.addEventListener("click", modifyText, { signal: controller.signal });
// 改变 t2 内容的函数
function modifyText() {
const t2 = document.getElementById("t2");
if (t2.firstChild.nodeValue === "three") {
t2.firstChild.nodeValue = "two";
} else {
t2.firstChild.nodeValue = "three";
controller.abort(); // 当值变为 "three" 后,移除监听器
}
}
/*3.带有匿名函数的监听器*/
// 改变 t2 内容的函数
function modifyText(new_text) {
const t2 = document.getElementById("t2");
t2.firstChild.nodeValue = new_text;
}
// 用匿名函数为 table 添加事件监听器
const el = document.getElementById("outside");
el.addEventListener(
"click",
function () {
modifyText("four");
},
false,
);
/*4.带有箭头函数的监听器*/
// 改变 t2 内容的函数
function modifyText(new_text) {
var t2 = document.getElementById("t2");
t2.firstChild.nodeValue = new_text;
}
// 用箭头函数为 table 添加事件监听器
const el = document.getElementById("outside");
el.addEventListener(
"click",
() => {
modifyText("four");
},
false,
);
Node.appendChlid
概要
将一个节点附加到指定父节点的子节点列表末尾处。若该节点已经存在,则将其移动到新的位置,返回追加后的子节点。
语法
element.appendChild(aChild)
/*
aChild:要追加给父节点的节点。
*/
示例
//创造一个新的段落元素p,然后将其添加到body的最尾部
let p = document.createElement("p");
document.body.appendChild(p);
window.onload
概要
load
事件在整个页面及所有依赖资源都已完全加载时触发。
语法
addEventListener("load", (event) => {});
onload = (event) => {};
示例
windows.addEventListener("load", (event) =>{
console.log("page is fully loaded");
});
windows.onload = (event) => {
console.log("page is fully loaded");
};
window.scrollTo
概要
滚动到文档中的某个坐标。
语法
window.scrollTo(x-coord, y-coord);
window.scrollTo(options);
/*
x-coord:文档的横轴坐标=left
y-coord:文档的纵轴坐标=top
options:包含三个属性的对象
*/
示例
window.scrollTo(0, 1000);
//设置滚动行为为平滑的滚动
window.scrollTo({
top:1000,
behavior:"smooth"
});