第 27 章 DOM 操作
第 27 章 DOM 操作
JavaScript 操控网页的核心技能——增删改查!
27.1 创建节点
createElement:创建元素
1
2
3
4
5
6
7
8
| // 创建元素节点
const div = document.createElement('div');
div.className = 'container';
div.id = 'my-div';
div.textContent = '这是一个 div';
console.log('创建的 div:', div.outerHTML);
// <div id="my-div" class="container">这是一个 div</div>
|
1
2
3
4
5
6
7
8
9
| // 创建带属性的元素
const link = document.createElement('a');
link.href = 'https://example.com';
link.target = '_blank';
link.className = 'link';
link.textContent = '访问 Example';
console.log('创建的链接:', link.outerHTML);
// <a href="https://example.com" target="_blank" class="link">访问 Example</a>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 创建嵌套结构
const article = document.createElement('article');
article.className = 'blog-post';
const title = document.createElement('h2');
title.textContent = '我的博客文章';
const content = document.createElement('p');
content.textContent = '这是一段示例内容。';
article.appendChild(title);
article.appendChild(content);
console.log('完整结构:', article.outerHTML);
|
createTextNode:创建文本节点
1
2
3
| // 创建纯文本节点(不会被解析为 HTML)
const text = document.createTextNode('<strong>粗体</strong>');
console.log('文本节点:', text.textContent); // <strong>粗体</strong>(HTML 被转义了)
|
1
2
3
4
5
6
7
8
9
10
| // 与 innerHTML 的区别
const div1 = document.createElement('div');
div1.appendChild(document.createTextNode('<strong>粗体</strong>'));
console.log('createTextNode:', div1.innerHTML);
// <strong>粗体</strong>(HTML 被转义了)
const div2 = document.createElement('div');
div2.innerHTML = '<strong>粗体</strong>';
console.log('innerHTML:', div2.innerHTML);
// <strong>粗体</strong>(HTML 被解析了)
|
createDocumentFragment:文档片段(减少回流)
DocumentFragment 是一个轻量级的文档容器,添加到这个容器中的元素不会直接添加到 DOM,直到你把它添加到 DOM 中。这样可以减少回流次数。
1
2
3
4
5
6
7
| // 传统方法:每次 appendChild 都可能触发回流
const container = document.getElementById('container');
for (let i = 0; i < 100; i++) {
const item = document.createElement('div');
item.textContent = `项 ${i}`;
container.appendChild(item); // 触发 100 次回流!
}
|
1
2
3
4
5
6
7
8
9
10
11
| // 使用 DocumentFragment:只触发一次回流
const container = document.getElementById('container');
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const item = document.createElement('div');
item.textContent = `项 ${i}`;
fragment.appendChild(item); // 添加到 fragment,不会触发回流
}
container.appendChild(fragment); // 一次性添加到 DOM,只触发一次回流
|
1
2
3
4
5
6
7
8
9
10
11
| // DocumentFragment 的特点
const fragment = document.createDocumentFragment();
// fragment 有父节点,但不在 DOM 树中
console.log('fragment 的父节点:', fragment.parentNode); // null
// 添加到 DOM 后,fragment 变为空
fragment.appendChild(document.createElement('div'));
console.log('添加后长度:', fragment.childNodes.length); // 1
container.appendChild(fragment);
console.log('添加到 DOM 后长度:', fragment.childNodes.length); // 0
|
💡 本章小结(第27章第1节)
创建节点有三种方法:createElement 创建元素节点,createTextNode 创建纯文本节点(HTML 不会被解析),createDocumentFragment 创建文档片段(用于减少回流)。使用 DocumentFragment 可以在添加到 DOM 之前先组装好节点,然后一次性添加,只触发一次回流。
27.2 插入与替换
appendChild / insertBefore
1
2
3
4
5
6
7
8
9
10
11
12
13
| // appendChild:将节点添加到父元素的末尾
const parent = document.createElement('div');
const child = document.createElement('p');
child.textContent = '新段落';
parent.appendChild(child);
console.log('添加后:', parent.innerHTML);
// <p>新段落</p>
// 如果添加的是已存在的节点,会移动它(不是复制)
const container = document.getElementById('container');
const existingP = container.querySelector('p');
container.appendChild(existingP); // 把已存在的 p 移到末尾
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // insertBefore:在指定节点前插入
const parent = document.createElement('ul');
const li1 = document.createElement('li');
li1.textContent = '第一项';
const li2 = document.createElement('li');
li2.textContent = '第二项';
parent.appendChild(li1);
parent.appendChild(li2);
// 在 li1 前插入新项
const li0 = document.createElement('li');
li0.textContent = '第零项';
parent.insertBefore(li0, li1);
console.log('insertBefore 结果:');
// <li>第零项</li>
// <li>第一项</li>
// <li>第二项</li>
|
1
2
| // insertBefore 的第二个参数为 null 时,等同于 appendChild
parent.insertBefore(newItem, null); // 等同于 parent.appendChild(newItem)
|
append / prepend / after / before(ES2017+)
ES2017 引入了更直观的新方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // append:在末尾插入(可一次插入多个)
const list = document.createElement('ul');
const li1 = document.createElement('li');
li1.textContent = '第一项';
const li2 = document.createElement('li');
li2.textContent = '第二项';
list.append(li1, li2);
console.log('append 结果:', list.innerHTML);
// <ul>
// <li>第一项</li>
// <li>第二项</li>
// </ul>
// 也可以插入文本节点
list.append('(这是文本)');
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // prepend:在开头插入
const header = document.createElement('header');
const h1 = document.createElement('h1');
h1.textContent = '标题';
header.append(h1);
const nav = document.createElement('nav');
nav.textContent = '导航';
header.prepend(nav);
console.log('prepend 结果:', header.innerHTML);
// <header>
// <nav>导航</nav>
// <h1>标题</h1>
// </header>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // after:在元素之后插入
const container = document.createElement('div');
const p = document.createElement('p');
p.textContent = '这是段落';
container.append(p);
const note = document.createElement('small');
note.textContent = '(小字说明)';
p.after(note);
console.log('after 结果:', container.innerHTML);
// <div>
// <p>这是段落</p>
// <small>(小字说明)</small>
// </div>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // before:在元素之前插入
const section = document.createElement('section');
const h2 = document.createElement('h2');
h2.textContent = '小标题';
section.append(h2);
const hr = document.createElement('hr');
h2.before(hr);
console.log('before 结果:', section.innerHTML);
// <section>
// <hr>
// <h2>小标题</h2>
// </section>
|
replaceWith:替换元素
1
2
3
4
5
6
7
8
9
10
11
12
| // replaceWith:用新元素替换目标元素
const oldDiv = document.createElement('div');
oldDiv.textContent = '旧 div';
const container = document.createElement('div');
container.append(oldDiv);
const newDiv = document.createElement('div');
newDiv.textContent = '新 div';
oldDiv.replaceWith(newDiv);
console.log('替换后:', container.innerHTML);
// <div>新 div</div>
|
1
2
3
4
5
6
7
8
9
| // replaceWith 也可以用字符串
const span = document.createElement('span');
span.textContent = '原来的 span';
const wrapper = document.createElement('div');
wrapper.append(span);
span.replaceWith('<strong>替换后的内容</strong>');
console.log('字符串替换:', wrapper.innerHTML);
// <div><strong>替换后的内容</strong></div>
|
💡 本章小结(第27章第2节)
插入节点有多种方法:appendChild 添加到末尾,insertBefore 在指定节点前插入,append/prepend(ES2017+)更直观地插入到开头或末尾,after/before 在元素之后或之前插入,replaceWith 替换元素。相比 appendChild + insertBefore,新方法更简洁,而且 append/prepend 还可以一次插入多个节点。
27.3 删除与克隆
remove / removeChild:删除元素
1
2
3
4
5
6
| // remove:直接删除元素(现代浏览器支持)
const element = document.getElementById('to-remove');
if (element) {
element.remove();
console.log('元素已删除');
}
|
1
2
3
4
5
6
7
| // removeChild:父节点删除子节点
const parent = document.getElementById('parent');
const child = document.getElementById('child');
// 删除并返回被删除的节点
const removed = parent.removeChild(child);
console.log('被删除的元素:', removed);
|
1
2
3
4
5
6
7
| // removeChild 需要父节点引用
// 如果只有子节点本身,可以使用 parentNode
const child = document.getElementById('child');
child.parentNode.removeChild(child);
// 或者用 remove(更简洁)
child.remove();
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| // 删除所有子节点
const container = document.getElementById('container');
// 方法1:while 循环
while (container.firstChild) {
container.removeChild(container.firstChild);
}
// 方法2:innerHTML
container.innerHTML = '';
// 方法3:replaceChildren(ES2022+)
container.replaceChildren();
|
cloneNode:克隆(true 深克隆 / false 浅克隆)
1
2
3
4
5
6
7
8
9
10
11
12
13
| // cloneNode:克隆节点
const original = document.createElement('div');
original.className = 'container';
original.innerHTML = '<p>段落内容</p>';
// 浅克隆(false):只克隆元素本身,不克隆子元素
const shallowClone = original.cloneNode(false);
console.log('浅克隆:', shallowClone.innerHTML); // 空!子元素没被克隆
// 深克隆(true):克隆元素及其所有后代
const deepClone = original.cloneNode(true);
console.log('深克隆:', deepClone.innerHTML);
// <p>段落内容</p>
|
1
2
3
4
5
6
7
8
9
10
11
12
| // 深克隆会复制所有属性和子节点
const source = document.createElement('article');
source.id = 'article-1';
source.className = 'blog-post';
source.innerHTML = '<h2>标题</h2><p>内容</p><footer>作者</footer>';
const copy = source.cloneNode(true);
document.body.appendChild(copy);
console.log('克隆的 id:', copy.id); // article-1(id 被复制了!)
// 注意:如果 id 在文档中已存在,可能导致 id 冲突
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| // 克隆事件监听器不会被复制!
const button = document.createElement('button');
button.textContent = '点击我';
button.addEventListener('click', () => {
console.log('按钮被点击了!');
});
// 克隆按钮
const clonedButton = button.cloneNode(true);
clonedButton.textContent = '克隆的按钮';
document.body.appendChild(clonedButton);
// 原按钮点击有效,克隆的按钮点击无效(因为事件监听器没被复制)
|
1
2
3
4
5
6
7
8
| // 如果需要克隆事件,可以使用 cloneNode + 重新绑定
function deepCloneWithEvents(element) {
const clone = element.cloneNode(true);
// 重新绑定事件(这里需要你自己实现)
return clone;
}
// 或者使用自定义的 clone 工具函数
|
💡 本章小结(第27章第3节)
删除元素有两种方法:remove(直接删除)和 removeChild(父节点删除子节点,返回被删除的节点)。克隆使用 cloneNode,参数为 true 时深克隆(包含所有子节点),为 false 时浅克隆。注意:cloneNode 不会复制事件监听器!
27.4 属性操作
getAttribute / setAttribute / removeAttribute
1
2
3
4
5
6
7
8
9
10
| // getAttribute:获取属性值
const link = document.createElement('a');
link.href = 'https://example.com';
link.title = '示例链接';
console.log('href:', link.getAttribute('href')); // https://example.com
console.log('title:', link.getAttribute('title')); // 示例链接
// 获取不存在的属性返回 null
console.log('target:', link.getAttribute('target')); // null
|
1
2
3
4
5
6
7
8
| // setAttribute:设置属性
const button = document.createElement('button');
button.setAttribute('type', 'submit');
button.setAttribute('disabled', '');
button.setAttribute('data-id', '123');
console.log('button:', button.outerHTML);
// <button type="submit" disabled data-id="123"></button>
|
1
2
3
4
5
6
7
8
9
10
| // setAttribute vs 直接赋值
const input = document.createElement('input');
// setAttribute:总是设置为字符串
input.setAttribute('value', '123');
console.log('value 类型:', typeof input.value); // string
// 直接赋值:可以是任意类型
input.value = 123;
console.log('value 类型:', typeof input.value); // number
|
1
2
3
4
5
6
7
8
| // removeAttribute:移除属性
const div = document.createElement('div');
div.id = 'my-div';
div.className = 'container active';
div.removeAttribute('id');
console.log('移除 id 后:', div.outerHTML);
// <div class="container active"></div>
|
直接属性访问:element.id / element.src 等(href 自动转绝对路径)
1
2
3
4
5
6
7
8
| // 直接访问常见属性
const link = document.createElement('a');
link.href = '/path/to/page'; // 设置相对路径
console.log('直接访问 href:', link.href); // 自动转换为绝对路径
// file:///path/to/page 或 https://example.com/path/to/page
console.log('getAttribute 获取的是原始值:', link.getAttribute('href')); // /path/to/page
|
1
2
3
4
5
6
7
8
9
| // 常见属性访问
const img = document.createElement('img');
img.src = 'image.png';
img.alt = '图片描述';
img.className = 'thumbnail';
img.id = 'main-image';
console.log('img.src:', img.src); // 绝对路径
console.log('img.alt:', img.alt); // 图片描述
|
1
2
3
4
| // 属性名映射
// class → className
// for → htmlFor
// maxlength → maxLength
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| // checked / selected / disabled 等布尔属性
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = true;
console.log('checked:', checkbox.checked); // true
// setAttribute 设置布尔属性(值必须是空字符串或属性名)
checkbox.setAttribute('checked', '');
checkbox.removeAttribute('checked');
// 直接赋值更简洁
checkbox.checked = false;
|
data-* 自定义属性:dataset
1
2
3
4
5
6
7
8
9
10
11
12
| // data-* 属性:存储自定义数据
const card = document.createElement('div');
card.className = 'card';
card.dataset.userId = '12345';
card.dataset.username = '张三';
card.dataset['product-id'] = 'PRO-001'; // 转为 data-product-id
console.log('dataset:', card.dataset);
// DOMStringMap { userId: '12345', username: '张三', productId: 'PRO-001' }
console.log('userId:', card.dataset.userId); // 12345
console.log('productId:', card.dataset.productId); // PRO-001
|
1
2
3
4
| // dataset 的命名规则
// data-user-name → dataset.userName
// data-userid → dataset.userid
// 短横线转为驼峰
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 实际应用:事件委托中使用 data 属性
// HTML
// <div id="product-list">
// <button data-action="add" data-product-id="1">添加</button>
// <button data-action="delete" data-product-id="1">删除</button>
// </div>
document.getElementById('product-list').addEventListener('click', (e) => {
const button = e.target.closest('button');
if (!button) return;
const { action, productId } = button.dataset;
console.log(`动作: ${action}, 产品ID: ${productId}`);
});
|
💡 本章小结(第27章第4节)
属性操作有三种方法:getAttribute/setAttribute/removeAttribute,以及直接属性访问。直接访问属性(如 element.href)时,href 会自动转换为绝对路径。data-* 自定义属性可以通过 element.dataset 对象来访问和修改,属性名会自动转为驼峰格式。
27.5 类名与样式
className / classList:add / remove / toggle / contains / replace
1
2
3
4
5
6
7
| // className:获取或设置 class 属性(字符串)
const div = document.createElement('div');
div.className = 'container';
console.log('className:', div.className); // container
div.className = 'wrapper active';
console.log('className:', div.className); // wrapper active
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| // classList:更方便地操作 class
const button = document.createElement('button');
button.classList.add('btn', 'btn-primary');
console.log('classList:', button.classList); // ['btn', 'btn-primary']
// contains:检查是否有某个 class
console.log('有 btn-primary?', button.classList.contains('btn-primary')); // true
console.log('有 btn-danger?', button.classList.contains('btn-danger')); // false
// add:添加 class
button.classList.add('disabled');
console.log('add 后:', button.classList.value); // btn btn-primary disabled
// remove:移除 class
button.classList.remove('disabled');
console.log('remove 后:', button.classList.value); // btn btn-primary
// toggle:切换 class(没有就添加,有就移除)
button.classList.toggle('active');
console.log('toggle 1:', button.classList.value); // btn btn-primary active
button.classList.toggle('active');
console.log('toggle 2:', button.classList.value); // btn btn-primary
|
1
2
3
4
5
6
7
8
9
| // classList.toggle 的第二个参数(ES2021+)
// 第二个参数决定是添加还是移除,而不是根据当前状态
button.classList.toggle('disabled', true); // 强制添加
button.classList.toggle('disabled', false); // 强制移除
// 实际应用:根据条件设置 class
button.classList.toggle('loading', isLoading);
button.classList.toggle('error', hasError);
|
1
2
3
4
5
| // replace:替换 class
const header = document.createElement('header');
header.className = 'header old-style';
header.classList.replace('old-style', 'new-style');
console.log('replace 后:', header.className); // header new-style
|
element.style:行内样式
1
2
3
4
5
6
7
8
| // style:获取或设置行内样式
const div = document.createElement('div');
div.style.color = 'red';
div.style.fontSize = '16px';
div.style.backgroundColor = '#f0f0f0';
console.log('行内样式:', div.getAttribute('style'));
// color: red; font-size: 16px; background-color: #f0f0f0;
|
1
2
3
4
| // CSS 属性名转为驼峰格式
// font-size → fontSize
// background-color → backgroundColor
// border-left-width → borderLeftWidth
|
1
2
3
4
5
6
7
| // style 的读写
// 读取时获取的是行内样式的值(即使 CSS 中有更高优先级的样式)
div.style.color = 'blue';
// 如果想获取计算后的完整样式,使用 getComputedStyle
const computed = window.getComputedStyle(div);
console.log('计算后的 color:', computed.color);
|
1
2
3
4
5
6
7
8
| // 批量设置样式
const box = document.createElement('div');
Object.assign(box.style, {
width: '100px',
height: '100px',
backgroundColor: 'blue',
borderRadius: '10px'
});
|
getComputedStyle:计算后的完整样式
1
2
3
4
5
6
7
8
9
| // getComputedStyle:获取元素计算后的完整样式
const div = document.createElement('div');
div.style.width = '50%';
div.style.color = 'red';
const computed = window.getComputedStyle(div);
console.log('计算后 width:', computed.width); // 可能是 400px(根据父元素宽度)
console.log('计算后 color:', computed.color); // rgb(255, 0, 0)
console.log('计算后 fontSize:', computed.fontSize); // 可能有默认值
|
1
2
3
4
5
6
| // getComputedStyle 是只读的
// 第一个参数是元素
// 第二个参数是伪元素(如 ::before, ::after)
const pseudoElement = document.querySelector('.tooltip::before');
const styles = window.getComputedStyle(pseudoElement, '::before');
console.log('content:', styles.content);
|
💡 本章小结(第27章第5节)
类名操作推荐使用 classList,它提供了 add、remove、toggle、contains、replace 等方法,比直接操作 className 字符串更安全。element.style 用于操作行内样式,CSS 属性名要转为驼峰格式。getComputedStyle 可以获取元素计算后的完整样式(包括 CSS 样式表中的样式),但它是只读的。
27.6 尺寸与位置
offsetWidth / offsetHeight:border + padding + content
1
2
3
4
5
6
| // offsetWidth / offsetHeight:元素的总尺寸
// = border + padding + content
// <div style="width: 100px; padding: 10px; border: 5px solid black;">
// offsetWidth = 100 + 20 + 10 = 130
// offsetHeight 同理
|
clientWidth / clientHeight:padding + content(不含 border)
1
2
| // clientWidth / clientHeight:内部尺寸
// = padding + content(不含 border 和滚动条)
|
1
2
3
| // scrollWidth / scrollHeight:滚动内容的总尺寸
// 如果没有滚动条,等于 clientWidth / clientHeight
// 如果内容溢出,包含隐藏部分的尺寸
|
offsetParent / offsetTop / offsetLeft:相对于 offsetParent 的偏移
1
2
3
4
| // offsetParent:最近的定位祖先元素
// 如果没有定位祖先,是 <body>
// offsetTop / offsetLeft:元素边框外侧到 offsetParent 边框内侧的距离
|
getBoundingClientRect:元素在视口中的位置和尺寸
1
2
3
4
5
6
7
8
9
10
11
| // getBoundingClientRect:获取元素相对于视口的位置和尺寸
const rect = element.getBoundingClientRect();
console.log('left:', rect.left); // 元素左边到视口左边的距离
console.log('top:', rect.top); // 元素上边到视口上边的距离
console.log('right:', rect.right); // 元素右边到视口左边的距离
console.log('bottom:', rect.bottom); // 元素下边到视口上边的距离
console.log('width:', rect.width); // 元素宽度
console.log('height:', rect.height); // 元素高度
console.log('x:', rect.x); // x 坐标
console.log('y:', rect.y); // y 坐标
|
1
2
3
4
5
6
7
8
9
10
| // 判断元素是否在视口内
function isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // scrollTop / scrollLeft:元素已滚动的距离
const container = document.getElementById('scroll-container');
// 读取滚动位置
console.log('scrollTop:', container.scrollTop);
console.log('scrollLeft:', container.scrollLeft);
// 设置滚动位置
container.scrollTop = 100;
container.scrollLeft = 50;
// 滚动到顶部
container.scrollTop = 0;
// 滚动到底部
container.scrollTop = container.scrollHeight;
|
1
2
3
| // scrollTo:滚动到指定位置
window.scrollTo(0, 100); // 滚动到 y=100 的位置
window.scrollTo({ top: 100, left: 0, behavior: 'smooth' }); // 平滑滚动
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // scrollIntoView:滚动使元素可见
const element = document.getElementById('target');
// 默认滚动
element.scrollIntoView();
// 滚动到顶部
element.scrollIntoView(true);
element.scrollIntoView({ block: 'start' });
// 滚动到底部
element.scrollIntoView(false);
element.scrollIntoView({ block: 'end' });
// 平滑滚动
element.scrollIntoView({ behavior: 'smooth' });
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
| 属性 | content | padding | border | scrollbar | 说明 |
|---|
| offsetWidth | ✓ | ✓ | ✓ | ✓ | border + padding + content + 滚动条 |
| clientWidth | ✓ | ✓ | ✗ | ✓ | padding + content |
| scrollWidth | ✓ | ✓ | ✗ | ✗ | 滚动内容的总宽度(可能超出可视区) |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 一图流
// +-----------------+
// | border |
// | +-------------+ |
// | | padding | |
// | | +---------+ | |
// | | | content | | |
// | | +---------+ | |
// | +-------------+ |
// +-----------------+
//
// offsetWidth = border + padding + content + scrollbar
// clientWidth = padding + content(不含 border 和 scrollbar)
// scrollWidth = 内容的总宽度(可能超出可视区)
|
graph TD
A["尺寸属性对比"] --> B["offsetWidth"]
A --> C["clientWidth"]
A --> D["scrollWidth"]
B --> B1["border ✓"]
B --> B2["padding ✓"]
B --> B3["content ✓"]
B --> B4["scrollbar ✓"]
C --> C1["border ✗"]
C --> C2["padding ✓"]
C --> C3["content ✓"]
C --> C4["scrollbar ✓"]
D --> D1["border ✗"]
D --> D2["padding ✓"]
D --> D3["content ✓"]
D --> D4["滚动超出内容 ✓"]💡 本章小结(第27章第6节)
DOM 提供了多种尺寸和位置 API:offsetWidth/offsetHeight 是 border + padding + content 的总尺寸;clientWidth/clientHeight 是 padding + content(不含 border);scrollWidth/scrollHeight 是滚动内容的总尺寸;getBoundingClientRect 获取元素相对于视口的位置和尺寸;scrollTop/scrollLeft 是已滚动的距离;scrollTo/scrollIntoView 用于控制滚动。了解这些 API 的区别,有助于正确获取元素尺寸和实现滚动效果。
本章小结(第27章)
1. 创建节点
createElement:创建元素节点createTextNode:创建纯文本节点createDocumentFragment:创建文档片段,减少回流
2. 插入与替换
appendChild/insertBefore:传统方法append/prepend/after/before:ES2017+ 更简洁replaceWith:替换元素
3. 删除与克隆
remove:直接删除元素removeChild:父节点删除子节点cloneNode(true/false):深克隆/浅克隆
4. 属性操作
getAttribute/setAttribute/removeAttribute- 直接属性访问:
element.href(href 会转绝对路径) dataset:访问 data-* 自定义属性
5. 类名与样式
classList:add/remove/toggle/contains/replaceelement.style:行内样式(驼峰命名)getComputedStyle:获取计算后的完整样式
6. 尺寸与位置
offsetWidth/offsetHeight:border + padding + contentclientWidth/clientHeight:padding + contentscrollWidth/scrollHeight:滚动内容总尺寸getBoundingClientRect:相对于视口的位置scrollTop/scrollLeft:滚动位置scrollTo/scrollIntoView:滚动控制
记忆口诀
DOM 操作全搞定,
创建用 create,
插入用 append,
删除用 remove,
属性用 set/get,
类名用 classList,
样式用 style,
尺寸用 offset/client/scroll,
位置用 getBoundingClientRect!