JavaScript操作HTML DOM节点的基础教程


Posted in Javascript onMarch 11, 2016

因为 DOM 的存在,这使我们可以通过 JavaScript 来获取、创建、修改、或删除节点。
NOTE:下面提供的例子中的 element 均为元素节点。
获取节点

父子关系

element.parentNode
element.firstChild/element.lastChild
element.childNodes/element.children

兄弟关系

element.previousSibling/element.nextSibling
element.previousElementSibling/element.nextElementSibling

通过节点直接的关系获取节点会导致代码维护性大大降低(节点之间的关系变化会直接影响到获取节点),而通过接口则可以有效的解决此问题。

通过节点直接的关系获取节点会导致代码维护性大大降低(节点之间的关系变化会直接影响到获取节点),而通过接口则可以有效的解决此问题。

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>ELEMENT_NODE & TEXT_NODE</title>
</head>
<body>
 <ul id="ul">
 <li>First</li>
 <li>Second</li>
 <li>Third</li>
 <li>Fourth</li>
 </ul>
 <p>Hello</p>
 <script type="text/javascript">
 var ulNode = document.getElementsByTagName("ul")[0];
 console.log(ulNode.parentNode);    //<body></body>
 console.log(ulNode.previousElementSibling); //null
 console.log(ulNode.nextElementSibling);  //<p>Hello</p>
 console.log(ulNode.firstElementChild);  //<li>First</li>
 console.log(ulNode.lastElementChild);  //<li>Fourth</li>
 </script>
</body>
</html>

NTOE:细心的人会发现,在节点遍历的例子中,body、ul、li、p节点之间是没有空格的,因为如果有空格,那么空格就会被当做一个TEXT节点,从而用ulNode.previousSibling获取到得就是一个空的文本节点,而不是 <li>First</li> 节点了。即节点遍历的几个属性会得到所有的节点类型,而元素遍历只会得到相对应的元素节点。一般情况下,用得比较多得还是元素节点的遍历属性。
实现浏览器兼容版的element.children
有一些低版本的浏览器并不支持 element.children 方法,但我们可以用下面的方式来实现兼容。

<html lang>
<head>
 <meta charest="utf-8">
 <title>Compatible Children Method</title>
</head>
<body id="body">
 <div id="item">
 <div>123</div>
 <p>ppp</p>
 <h1>h1</h1>
 </div>
 <script type="text/javascript">
 function getElementChildren(e){
  if(e.children){
  return e.children;
  }else{
  /* compatible other browse */
  var i, len, children = [];
  var child = element.firstChild;
  if(child != element.lastChild){
   while(child != null){
   if(child.nodeType == 1){
    children.push(child);
   }
   child = child.nextSibling;
   }
  }else{
   children.push(child);
  }
  return children;
  }
 }
 /* Test method getElementChildren(e) */
 var item = document.getElementById("item");
 var children = getElementChildren(item);
 for(var i =0; i < children.length; i++){
  alert(children[i]);
 }
 </script>
</body>
</html>

NOTE:此兼容方法为初稿,还未进行兼容性测试。
接口获取元素节点

getElementById
getElementsByTagName
getElementsByClassName
querySelector
querySelectorAll

JavaScript操作HTML DOM节点的基础教程

getElementById

获取文档中指定 id 的节点对象。

var element = document.getElementById('id');
getElementsByTagName

动态的获取具有指定标签元素节点的集合(其返回值会被 DOM 的变化所影响,其值会发生变化)。此接口可直接通过元素而获取,不必直接作用于 document 之上。

// 示例
var collection = element.getElementsByTagName('tagName');

// 获取指定元素的所有节点
var allNodes = document.getElementsByTagName('*');

// 获取所有 p 元素的节点
var elements = document.getElementsByTagName('p');
// 取出第一个 p 元素
var p = elements[0];

getElementsByClassName
获取指定元素中具有指定 class 的所有节点。多个 class 可的选择可使用空格分隔,与顺序无关。
var elements = element.getElementsByClassName('className');
NOTE:IE9 及一下版本不支持 getElementsByClassName
兼容方法

function getElementsByClassName(root, className) {
 // 特性侦测
 if (root.getElementsByClassName) {
 // 优先使用 W3C 规范接口
 return root.getElementsByClassName(className);
 } else {
 // 获取所有后代节点
 var elements = root.getElementsByTagName('*');
 var result = [];
 var element = null;
 var classNameStr = null;
 var flag = null;

 className = className.split(' ');

 // 选择包含 class 的元素
 for (var i = 0, element; element = elements[i]; i++) {
  classNameStr = ' ' + element.getAttribute('class') + ' ';
  flag = true;
  for (var j = 0, name; name = className[j]; j++) {
  if (classNameStr.indexOf(' ' + name + ' ') === -1) {
   flag = false;
   break;
  }
  }
  if (flag) {
  result.push(element);
  }
 }
 return result;
 }
}

querySelector / querySelectorAll

获取一个 list (其返回结果不会被之后 DOM 的修改所影响,获取后不会再变化)符合传入的 CSS 选择器的第一个元素或全部元素。

var listElementNode = element.querySelector('selector');
var listElementsNodes = element.querySelectorAll('selector');

var sampleSingleNode = element.querySelector('#className');
var sampleAllNodes = element.querySelectorAll('#className');

NOTE: IE9 一下不支持 querySelector 与 querySelectorAll
创建节点

创建节点 -> 设置属性 -> 插入节点

var element = document.createElement('tagName');

修改节点

textContent
获取或设置节点以及其后代节点的文本内容(对于节点中的所有文本内容)。

element.textContent; // 获取
element.textContent = 'New Content';

NOTE:不支持 IE 9 及其一下版本。
innerText (不符合 W3C 规范)
获取或设置节点以及节点后代的文本内容。其作用于 textContent 几乎一致。

element.innerText;

NOTE:不符合 W3C 规范,不支持 FireFox 浏览器。
FireFox 兼容方案

if (!('innerText' in document.body)) {
 HTMLElement.prototype.__defineGetter__('innerText', function(){
 return this.textContent;
 });
 HTMLElement.prototype.__defineSetter__('innerText', function(s) {
 return this.textContent = s;
 });
}

插入节点

appendChild

在指定的元素内追加一个元素节点。

var aChild = element.appendChild(aChild);

insertBefore

在指定元素的指定节点前插入指定的元素。

var aChild = element.insertBefore(aChild, referenceChild);

删除节点

删除指定的节点的子元素节点。

var child = element.removeChild(child);

innerHTML

获取或设置指定节点之中所有的 HTML 内容。替换之前内部所有的内容并创建全新的一批节点(去除之前添加的事件和样式)。innerHTML 不检查内容,直接运行并替换原先的内容。
NOTE:只建议在创建全新的节点时使用。不可在用户可控的情况下使用。

var elementsHTML = element.innerHTML;

存在的问题+

  • 低版本 IE 存在内存泄露
  • 安全问题(用户可以在名称中运行脚本代码)

PS: appendChild() , insertBefore()插入节点需注意的问题
使用appendChild()和insertBefore()插入节点都会返回给插入的节点,

//由于这两种方法操作的都是某个节点的子节点,所以必须现取得父节点,代码中 someNode 表示父节点 
//使用appendChild()方法插入节点 
var returnedNode = someNode.appendChild(newNode); 
alert(returnedNode == newNode) //true 
 
//使用insertBefore()方法插入节点 
var returnedNode = someNode.appendChild(newNode); 
alert(returnedNode == newNode) //true

 值得注意的是,如果这两种方法插入的节点原本已经存在与文档树中,那么该节点将会被移动到新的位置,而不是被复制。

<div id="test"> 
 <div>adscasdjk</div> 
  <div id="a">adscasdjk</div> 
</div> 
<script type="text/javascript"> 
 var t = document.getElementById("test"); 
 var a = document.getElementById('a'); 
 //var tt = a.cloneNode(true); 
 t.appendChild(a); 
</script>

在这段代码中,页面输出的结果和没有Javascript时是一样的,元素并没有被复制,由于元素本来就在最后一个位置,所以就和没有操作一样。如果把id为test的元素的两个子元素点换位置,就可以在firbug中看到这两个div已经被调换了位置。
如果我们希望把id为a的元素复制一个,然后添加到文档中,那么必须使被复制的元素现脱离文档流。这样被添加复制的节点被添加到文档中之后就不会影响到文档流中原本的节点。即我们可以把复制的元素放到文档的任何地方,而不影响被复制的元素。下面使用了cloneNode()方法,实现节点的深度复制,使用这种方法复制的节点会脱离文档流。当然,我不建议使用这种方法复制具有id属性的元素。因为在文档中id值是唯一的。

<div id="test"> 
 <div>adscasdjk</div> 
  <div id="a">adscasdjk</div> 
</div> 
<script type="text/javascript"> 
 var t = document.getElementById("test"); 
 var a = document.getElementById('a'); 
 var tt = a.cloneNode(true); 
 t.appendChild(tt); 
</script>

相似的操作方法还有 removeNode(node)删除一个节点,并返回该节;replaceNode(newNode,node)替换node节点,并返回该节点。这两种方法相对来说更容易使用一些。

Javascript 相关文章推荐
调试JavaScript中正则表达式中遇到的问题
Jan 27 Javascript
javascript解三阶幻方(九宫格)
Apr 22 Javascript
微信JSSDK上传图片
Aug 23 Javascript
javascript计时器编写过程与实现方法
Feb 29 Javascript
HTML的select控件美化
Mar 27 Javascript
JavaScript通过filereader接口读取文件
May 10 Javascript
微信小程序实现点击按钮修改文字大小功能【附demo源码下载】
Dec 06 Javascript
Javascript中从学习bind到实现bind的过程
Jan 05 Javascript
Express之托管静态文件的方法
Jun 01 Javascript
js String.prototype.trim字符去前后空格的扩展
Aug 23 Javascript
Vue Render函数创建DOM节点代码实例
Jul 08 Javascript
Vue+scss白天和夜间模式切换功能的实现方法
Jan 05 Vue.js
举例说明JavaScript中的实例对象与原型对象
Mar 11 #Javascript
JavaScript中setTimeout和setInterval函数的传参及调用
Mar 11 #Javascript
原生JavaScript制作微博发布面板效果
Mar 11 #Javascript
JavaScript获取图片像素颜色并转换为box-shadow显示
Mar 11 #Javascript
详解Angularjs中的依赖注入
Mar 11 #Javascript
详解AngularJS过滤器的使用
Mar 11 #Javascript
javascript html5 canvas实现可拖动省份的中国地图
Mar 11 #Javascript
You might like
DSP接收机前端设想
2021/03/02 无线电
个人站长制做网页常用的php代码
2007/03/03 PHP
解析PHP中的正则表达式以及模式匹配
2013/06/19 PHP
php自动加载autoload机制示例分享
2014/02/20 PHP
Yii框架where查询用法实例分析
2019/10/22 PHP
用jquery来定位
2007/02/20 Javascript
js 模拟气泡屏保效果代码
2010/07/10 Javascript
用JQuery调用Session的实现代码
2010/10/29 Javascript
jquery自动切换tabs选项卡的具体实现
2013/12/24 Javascript
判断及设置浏览器全屏模式
2014/04/20 Javascript
教你在heroku云平台上部署Node.js应用
2014/07/30 Javascript
jQuery实现Div拖动+键盘控制综合效果的方法
2015/03/10 Javascript
jQuery结合AJAX之在页面滚动时从服务器加载数据
2015/06/30 Javascript
WebSocket+node.js创建即时通信的Web聊天服务器
2016/08/08 Javascript
JavaScript基于对象去除数组重复项的方法
2016/10/09 Javascript
Vue.js中数据绑定的语法教程
2017/06/02 Javascript
VueJs单页应用实现微信网页授权及微信分享功能示例
2017/07/26 Javascript
代码详解JS操作剪贴板
2018/02/11 Javascript
详解vuex的简单使用
2018/03/12 Javascript
使用JS来动态操作css的几种方法
2019/12/18 Javascript
[02:41]辉夜杯现场一家三口 “我爸玩风行 我玩血魔”
2015/12/27 DOTA
[01:01:18]VP vs NIP 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
Python的另外几种语言实现
2015/01/29 Python
Python中将字典转换为列表的方法
2016/09/21 Python
Python实现识别手写数字 Python图片读入与处理
2020/03/23 Python
Flask Web开发入门之文件上传(八)
2018/08/17 Python
Python面向对象程序设计类的封装与继承用法示例
2019/04/12 Python
python 控制台单行刷新,多行刷新实例
2020/02/19 Python
Python实现从N个数中找到最大的K个数
2020/04/02 Python
Python+Selenium实现自动化的环境搭建的步骤(图文)
2020/09/01 Python
MATCHESFASHION.COM法国官网:英国奢侈品零售商
2018/01/04 全球购物
SQL面试题
2013/04/30 面试题
Java面向对象面试题
2016/12/26 面试题
校企合作协议书
2014/04/16 职场文书
竞选村长演讲稿
2014/04/28 职场文书
OpenCV-Python直方图均衡化实现图像去雾
2021/06/07 Python