DOM 节点操作

本文深入探讨了DOM节点的各种操作,包括获取元素、节点属性、父子节点、兄弟节点的属性和方法,以及如何创建、添加、删除和修改节点。通过实例展示了动态创建表格、实现下拉菜单效果和动态删除元素等功能,强调了innerHTML与createElement方法的使用场景和效率对比。同时,讲解了事件处理和节点遍历等核心概念,为JavaScript操作HTML提供了全面的指南。
摘要由CSDN通过智能技术生成

DOM 节点操作

获取元素通常使用两种方式:

(1)利用 DOM 提供的方法获取元素(逻辑性不强,繁琐)

(2)利用节点层级关系获取元素(逻辑性强,但兼容性稍差)

1. DOM 树

在这里插入图片描述

2. 节点属性

(1)nodeType 节点的类型,属性值为数字,表示不同的节点类型,共 12 种,只读

  • 1 → 元素节点
  • 2 → 属性节点
  • 3 → 文本节点 (包含文字、空格、换行等)

(2)nodeName 节点的名称(标签名称),只读

(3)nodeValue 节点值,返回或设置当前节点的值

注意:元素节点的 nodeValue 始终是 nul

3. 父子节点常用属性

(1)parentNode ***

  • 返回一个当前节点的父节点(离元素最近的父级节点)
  • 如果没有这样的节点,比如说像这个节点是树结构的顶端或者没有插入一棵树中,这个属性返回 null

(2)parentElement

  • 返回当前节点的父元素节点
  • 如果该元素没有父节点,或者父节点不是一个 DOM 元素,则返回 null

(3)childNodes

  • 只读属性
  • 获取一个节点所有子节点(包括元素节点、文本节点等)的实时的集合
  • 集合是动态变化的。
  • 标准,不提倡使用
<div id="box">
    <p>段落</p>
</div>
<script>
	console.log(box.childNodes); // NodeList(3) [text, p, text]
</script>

(4)children ***

  • 只读属性
  • 返回一个节点所有的子元素节点集合
  • 是一个动态更新的 HTML 元素集合。
  • 非标准,但得到各个浏览器的支持,常用

(5)firstChild

  • 只读属性
  • 返回该节点的第一个子节点,如果该节点没有子节点则返回 null。
  • 不区分节点类型

(6)lastChild

  • 只读属性
  • 返回该节点的最后一个子节点,如果该节点没有子节点则返回 null
  • 不区分节点类型

(7)firstElementChild

  • 只读属性
  • 返回该节点的第一个子元素节点,如果没有则返回 null。
  • 有兼容问题,IE9 以上才支持

(8)lastElementChild

  • 只读属性
  • 返回该节点的最后一个子元素节点,如果没有则返回 null。
  • 有兼容问题,IE9 以上才支持

注:实际开发中的写法,既没有兼容问题又返回第一个或最后一个子元素

console.log(box.children[0]); // 返回 box 中的第一个子元素
console.log(box.children[box.children.length - 1]);  // 返回 box 中的最后一个子元素

案例:隔行变色案例重写

// 获取 tbody
var tb = document.getElementById("tb");
// 获取 tbody 里的所有子元素节点 tr
var trs = tb.children;
	for (var i = 0; i < trs.length; i++) {
    if (i % 2 === 0) {
    	trs[i].style.backgroundColor = "pink";
    } else {
        trs[i].style.backgroundColor = "lightgray";
    }
}

案例:新浪下拉菜单

在这里插入图片描述

结构:大盒子 ul (.nav)包含3个 li ,每个 li 包含上下两部分,上边一个 a 链接,下边 ul 包括3个 li

效果:当鼠标经过 .nav 的 li 时,让 li 的第二个子元素显示,即下边的 ul 显示

 var nav = document.querySelector(".nav");
 var lis = nav.children; // 得到3个小li
 for (var i = 0; i < lis.length; i++) {
 	lis[i].onmouseover = function () {
    	this.children[1].style.display = "block";
    };
    lis[i].onmouseout = function () {
    	this.children[1].style.display = "none";
    };
}

4. 兄弟节点常用属性

(1)nextSibling

  • 只读属性
  • 返回与该节点同级的下一个节点,如果没有返回null
  • 不区分节点类型

(2)previousSibling

  • 只读属性
  • 返回与该节点同级的上一个节点,如果没有返回null
  • 不区分节点类型

(3)nextElementSibling

  • 只读属性
  • 返回与该节点同级的下一个元素节点,如果没有返回null。
  • 有兼容性问题,IE9以后才支持

(4)previousElementSibling

  • 只读属性
  • 返回与该节点同级的上一个元素节点,如果没有返回null。
  • 有兼容性问题,IE9以后才支持

可以封装一个解决兼容性问题的函数:

function getNextElementSibling(element) {
	var el = element;
    while ((el = el.nextSibling)) {
    	if (el.nodeType === 1) {
        	return el;
        }
    }
    return null;
}

5. 创建新节点的方法

(1)document.createElement(“div”) → 创建元素节点

(2)document.createAttribute(“id”) → 创建属性节点

(3)document.createTextNode(“hello”) → 创建文本节点

注意:

  • 一般将创建的新节点存在变量中,方便使用。
  • 创建的新的节点是存储在内存中的,但是并没有添加到 DOM 树上

6. 节点常用操作方法1

(1)parentNode.appendChild(child)

  • 将一个节点添加到指定父节点的子节点列表末尾。
  • 文本节点也可以添加到元素内部
  • 自己创建的元素节点本身也是一个对象,也可以去添加一些新的属性和方法,这些操作将来在元素加载到 DOM 树时,依旧保留

注意:DOM 中原有的节点也可以传给 appendChild 的参数(相当于剪切)

  • 先将节点从原始位置删除,然后添加到新的指定位置。
  • 原因:内存中这个原有节点只有一个,渲染时只能有一个位置

(2)parentNode.replaceChild(newChild, oldChild)

  • 用指定的节点替换当前节点的一个子节点
  • 并返回被替换掉的节点。

(3)parentNode.insertBefore(newNode, referenceNode)

  • 在参考节点之前插入一个拥有指定父节点的子节点
  • referenceNode 必须设置,如果 referenceElement 为 null 则 newNode 将被插入到子节点的末尾。

(4)parentNode.removeChild(child)

  • 移除当前节点的一个子节点。
  • 这个子节点必须存在于当前节点中。

7. 节点常用操作方法2

Node.cloneNode() (相当于复制)

  • 克隆一个节点,并且可以选择是否克隆这个节点下的所有内容。
  • 参数为 Boolean 布尔值,表示是否采用深度克隆
  • 如果为 true,深度克隆,则该节点的所有后代节点也都会被克隆
  • 如果为 false,浅度克隆,则只克隆该节点本身
  • 默认值为 false,节点下的内容不会被克隆。(建议写的时候标明true / false)

注意:克隆时,标签上的属性和属性值也会被复制,写在标签行内的绑定事件可以被复制, 但是通过 JavaScript 动态绑定的事件不会被复制

(克隆时,为了防止 id 名和之前的冲突,可以更改 id 名)

8. 节点常用操作方法3

(1)Node.hasChildNodes()

  • 没有参数,返回一个 Boolean 布尔值
  • 表示该元素是否包含有子节点
  • 判断有没有子节点,不区分节点类型(若有个空格也显示为true)

(2) Node.contains(child)

  • 返回一个 Boolean 布尔值
  • 表示传入的节点是否为该节点的后代节点
  • 不局限于父子关系,只要包含就返回true

9. 判断方法总结

判断当前节点是否有子节点(若返回 true 就表示有)

  • node.firstChild !== null (判断节点内部第一个子节点是否不为空)
  • node.childNodes.length > 0 (判断子节点的数组对象的长度是否为0)
  • node.hasChildNodes() (判断有没有子节点,不区分节点类型)

判断当前节点是否有子元素节点(若返回 true 就表示有)

  • node.firstElementChild !== null
  • node.children.length > 0

10. 三种动态创建元素区别

(1)document.write()

很少用;直接将内容写入页面的内容流,但文档流执行完毕,则它会导致页面全部重绘。

btn.onclick = function () {
	document.write("<div>123</div>");
};
// 点击按钮后会形成一个新的页面,页面中只有一个 div 元素,其他都没有了

注意:如果页面文档流加载完毕,再调回这句话会导致页面重绘。

(2)element.innerHTML

innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘

var inner = document.querySelector(".inner");
//   for (var i = 0; i <= 100; i++) {
//     inner.innerHTML += "<a href='#'>百</a>";
//   }
// 因为拼接字符串,根据字符串的不可变性,需要开辟新的内容,所以比较耗时
var arr = [];
for (var i = 0; i <= 100; i++) {
  arr.push("<a href='#'>百</a>");
}
inner.innerHTML = arr.join("");
// 数组转换为字符串,耗时最短 

(3)document.createElement()

var tr = document.createElement("tr");
tb.appendChild(tr);

总结:不同浏览器下,innerHTML 效率要比 createElement 高(前提:不要拼接字符串,采取数组形式拼接)

  • innerHTML 创建多个元素效率更高,(采取数组形式拼接),结构稍复杂
  • createElement() 创建多个元素效率稍低一点点,但结构更清晰

案例:动态创建表格

<body>
	<h1>动态创建表格</h1>
    <table border="1">
      <thead>
        <tr>
          <th>姓名</th>
          <th>科目</th>
          <th>成绩</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody id="tb"></tbody>
    </table>
    <script>
      var tb = document.getElementById("tb");
      // 模拟后台传输的数据
      var datas = [
        { name: "zs", subject: "语文", score: 80 },
        { name: "ls", subject: "数学", score: 90 },
        { name: "ww", subject: "英语", score: 70 },
        { name: "cc", subject: "物理", score: 88 },
        { name: "lb", subject: "化学", score: 57 },
        { name: "gy", subject: "生物", score: 100 },
        { name: "sq", subject: "历史", score: 93 },
      ];
      // 根据数组中的数据个数生成对应个数的 tr
      // 将生成的 tr 添加到 tbody 中
      // 数组遍历
      for (var i = 0; i < datas.length; i++) {
        // 每一个数据都要生成一个 tr
        var tr = document.createElement("tr");
        // 添加到 tbody 中
        tb.appendChild(tr);
        // 每一个 tr 中还需要添加对应的 td
        // 每一行内部的 tbody 中的数据来自于数组中的每一项
        var data = datas[i]; // { name: "zs", subject: "语文", score: 80 }
        // 遍历 data 对象,根据它的项数来确定添加的 td 的个数
        for (var k in data) {
          // 生成一个 td
          var td = document.createElement("td");
          // 添加到 tr 中
          tr.appendChild(td);
          // 添加给每一个 td 数据
          td.innerText = data[k];
        }
        // 除了前面动态获取的数据 td 之外,还要添加一个删除的 td
        // 循环内的变量是全局变量
        td = document.createElement("td");
        // 添加到 tr 中
        tr.appendChild(td);
        // 最后一个 td 中需要添加一个 a 标签
        var a = document.createElement("a");
        a.innerText = "删除";
        a.href = "javascript:void(0);";
        // 将 a 添加到 td 中
        td.appendChild(a);
        // 给生成的每个 a 添加一个点击事件,移除当前所在的行
        a.onclick = function () {
          // 找到所在的行 tr
          // this.parentNode.parentNode
          // 从 tbody 中移除对应的 tr
          tb.removeChild(this.parentNode.parentNode);
        };
      }
    </script>
</body>

案例:选择水果

<body>
    <!-- multiple 属性规定可选择多个选项 -->
    <!-- size 规定下拉列表中可见选项的数目 -->
    <select id="all" size="5" multiple="multiple"></select>

    <input type="button" value=">>" id="btn1" />
    <input type="button" value="<<" id="btn2" />
    <input type="button" value=">" id="btn3" />
    <input type="button" value="<" id="btn4" />

    <select id="choose" multiple="multiple"></select>

    <script>
      var all = document.getElementById("all");
      var btn1 = document.getElementById("btn1");
      var btn2 = document.getElementById("btn2");
      var btn3 = document.getElementById("btn3");
      var btn4 = document.getElementById("btn4");
      var choose = document.getElementById("choose");

      var datas = ["苹果", "橘子", "梨", "西瓜", "水蜜桃"];
      // 动态创建 option
      for (var i = 0; i < datas.length; i++) {
        var opts = document.createElement("option");
        all.appendChild(opts);
        opts.innerText = datas[i];
      }
      // 给第一个按钮添加点击事件,让 all 中的所有子元素移动到 choose 中
      btn1.onclick = function () {
        // 获取 all 中所有的子元素
        var opts = all.children; // 内部的元素是动态添加的
        // 获取最开始的数组个数
        var n = opts.length;
        // 将所有的 opts 中的元素添加给 choose
        for (var i = 0; i < n; i++) {
          choose.appendChild(opts[0]);
        }
      };
      btn2.onclick = function () {
        var opts = choose.children;
        var n = opts.length;
        for (var i = 0; i < n; i++) {
          all.appendChild(opts[0]);
        }
      };

      // 单选移动
      btn3.onclick = function () {
        var opts = all.children;
        // 移动的内容希望可以是固定的一个数组中的项
        var arr = [];
        // 通过判断条件,往 arr 中添加需要移动的元素
        for (var i = 0; i < opts.length; i++) {
          if (opts[i].selected === true) {
            arr.push(opts[i]);
          }
        }
        for (var j = 0; j < arr.length; j++) {
          choose.appendChild(arr[j]);
          arr[j].selected = false;
        }
      };

      btn4.onclick = function () {
        var opts = choose.children;
        // 移动的内容希望可以是固定的一个数组中的项
        var arr = [];
        // 通过判断条件,往 arr 中添加需要移动的元素
        for (var i = 0; i < choose.length; i++) {
          if (opts[i].selected === true) {
            arr.push(choose[i]);
          }
        }
        for (var j = 0; j < arr.length; j++) {
          all.appendChild(arr[j]);
          arr[j].selected = false;
        }
      };
    </script>
</body>
/* css 代码 */
select {
	width: 200px;
	height: 200px;
	background-color: #33cccc;
	font-size: 20px;
}

案例:简单版发布留言及删除留言

<body>
	<textarea cols="30" rows="10"></textarea>
    <button>发布</button>
    <ul></ul>
    <script>
      var text = document.querySelector("textarea");
      var ul = document.querySelector("ul");
      var btn = document.querySelector("button");
      // 发布留言
      btn.onclick = function () {
        if (text.value == "") {
          alert("您还没有输入内容!");
          return false; // 终止函数继续执行
        } else {
          var li = document.createElement("li");
          // 文本域的内容赋给 li
          // a 的样式可以写在 CSS 中,如 li a{}
          li.innerHTML = text.value + "<a href='javascript:void(0);'>删除</a>";
          // 每次发布都在前面
          ul.insertBefore(li, ul.children[0]);

          //   var a = document.createElement("a");
          //   a.href = "javascript:void(0);";
          //   a.innerText = "删除";
          //   li.appendChild(a);

          // 删除留言
          var as = document.querySelectorAll("a");
          for (var i = 0; i < as.length; i++) {
            as[i].onclick = function () {
                // 删除的是当前 a 的父亲 li 
              ul.removeChild(this.parentNode);
            };
          }
        }
      };
    </script>
</body>

案例:每点击一次按钮,就删除一个元素,当没有元素可删除时,按钮被禁用

var ul = document.querySelector("ul");
var btn = document.querySelector("button");
btn.onclick = function () {
  if (ul.children.length == 0) {
    this.disabled = true;
  } else {
    ul.removeChild(ul.children[0]);
  }
};

注:阻止链接跳转 <a href='javascript:void(0);'></a><a href='javascript:;'></a>

DOM 重点核心总结

对于 JavaScript ,为了能使 JavaScript 操作 HTML,JavaScript就有了一套自己的 DOM 编程接口

对于 HTML,DOM 使得 HTML 形成一棵 DOM 树,包括文档、元素、节点

注意:我们获取过来的 DOM 元素是一个对象(object),所以称为文档对象模型

关于 DOM 操作,我们主要针对于元素的操作,主要有创建、增删改查、属性操作、事件操作

(1)创建

  • document.write()
  • element.innerHTML
  • document.createElement()

(2)增

  • parentNode.appendChild(child) (后)
  • parentNode.insertBefore(newNode, referenceNode) (前)

(3)删

  • parentNode.removeChild(child)

(4)改

  • 修改元素属性:src、href、title等
  • 修改普通元素内容:innerHTML、innerText
  • 修改表单元素:value、type、disabled等
  • 修改元素样式:style、className

(5)查(主要获取查询 DOM 的元素)

  • DOM 提供的 API 方法:getElementById、getElementsByTagName
  • H5 提供的新方法:querySelector、querySelectorAll
  • 利用节点操作获取元素:父(parentNode)、子(children)、兄(previousElementSibling、nextElementSibling)

(6)属性操作(主要针对于自定义属性)

  • setAttribute:设置 DOM 的属性值
  • getAttribute:得到 DOM 的属性值
  • removeAttribute:移除属性

(7)事件操作

  • 给元素注册事件
  • 采取 “事件源.事件类型 = 事件处理程序”
    tBefore(newNode, referenceNode) (前)

(3)删

  • parentNode.removeChild(child)

(4)改

  • 修改元素属性:src、href、title等
  • 修改普通元素内容:innerHTML、innerText
  • 修改表单元素:value、type、disabled等
  • 修改元素样式:style、className

(5)查(主要获取查询 DOM 的元素)

  • DOM 提供的 API 方法:getElementById、getElementsByTagName
  • H5 提供的新方法:querySelector、querySelectorAll
  • 利用节点操作获取元素:父(parentNode)、子(children)、兄(previousElementSibling、nextElementSibling)

(6)属性操作(主要针对于自定义属性)

  • setAttribute:设置 DOM 的属性值
  • getAttribute:得到 DOM 的属性值
  • removeAttribute:移除属性

(7)事件操作

  • 给元素注册事件
  • 采取 “事件源.事件类型 = 事件处理程序”
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值