DOM基础知识

319 阅读5分钟

“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第17篇文章,点击查看活动详情

docoment object model 文档对象模型

一、DOM树

【dom树】:浏览器在加载页面的时候,首先就是dom的结构计算,它形式就像是一颗大树,有很多的分支,所以被称为 "dom tree"

二、DOM操作

1.【常见的获取DOM的方式】

           //ID=>submit 默认不写 docoument 浏览器可以渲染

<body>
    <button id="submit">更改Body的背景色</button>
</body>
<script>
    console.log(submit);
</script>
</html>

1)getElementById:通过元素的id获取元素

//DOM元素对象
document.getElementById("box");
【document】

document 是上下文,限制范围的意思:在哪个范围下的 id名字是box的这个元素,不过这个上下文只能是   document

【id名字唯一性】

id名字是唯一的,一个document文档中不能同时出现多个相同的id名字,如果设置了多个相同的id,只能获取到第一个。

[兼容性注意]:不要让name 和id的名字一样。因为在ie6-ie7 的时候,如果设置了name属性,通过id也可以获取到

<body>
   <input type="text" name="text">
</body>
</html>
<script>
  console.log(document.getElementById("text"));
  //  在ie6或者ie7的时候可以获取到
</script>

2)[context].getElementsByTagName:HTMLCollection 元素集合【类数组集合】

在特定的上下文中,通过标签名,获取一组元素。
  • 得到的是一个集合,如果想要操作其中的某一项,可以基于索引获取。
var omain=document.getElementById("main");
var olis=omain.getElementsByTagName("li");
// 想要获取到第二个li:olis[1]

3) [context].getElementsByCalssName:通过类名获取元素集合【类数组集合】

  • 此方法在ie6--ie8下不兼容

4)document.getElementsByName:通过name名字获取一组 【节点集合 类数组】

  • 它的上下文也只能是document
  • 另外,正常的规范中,咱们只会给表单元素起name值,如果给其它元素设置name,在ie9以下版本浏览器不兼容,是获取不到的,所以为了这种情况,咱们以后养成习惯,只给表单元素用name,非表单元素不用name

5)[context].querySelector("选择器")

通过选择器获取指定的元素,即使匹配的有多个,也只会对第一个起作用,获取到的是一个对象(给咱们平时写样式时候写选择器是一样的)

  <div class="main">
        <div class="box1"></div>
    </div>
    
    document.querySlector(".main>div")

6) [context】.querySelectorAll("选择器") 【节点集合 类数组】

通过指定的选择器获取一组节点集合

注意:querySelector 和querySelector 在ie6-ie8 下不兼容

7)document.head

     获取Head元素对象

8) document.body

   获取body元素对象

9) document.documentElement

  获取html元素对象

【需求:】获取一屏幕的宽度或者高度,兼容所有的浏览器
// 获取一屏幕的高度
var vH=document.documnentElement.clientHeight || document.body.clientHeight;
// 获取一屏幕的宽度
var vW=document.documentElement.clientWidth || document.body.clientWidth;

1.2 【练习题】

获取页面中所有name属性值为 "hehe" 的元素标签

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
       
    </style>

</head>
<body>
    
   <div class="box1">
       <input type="text" name="he1">
   </div>
   <input class="box1" name="he1">
</body>
</html>
<script>
 function getElementsByName(value){
     var allTag=document.getElementsByTagName("*");
     var names=[];
     for(var i=0;i<allTag.length;i++){
          var item=allTag[i];
          if(item.name==value){
             names.push(item);
          }
     }
     return names;
 }
 var ary=getElementsByName("he1")
</script>

2.【修改DOM元素的样式】

1).元素.style.xxx 修改(获取)当前元素的行内样式

操作的都是行内样式,哪怕把样式写在样式表中 只要没有写在行内上,也获取不到

2).元素.className

存储/操作元素的样式类名,基于样式类的管理给予其不同的样式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .active{
            color:red;
            background: pink;
        }
    </style>
</head>
<body>
     <div class="box">
         <h2 class="title"></h2>
        <ul class="item" id="itembox">
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
        </ul>
     </div>
    <script>
        var itembox=document.getElementById("itembox"),
            lilist=itembox.getElementsByTagName("li");     
        // console.dir(itembox); =>是一个DOM元素对象
        // console.dir(lilist);=>是一个元素集合

        // ======》设置样式
        /* 方式1 */
        // itembox.style.color='Red';
        // itembox.style.backgroundColor='pink';
        //简写
        //itembox.style.cssText=`color:red; background: pink;`;

        //或者样式类
        //itembox.className='active';//这样操作会把之前的样式类名覆盖
        //itembox.className+=' active';//这样也可以 加空格区分

        itembox.classList.add('active');//向指定样式集合中新增一个样式 兼容差
    </script>
</body>
</html>

3.【给DOM元素设置内容innerHTML/innerText/value】

  • innerHTML:存储当前元素的所有内容,包含标签
  • innerText:存储当前元素的文本内容
  • value: 操作表单元素

4.【节点】

一个文档中包含的所有内容都是节点node

1663574115597(1).png

1)元素节点 ["HTML标签"]

  • nodeType:1
  • nodeName:"大写的标签名"
  • nodeValue:null

2)文本节点 [文本内容/空格/换行]

  • nodeType:3
  • nodeName:"#text"
  • nodeValue:文本内容
  • 在标准浏览器中,换行和空格也属于文本节点
 <a href="http://www.baidu.com" id="a1">百度</a>
var res=a1.childNodes[0];
console.log(res.nodeType);//3
console.log(res.nodeValue);//"百度";
console.log(res.nodeName);//"#text";

3)注释节点 “注释的内容”

  • nodeType:8
  • nodeName:"#comment"
  • nodeValue:注释的内容
 <a href="http://www.baidu.com" id="a1">
    <!--a标签 -->
    百度
 </a>
a1.childNodes; //NodeList(3) [text, comment, text]
a1.childNodes[1].nodeName;//"#comment"
a1.childNodes[1].nodeType;//8
a1.childNodes[1].nodeValue;//"a标签 "

4)文档节点 "document"

  • nodeType:9
  • nodeName:"#document"
  • nodeValue:null
document.nodeType;
document.nodeName
document.nodeValue;

5)属性节点

  • nodeType:2
  • nodeName:属性名
  • nodeValue:属性值
 <a href="http://www.baidu.com" id="a1">百度</a>
var a1=a1.getAttributeNode("href");// 获取属性节点
console.log(a1.nodeType);//2
console.log(a1.nodeName);//"href"; 属性名
console.log(a1.nodeValue);//"http://www.baidu.com"; 属性值

5.【节点的关系关系属性】

描述和获取节点之间关系的属性 已知一个节点 我们基于这些属性可以获取到任何一个跟其相关的节点

1)childNodes 集合 [NodeList]

  • 获取当前节点所有的子节点{包含各种类型}

  • 获取的是节点集合 空格换行是文本节点

  • 兼容问题

    • 在IE6-8 因为在这浏览器中,不会把空格和换行当做文本节点,不认为这是个节点
<ul id="main">
       <!-- 我是注释 -->
       <li>东方淼淼</li>
       <li>东方淼淼</li>
       <li>东方淼淼</li>
       <li>东方淼淼</li>
</ul>

<script>
console.log(main.childNodes);
// NodeList(11) [text, comment, text, li, text, li, text, li, text, li, text]
</script>	

2) children

获取当前元素所有的元素子节点,获取的是一个元素集合

但是在ie6--ie8下不兼容

会把注释节点当成元素节点 [HTMLCollection] 

console.log(main.children);

HTMLCollection(4[li, li, li, li]

3)parentNode

获取当前节点唯一的父亲节点

4) previousSibling

获取上一个哥哥节点 {只获取一个,紧挨着的这一个,而且不一定是元素节点}

main.previousSibling
//#text

5) previousElementSibling

获取其元素哥哥节点 

ie6-ie8 不兼容

   <span>1</span>
   <ul id="main">
       <!-- 我是注释 -->
       <li>东方淼淼</li>
       <li>东方淼淼</li>
       <li>东方淼淼</li>
       <li>东方淼淼</li>
   </ul>
 <script>
 console.log(main.previousElementSibling);
 //<span>1</span>
 
 <script>

6)nextSibling

获取当前节点的下一个兄弟节点

   <span id="span1">1</span>
   <ul id="main">
       <!-- 我是注释 -->
       <li>东方淼淼</li>
       <li>东方淼淼</li>
       <li>东方淼淼</li>
       <li>东方淼淼</li>
   </ul>
   <span>2</span>
   
 // console.log( span.nextSibling)

7) nextElementSibling

获取当前节点的下一个元素兄弟节点

ie6-ie8 不兼容

8) firstChild

获取其所有子节点的第一个{大儿子}

9)firstElementChild

获取当前节点的第一个元素子节点

ie6-ie8 不兼容

10)lastChild

获取当前节点的最后一个子节点{小儿子}

11)lastElementChild

获取当前节点的最后一个元素子节点{小儿子} ie6-ie8 不兼容

6.【dom 的增删改】

1)createElement:动态创建一个元素节点【元素标签】

var link=document.createElement('a');
link.href="http://www.baidu.com"; //增加href属性

2)createTextNode: 创建一个文本节点

 var text=document.createTextNode('我是文本的内容');

3) appendChild:把元素追加到一个容器的末尾

  • 容器.appendChild(元素对象) 把元素对象放置容器的末尾
  • 【context】.appendChild([元素]);
var odiv=documment.createElement("div");

document.body.appendChild(odiv);

4)insertBefore: 把一个元素插入到另一个元素的前面

  • insertBefore 把A{新增的}放到B{现有的}的前面  
(B.parentNode).insertBefore(A,B)
  • 把新元素添加到原有元素的前面
【context】.insertBefore(newEle,oldEle);

5) cloneNode:把某一个节点进行克隆

  • 【ele】.cloneNode(false/true);浅克隆: 只是克隆了一个节点,里面的内容还有样式都没克隆
  • 【ele】.cloneNode(true);深克隆:把节点包含的所有内容进行克隆

6)removeChild:移除某个节点

【context】.removeChild(ele);

7)set/get/removeAttribute

设置/获取/删除 当前元素的某一个自定义属性

  • setAttribute
  • getAttribute
  • removeAttribute
<div id="box"></div>
//=======>  setAttribute  设置
box.setAttribute("index",1);
box.getAttribute("index");
box.removeAttribute("index");
//========> 基于键值对操作
// 设置
box["aa"]=22;
// 获取
box["aa"]
//移除
delete box[aa];
  • 基于键值对方式 增删改:修改当前对象的堆内存空间完成的(在堆内存空间可以看到)

  • 基于Attribute dom方法增删改,是修改html结构来完成的(此种方法设置的在结构上可以看到)

以上两种方式不能混用

三、【相关封装】

1. 自己手动封装一个获取节点下面的所有子元素,要求考虑兼容性。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
       
    </style>

</head>
<body>
   <span id="span1">1</span>
   <ul id="main">
       <!-- 我是注释 -->
       <li>东方淼淼</li>
       <li>东方淼淼</li>
       <li>东方淼淼</li>
       <li>东方淼淼</li>
   </ul>
   <span>2</span>
</body>
</html>
<script>
 function children(element){
     var nodeLists=element.childNodes;
     var result=[]
      for (var i = 0; i < nodelist.length; i++) {
             var itemNode=nodelist[i];
             if(itemNode.nodeType===1){
                 result.push(itemNode);
          	}
      }
     return result;
 }
 console.log(children(main));
</script>

2. 自己手动封装一个perviousElementSibling,要兼容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
       
    </style>

</head>
<body>
   <span id="span1">1</span>
   <ul id="main">
       <!-- 我是注释 -->
       <li>东方淼淼</li>
       <li>东方淼淼</li>
       <li>东方淼淼</li>
       <li>东方淼淼</li>
   </ul>
   <span id="span2">2</span>
</body>
</html>
<script>
 function previousElmentSibling(ele){
     var pre=ele.previousSibling;
     while(pre&&pre.nodeType!==1){
         pre=pre.previousSibling;
     }
     return pre;
 }

 previousElmentSibling(span2)
</script>

3.获取当前节点的所有元素哥哥节点 兼容所有浏览器

       JQ中的prevAll这个方法 就是干这个的

方式1

function prevAll(node){
            var result=[];
            // 获取父节点
            var parent=node.parentNode;
            //获取子节点 自己和他所有子节点
            var nodelist=parent.childNodes;
            // 循环所有节点 元素类型是我们想要的 并且找到当前节点后就不在循环了
            for(var i=0;i<nodelist.length;i++){
                var item=nodelist[i];
                if(item===node){
                    // 找到的是自己
                    break;
                }
                // 找的不是自己 我们把元素节点存储起来
                if(item.nodeType===1){
                    result.push(item);
                }
            }
            return result;
 }

方式2

 function prevAll2(node){
            var prev=node.previousSibling;
            var result=[];
            while(prev!== null){
                if(prev.nodeType===1){
                    result.unshift(prev);
                }
                prev=prev.previousSibling;
            }
            return result;
}

4获取所有的弟弟节点

方式1

  function nextAll(node){
            var nodelist=node.parentNode.childNodes;
            var result=[];
            //倒着从最后一项开始循环
            for(var i=nodelist.length-1;i>=0;i--){
                var item=nodelist[i];
                if(item===node){
                    // 找到的是自己
                    break;
                }
                // 找的不是自己 我们把元素节点存储起来
                if(item.nodeType===1){
                    result.unshift(item);
                }
            }
            return result;
}

方式2

 function nextAll2(node){
            var result=[];
            var next=node.nextSibling;
            while(next!==null){
                if(next.nodeType===1){
                    result.push(next);
                }
                next=next.nextSibling;
            }
            return result;
}

5.获取所有的兄弟节点

方式1

function silbings(node){
        //所有的儿子中 一定包含了我和我的兄弟们
        var nodelist=node.parentNode.childNodes;
        var result=[];
        //依次遍历每一项节点 除了非元素和我本身除外 其余的存储起来
        for (var i = 0; i < nodelist.length; i++) {
            var item=nodelist[i];
            if(item.nodeType!==1||item===node){
                continue;
            }
            result.push(item);
        }
        return result;
}

方式2

 function silbings2(node){
        // 分别调用两个方法所有的哥哥和所有弟弟 就是所有的兄弟
        return prevAll2(node).concat( nextAll2(node));   
  } 

6.获取当前节点的索引 他在所有兄弟中的排行

function index(node){
        //他有几个哥哥 那么它的索引就是几
        return prevAll2(node).length;
 }