【前端教程】JavaScript DOM 操作案例解析与代码优化
案例1:获取div子节点并批量修改样式与内容
功能解析
该案例实现两个核心功能:获取div下所有子节点并设置红色背景;识别所有p标签并修改其内容为"我爱中国"。
原代码分析
window.onload=function(){// 获取div下所有元素节点var ziJieDianDivShuZu = document.getElementsByTagName("div")[0].children;// 设置所有子节点背景为红色for(var i of ziJieDianDivShuZu){i.style.backgroundColor="red";}// 获取div下所有p标签var jieDianPShuZu=document.getElementsByTagName("div")[0].getElementsByTagName("p");// 修改p标签内容for(var i of jieDianPShuZu){i.innerHTML="我爱中国";}
}
优化建议
-
缓存DOM查询结果:避免重复查询相同元素
// 优化后 var div = document.querySelector("div"); // 使用更简洁的querySelector var children = div.children;
-
合并操作减少遍历:可以在一个循环中完成两种操作
for(var child of children) {child.style.backgroundColor = "red";if(child.tagName === "P") { // 检查是否为p标签child.textContent = "我爱中国"; // 纯文本内容推荐使用textContent} }
-
使用更现代的API:querySelector和querySelectorAll更灵活
// 直接获取所有p标签 var pTags = div.querySelectorAll("p");
案例2:ul下子节点隔行换色效果
功能解析
实现鼠标悬停在ul的li元素上时,奇数项和偶数项显示不同的背景色,移开时恢复默认黄色。
原代码分析
window.onload=function(){var ulFirstNeiRong = document.getElementsByTagName("ul")[0];var jiShuZu = ulFirstNeiRong.getElementsByClassName("jiID");var ouShuZu = ulFirstNeiRong.getElementsByClassName("ouID");// 奇数项事件for(var i of jiShuZu){i.onmouseover=function(){this.style.backgroundColor="red";}i.onmouseout=function(){this.style.backgroundColor="yellow";}}// 偶数项事件for(var i of ouShuZu){i.onmouseover=function(){this.style.backgroundColor="green";}i.onmouseout=function(){this.style.backgroundColor="yellow";}}
}
优化建议
-
移除冗余类名:不需要提前定义奇偶类,可以通过索引判断
var lis = ulFirstNeiRong.querySelectorAll("li"); lis.forEach((li, index) => {// 绑定事件处理函数li.addEventListener("mouseover", function() {this.style.backgroundColor = index % 2 === 0 ? "red" : "green";});li.addEventListener("mouseout", function() {this.style.backgroundColor = "yellow";}); });
-
使用addEventListener:更灵活的事件绑定方式,支持事件捕获/冒泡控制
-
CSS优化:可以考虑使用CSS伪类结合JS实现,减少DOM操作
/* 可配合使用的CSS */ li:hover { transition: background-color 0.3s; }
案例3:节点类型统计功能
功能解析
实现鼠标移动到ul上时,在div中显示ul包含的各类节点数量:子节点、元素节点、属性节点和文本节点。
原代码分析
window.onload = function() {var ulFirstNeiRong = document.getElementsByTagName("ul")[0];var divAllNeiRong = ulFirstNeiRong.nextElementSibling;ulFirstNeiRong.onmouseover = function() {var ziJieDanShuZuZongChangDu = this.childNodes.length;var yuanSuJieDianShuZuZongChangDu = this.children.length;var shuXingJieDianShuZuZongChangDu = this.attributes.length;var wenBenJieDianShuZuZongChangDu = 0;var allJieDianShuZu = this.childNodes;for(var i of allJieDianShuZu) {if(i.nodeType == 3) {wenBenJieDianShuZuZongChangDu++;}}divAllNeiRong.innerHTML = `子节点${ziJieDanShuZuZongChangDu}个<br>元素节点${yuanSuJieDianShuZuZongChangDu}个<br>属性节点${shuXingJieDianShuZuZongChangDu}个<br>文本节点${wenBenJieDianShuZuZongChangDu}个`;}
}
优化建议
-
使用模板字符串:使HTML拼接更清晰(原代码已部分实现)
-
封装统计逻辑:将节点统计功能封装为函数,提高复用性
function countNodes(element) {const childNodes = element.childNodes;const textNodes = Array.from(childNodes).filter(node => node.nodeType === 3).length;return {total: childNodes.length,elements: element.children.length,attributes: element.attributes.length,text: textNodes}; }
-
处理空白文本节点:HTML中的换行和空格会被解析为文本节点,可以过滤空文本
// 优化文本节点计数,排除纯空白文本 if(i.nodeType === 3 && i.textContent.trim() !== '') {wenBenJieDianShuZuZongChangDu++; }
案例4:图片轮播功能
功能解析
实现图片自动轮播效果,鼠标悬停时停止轮播,移开时继续播放。
原代码分析
window.onload=function(){window.imgShuZu = ["./img/1.jpg","./img/2.jpg","./img/3.jpg"];window.indexXiaBiao = 0;window.img = document.getElementsByTagName("img")[0];window.start = null;// 鼠标移入停止轮播window.img.onmouseover=function(){clearInterval(window.start);}// 鼠标移出继续轮播window.img.onmouseout=function(){window.start = setInterval(gaiBianFunction, 666);}
}function gaiBianFunction(){if (window.indexXiaBiao <= 2) {indexXiaBiao++;}if (window.indexXiaBiao > 2) {indexXiaBiao = 0;}window.img.src = window.imgShuZu[window.indexXiaBiao];
}
优化建议
-
减少全局变量:使用闭包或IIFE封装变量,避免污染全局作用域
(function() {const imgShuZu = ["./img/1.jpg","./img/2.jpg","./img/3.jpg"];let indexXiaBiao = 0;const img = document.querySelector("img");let start = null;// 内部函数可以访问上述变量function startSlide() {start = setInterval(gaiBianFunction, 666);}// 其他代码...// 页面加载完成后自动开始轮播startSlide(); })();
-
优化索引计算:使用取模运算简化循环逻辑
function gaiBianFunction() {indexXiaBiao = (indexXiaBiao + 1) % imgShuZu.length;img.src = imgShuZu[indexXiaBiao]; }
-
添加图片预加载:避免轮播时图片加载延迟
// 预加载图片 imgShuZu.forEach(src => {const preloadImg = new Image();preloadImg.src = src; });
-
防止重复设置定时器:在设置新定时器前先清除旧的
window.img.onmouseout = function() {clearInterval(start); // 先清除start = setInterval(gaiBianFunction, 666); // 再设置 }
总结与扩展
以上四个案例覆盖了DOM操作的核心知识点:节点获取、样式修改、事件处理和动态内容更新。通过优化,我们可以使代码更简洁、高效且易于维护。
扩展建议:
- 学习使用ES6+特性(箭头函数、解构赋值等)简化代码
- 考虑使用事件委托减少事件绑定数量
- 对于复杂DOM操作,可以学习使用Vue、React等框架提高开发效率
- 始终注意DOM操作的性能问题,减少不必要的重绘和回流