DOM--节点操作

我们知道,DOM主要是对元素进行增删改查和绑定事件,那么获取元素作为前提,就显得格外的

重要。获取元素通常使用两种方式

(1)  利用 DOM 提供的方法获取元素              (2)  利用节点层级关系获取元素

DOM提供的方法,在前面的文章里已经介绍过了,就是 document.getElementById()

document.getElementsByTagName()、document.querySelector 等。这些方法其实逻辑性不强、

而且相对来说有些繁琐。

所以,本文将介绍第二种方式--利用节点关系来获取元素,以及进行节点操作。

1. 节点概述

网页中所有内容都是节点(标签、属性、文本、注释等),在DOM 中,节点使用 node 来表示。
HTML DOM 树(如下图所示)中的所有节点均可通过 JavaScript 进行访问,所有 HTML 元素(节点)均可被修改,也可以创建或删除。

一般地,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。 

元素节点 nodeType 为 1
属性节点 nodeType 为 2
文本节点 nodeType 为 3 (文本节点包含文字、空格、换行等)
我们在实际开发中,节点操作主要操作的是元素节点

2. 节点层级

利用 DOM 树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。 

2.1. 父级节点

node.parentNode  或 node.parentElement

parentNode 属性 / parentElement 属性的用法一致,没有区别,都可返回某节点的父节点,注意是最近的一个父节点
② 如果指定的节点没有父节点则返回 null  

2.2 子节点 

1. parentNode.childNodes

parentNode.childNodes 返回包含指定节点的所有子节点的集合,该集合为即时更新的集合。
注意:返回值里面包含了所有的子节点包括元素节点,文本节点等。因此,如果只想要获得里面的元素节点,则需要专门处理。 所以我们一般不提倡使用childNodes

2. parentNode.children

parentNode.children 是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回 (这个是我们重点掌握的)。

3. parentNode.firstChild

4. parentNode.lastChild

firstChild 返回第一个子节点,找不到则返回null。同样,也是包含所有的节点,即包含文本节点。 lastChild 返回最后一个子节点,找不到则返回null。同样,也是包含所有的节点

5. parentNode.firstElementChild 

6. parentNode.lastElementChild

 firstElementChild 返回第一个子元素节点,找不到则返回null。lastElementChild 返回最后一个子元素节点,找不到则返回null。但是这两个方法有兼容性问题,IE9 以上才支持。

实际开发中,firstChild 和 lastChild 包含其他节点,操作不方便,而 firstElementChild 和 
lastElementChild 又有兼容性问题,那么我们如何获取第一个子元素节点或最后一个子元素节点呢
解决方案:
1. 如果想要第一个子元素节点,可以使用 parentNode.chilren[0] 
2. 如果想要最后一个子元素节点,可以使用 parentNode.chilren[parentNode.chilren.length - 1]

2.3 案例:下拉菜单

<!-- css样式 -->
<style>
    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
    body {
        background-color: #F5F5F5;
    }
    li {
        list-style: none;
    }
    #select {
        height: 50px;
        margin: 50px;
        background-color: #eee;
    }
    #select>li {
        float: left;
        width: 100px;
        height: 50px;
        line-height: 50px;
        text-indent: 5px;
    }
    a {
        color: #666;
        text-decoration: none;
    }
    a:hover {
        color: #F83C3D;
    }
    #select ul {
        display: none;
        font-size: 14px;
        width: 100px;
        background-color: #fff;
    }
</style>
<body>
    <ul id="select">
        <li>
            <a href="#">我的淘宝</a>
            <ul>
                <li>已买到的宝贝</li>
                <li>我的足迹</li>
            </ul>
        </li>
        <li>
            <a href="#">我的收藏</a>
            <ul>
                <li>收藏的宝贝</li>
                <li>收藏的店铺</li>
                <li>收藏的主播</li>
            </ul>
        </li>
        <li>
            <a href="#">联系客服</a>
            <ul>
                <li>卖家客服</li>
                <li>消费者客服</li>
                <li>意见反馈</li>
            </ul>
        </li>
    </ul>
    <script>
        // 1. 获取元素
        let lis = document.querySelectorAll("#select>li");
        // 2. 通过遍历给每个li绑定鼠标移入移出事件
        for (let i = 0; i < lis.length; i++) {
            lis[i].onmouseover = function() {
              // 鼠标移入该li背景色变成白色
                this.style.backgroundColor = '#fff';
             // 对应的下拉菜单显示(下拉菜单是该li 的第二个孩子,下标从0开始)
                lis[i].children[1].style.display = 'block';
            };
            lis[i].onmouseout = function() {
              // 鼠标移出该li背景色变回原来颜色
                this.style.backgroundColor = '';
                // 对应的下拉菜单隐藏
                lis[i].children[1].style.display = 'none';
            }
        }
    </script>
</body>

案例分析: 

① 导航栏里面的li 都要有鼠标经过效果,所以需要循环注册鼠标事件
② 核心原理: 当鼠标经过 li 里面的 第二个孩子 ul 显示, 当鼠标离开,则ul 隐藏。

本案例就是合理的利用了不同元素之间的节点关系,从而方便而快速的实现了下拉效果。

2.3 兄弟节点 

1. node.nextSibling

2. node.previousSibling

nextSibling 返回当前元素的下一个兄弟节点,找不到则返回null。同样,也是包含所有的节点。 previousSibling 返回当前元素上一个兄弟节点,找不到则返回null。同样,也是包含所有的节点。由于获取到的节点包括所有节点,所以不推荐使用

3. node.nextElementSibling

4. node.previousElementSibling

nextElementSibling 返回当前元素下一个兄弟元素节点,找不到则返回null。previousElementSibling 返回当前元素上一个兄弟元素节点,找不到则返回null。但是这两个方法有兼容性问题, IE9 以上才支持。 

如何解决兼容性问题 ?

—— 自己封装一个兼容性的函数,如下:

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

3.创建节点

document.createElement('tagName'); 

如:document.createElement(‘li’)  // 动态创建了一个li节点

document.createElement() 方法创建由 tagName 指定的 HTML 元素。因为这些元素原先不存在,
是根据我们的需求动态生成的,所以我们也称为动态创建元素节点。  

4.添加节点

1. node.appendChild(child)

node.appendChild() 方法将一个节点添加到指定父节点的子节点列表末尾。类似于 CSS 里面的 
after 伪元素。

 2. node.insertBefore(child, 指定元素) 

node.insertBefore() 方法将一个节点添加到父节点的指定子节点前面。类似于 CSS 里面的 before 
伪元素。

5. 删除节点

node.removeChild(child) 

node.removeChild() 方法从 DOM 中删除一个子节点,返回删除的节点,node是要删除节点的父节点(亲生的/最近的那个父节点)

6.案例:简单版发布留言案例

需求:输入信息,点击发送按钮,页面上会将刚刚这条信息显示在留言区域的最上方。并且每生成的这条信息都自带一个删除按钮,用来删除本条信息。

<!-- css样式 -->
<style>
    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
    .box {
        width: 400px;
        margin: 50px;
    }
    .box_send textarea {
        width: 300px;
        height: 160px;
        outline: none;
        resize: none;
    }
    .box_send button {
        padding: 5px 15px;
        vertical-align: bottom;
    }
    .box_message {
        margin-top: 15px;
    }
    .box_message li {
        width: 300px;
        line-height: 34px;
        font-size: 14px;
        text-indent: 5px;
        color: #666;
        list-style: none;
        border-bottom: 1px dashed #ccc;
    }
    .box_message li a {
        float: right;
    }
</style>
<body>
    <div class="box">
        <div class="box_send">
            <textarea id="send_data"></textarea>
            <button id="btnSend">发布</button>
        </div>
        <div class="box_message">
            <ul></ul>
        </div>
    </div>
    <script>
        // 1. 获取元素
        let textarea = document.getElementById("send_data");
        let btn = document.getElementById("btnSend");
        let ul = document.querySelector(".box_message ul");
        // 2. 给发布按钮绑定点击事件
        btn.onclick = function() {
            // 使用value获取文本域里的值,并且做非空判断
            // trim() 方法用于去除文本两端的空格,那么只输入空格也是无效内容
            if (textarea.value.trim().length <= 0) {
                return alert('请输入内容!');
            }
            // 在确定输入内容有效后,开始创建节点li
            let li = document.createElement('li');
            // 把li 节点添加至ul 中,由于最新发布的显示在最上方,所以使用insertBefore()
            ul.insertBefore(li, ul.children[0]);
      // 给li添加内容(即把文本域里的内容赋值给li,同时给li里添加一个删除链接,用来删除本条内容)
            li.innerHTML = textarea.value + '<a href="javascript:;">删除</a>';
            // 先获取a 再给 a 绑定点击事件
            let a = document.querySelector('a');
            a.onclick = function() {
                // 在删除前先提醒用户
                let flag = confirm("您确定删除吗?");
                // 如果确定,则执行删除操作
                if (flag) {
                    // 删除本条数据,即删除它的父亲
                    ul.removeChild(this.parentNode);
                }
            };
            // 添加成功后,将文本域内容清空,方便下次输入内容发布
            textarea.value = '';
        };
    </script>
</body>

案例分析:

① 核心思路: 点击按钮之后,就动态创建一个li,添加到ul 里面。
② 创建li 的同时,把文本域里面的值通过li.innerHTML 赋值给 li
③ 如果想要新的留言后面显示就用 appendChild 如果想要前面显示就用insertBefore

④ 当我们把文本域里面的值赋值给li 的时候,多添加一个删除的链接
⑤ 需要把链接获取过来,当我们点击当前的链接的时候,删除当前链接所在的li
⑥ 阻止链接跳转需要添加 javascript:void(0); 或者 javascript:;

7.复制节点(克隆节点)

node.cloneNode()

node.cloneNode() 方法返回调用该方法的节点的一个副本。 也称为克隆节点/拷贝节点  

注意:
1. 如果括号参数为空或者为 false ,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点
2. 如果括号参数为 true ,则是深度拷贝会复制节点本身以及里面所有的子节点。 

猜你喜欢

转载自blog.csdn.net/JJ_Smilewang/article/details/125321755