网页上的“DOM”到底是个啥玩意儿?每次你听到前端开发者聊起它,是不是感觉云里雾里?其实这东西没那么玄乎。简单说,DOM就是浏览器给你的一份网页“活地图”,让你能用代码去操作网页上的所有东西。
想象一下你拿到一份建筑图纸,这份图纸就是HTML文件。它规定了哪里是客厅,哪里是卧室,有几扇窗户。但是,一旦房子建好了,你看到的就不再是图纸,而是一个实实在在的房子。你可以走进客厅,换掉一盏灯,或者给墙刷个新颜色。
DOM(Document Object Model,文档对象模型)就是这座“建好的房子”。浏览器读取了你的HTML图纸后,会在内存里搭建出这个房子的模型。这个模型里的所有东西——客厅、卧室、窗户、灯——都变成了可以独立操作的对象。所以,你才能用JavaScript这把“万能钥匙”,去开任何一扇门,换任何一盏灯。
HTML是“死”的,DOM是“活”的
这一点必须搞清楚。你写的那个.html文件,一旦加载到浏览器里,它的使命基本就完成了。它就是一份静态的说明书。浏览器会根据这份说明书创建DOM。之后,你用JavaScript去改动网页,比如让一个按钮点击后变色,或者让一个列表增加一个新项目,你改的都不是那个原始的HTML文件,而是这个活生生的DOM模型。
举个例子,假设你的HTML里有这么一行:
<p id="intro">大家好</p>
浏览器读到这行代码,就会在DOM里创建一个p(段落)对象。这个对象有个属性叫id,值是intro,里面还有个文本对象,内容是“大家好”。
现在,你想用JavaScript把它改成“大家好,我是小明”。你会这么写:
let p = document.getElementById('intro');
p.textContent = '大家好,我是小明';
你看,你先用document.getElementById('intro')这个方法,通过ID找到了那个段落对象。这就像你在房子里喊了一声:“那个叫‘intro’的房间在哪里?”浏览器(管家)就帮你找到了。然后,你通过修改它的textContent属性,把里面的文字换掉了。这个改动会立刻反映在网页上,用户马上就能看到新的文字。但是,你去看原始的HTML文件,它里面写的还是“大家好”。这就是“活”与“死”的区别。DOM是动态的,是内存里的东西,而HTML文件只是一个初始蓝图。
DOM其实是一棵“家族树”
为什么叫“文档对象模型”?因为整个网页都被看作一个“文档”(Document),里面的所有东西都被抽象成了“对象”(Object),而这些对象之间的关系,就像一棵树一样,有父有子,有兄弟姐妹,这就是“模型”(Model)。
这棵树的树根,就是document对象,它是整个网页的顶层入口。往下,就是<html>标签,它是document的第一个孩子。<html>标签又有两个孩子:<head>和<body>。它们是兄弟关系。然后<body>里面可能又有<div>、<p>、<ul>这些孩子。<ul>里面又有<li>……一层一层往下,构成了一棵巨大的树形结构。
这个树状结构非常重要,因为它定义了网页上所有元素的位置和关系。比如,你想找到某个<li>标签,你就可以从它的父节点<ul>开始找。或者你想删除一个<div>,你必须先找到它的父节点,然后告诉父节点:“把你这个叫<div>的孩子扔掉”。
我们再看一个具体的例子:
“`html
一个标题
这是一个段落。
- 项目一
- 项目二
“`
这棵DOM树长什么样?
document<html><head><title>- (文本: “我的网页”)
<body><h1>- (文本: “一个标题”)
<p>- (文本: “这是一个段落。”)
<ul><li>- (文本: “项目一”)
<li>- (文本: “项目二”)
你看,所有东西,包括标签(比如<body>、<p>)、属性(比如<p id="intro">里的id)、甚至标签里的文字,在DOM树里都是一个独立的节点(Node)。搞懂了这个树形结构,你就能理解为什么可以用parentElement(父元素)、children(子元素集合)、nextElementSibling(下一个兄弟元素)这类方法在DOM中自由穿梭了。
怎么用JavaScript操作DOM?
既然DOM是活地图,那JavaScript就是操作这张地图的工具。操作DOM基本上就分四种:增、删、改、查。
1. 查(找到你要操作的元素)
这是所有操作的第一步。你得先告诉浏览器你要动哪个东西。方法有很多,最常用的就是:
document.getElementById('id名'): 通过ID查找,最快最准,因为ID在整个页面里是唯一的。document.getElementsByClassName('class名'): 通过类名查找,会返回一个包含所有符合条件的元素的集合(像一个数组)。document.getElementsByTagName('标签名'): 通过标签名查找,比如找出所有的<p>标签。document.querySelector('CSS选择器'): 这是个更现代的方法,你可以用任何CSS选择器来查找,比如#intro、.my-class、div p等等。它只返回找到的第一个元素。document.querySelectorAll('CSS选择器'): 和上面那个一样,但它会返回所有符合条件的元素集合。
我个人最常用querySelector和querySelectorAll,因为CSS选择器实在太方便了,基本能满足所有查找需求。
2. 改(修改找到的元素)
找到了元素,你就可以对它为所欲为了。比如:
- 改内容:
element.textContent = '新文字': 只改文字内容,会忽略HTML标签。-
element.innerHTML = '<h1>新标题</h1>': 直接把里面的HTML结构都换掉。用这个要小心,因为它可能导致安全问题(比如别人注入恶意脚本),但有时候确实很方便。 -
改样式:
-
element.style.color = 'red': 直接修改CSS样式。注意,这里属性名要用驼峰命名法,比如background-color要写成backgroundColor。 -
改属性:
element.setAttribute('class', 'new-class'): 设置或修改元素的属性。element.getAttribute('class'): 获取属性的值。element.removeAttribute('class'): 删除属性。
3. 增(创建并添加新元素)
有时候你需要动态地在页面上加点新东西,比如用户发了一条评论,你得把这条评论显示出来。
步骤一般是三步:
1. 创建新元素: let newDiv = document.createElement('div');
2. 设置内容和属性: newDiv.textContent = '我是新来的';
3. 把它插到指定位置: 找到一个父元素,然后用parentElement.appendChild(newDiv);把它加进去。appendChild会把新元素作为最后一个孩子添加。
4. 删(删除一个元素)
这个也简单,先找到要删除的元素,然后用element.remove()直接干掉它。
或者,也可以用老办法:parentElement.removeChild(element);,就是找到它的爸爸,让爸爸把它删了。
为什么懂DOM很重要?
你可能会说,现在都有React、Vue这些框架了,它们都帮我们处理好DOM操作了,我干嘛还要学这个底层的东西?
问得好。但是,这就是区别一个“会用工具的”和“懂原理的”开发者的关键。
首先,所有前端框架,不管它包装得多么华丽,其底层原理最终还是要回归到对DOM的操作上。React的虚拟DOM(Virtual DOM)做得再快,最后也得计算出最小的差异,然后去更新真实的DOM。你如果不懂DOM,那你对这些框架的理解就永远停留在表面。当遇到性能问题或者一些奇怪的bug时,你就不知道从何下手。
其次,理解DOM操作的成本,能让你写出性能更好的代码。频繁地、大量地直接操作DOM,会让浏览器不停地“重排”(Reflow)和“重绘”(Repaint),这非常消耗性能,会让页面卡顿。比如,你要往一个列表里加100个新项目,如果你用一个循环,appendChild一百次,那浏览器可能就会忙活一百次。但如果你先把这100个项目在一个“文档片段”(DocumentFragment)里创建好,然后一次性把这个片段appendChild到列表里,浏览器就只需要忙活一次。性能差距巨大。这就是懂与不懂的区别。
最后,很多时候你可能只是写一个简单的网页或功能,根本用不上React或Vue这种重型框架。这时候,原生JavaScript操作DOM的技能就直接派上用场了。它让你有能力在不依赖任何框架的情况下,实现丰富的页面交互。
所以,把DOM搞明白,不是为了“茴香豆”的“茴”有几种写法,而是为了让你在前端开发的路上走得更稳、更远。它就是地基。地基不牢,上层建筑再漂亮,也只是空中楼阁。

技能提升网