第九章 DOM

75 阅读7分钟

一、DOM:Document Object Model(文档对象模型)

1.DOM树:

2.回流和重绘

  1. 重绘(repaint)

当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要UI层面的重新像素绘制,因此损耗比回流少

常见的重绘操作有:

  • 改变元素颜色
  • 改变元素背景色
  • 改变字体颜色
  • ……
  1. 回流(reflow)

又称重排(layout);当元素恶的尺寸、结构或者触发某种属性是,浏览器会重新渲染页面,称为回流。此时,浏览器需要重新计算,计算出页面布局,损耗比较大。

常见的回流操作有:

  • 页面初次渲染
  • 浏览器窗口大小的改变
  • 元素尺寸/位置/内容发生改变
  • 字体尺寸
  • 添加或者删除可见的DOM元素
  • ……

注意:回流时必定会引起重绘,重绘不一定会触发回流;重绘损耗小,回流代价高

二、DOM操作

1、获取元素

1.1 通过id名获取

document.getElementById(idname)

1.2 通过标签名获取

document.getElementByTagName()

1.3 通过类名获取(不兼容ie6~8)

document.getElementsByClassName(className)   // 返回一个集合(类数组对象)  从整个文档获取
element.getElementsByClassName(className)   // 从element的后代中获取

1.4 根据name属性值获取

正常规范中,我们只会给表单元素取name值,如果给其他元素设置name,在ie9以下版本浏览器不兼容,是获取不到的,所以为了防止事件的发生,只给表单元素用name,非表单元素不使用name属性名。

document.getElementsByName()  //返回集合  只有document才有该方法
  • 通过css选择器获取

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

document.querySelector(选择器)   //选择器第一个满足选择器条件的
document.querySelectorAll(选择器) //选择所有满足选择器条件的,返回nodeList(类数组对象)
element.querySelector(选择器)
element.querySelectorAll(选择器)
  • document.head 获取Head元素对象
  • document.body 获取body元素对象
  • document.documentElement 获取html元素对象
  • 【context】.getAttribute 获取行内属性对应的值

1.5 获取一屏幕的宽度或者高度,兼容所有的浏览器

// 获取一屏幕的高度
var vH = document.documentElement.clientHeight || document.body.clientHeight;
// 偶去一屏幕的宽度
var vW = document.documentElement.clientWidth || document.body.clientWidth;

1.6 练习题

        // let all = document.getElementsByTagName('*'); //通配
        //先获取所有的元素;然后在从这些元素里边筛选出我们要的目标元素
        //元素 getAttribute('xxx')  获取元素的行内属性xxx对应的值 




//场景:找出当前目录下属性名为‘name’且属性值为‘hehe’的标签


  /*    var aaa = document.querySelectorAll('[name="hehe"]');
        console.log(aaa);
         */
        /* var res = [];
        for (var i = 0; i < all.length; i++) {
            var aaa = all[i].getAttribute('name')
            if (aaa == "hehe") {
                //证明当前元素是我们想要的
                res.push(all[i])
            }
        }
        console.log(res); */
        function search_attribute(key,value) {
            let all = document.getElementsByTagName('*');
            var res = [];
            for (var i = 0; i < all.length; i++) {
               // var aaa = all[i].getAttribute('key')
                if (all[i].getAttribute(key) == value) {
                    //证明当前元素是我们想要的
                    res.push(all[i])
                }
            }
            return console.log(res);
            

        }
        search_attribute('name',"hehe")

1.7 id小妙招

  • 直接把id变成变量去用的时候,可以获取相应的id元素。(浏览器的机制)
<div id="box1">111</div>
<script>
  console.log(box1)
</script>
   

2、节点

  • 元素节点:1

  • 属性节点:2

  • 文本节点:3

  • 注释节点:8

  • 文档节点:9

2.1 元素节点

  • nodeType:1

  • nodeName:大写的标签名

  • nodeValue:null

<div id="box">百度</div>

<script>
  console.log(box.nodeName)//DIV
</script>

2.2 属性节点

  • nodeType:2

  • nodeName:属性名

  • nodeValue:属性值

getAttributeNode()方法从当前元素中通过名称获取属性节点。

 <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"; 属性值

2.3 文本节点

  • 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";

2.4 注释节点

  • 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标签 "

2.5 文档节点

  • nodeType:9

  • nodeName:"#document"

  • nodeValue:null

document.nodeType;
document.nodeName
document.nodeValue;

3、节点之间关系的属性

节点类:

parentNode 父节点

childNodes 所有子节点的集合

firsrChild 第一个子节点

lastChild 最后一个子节点

previousSibling 上一个兄弟节点

nextSibling 下一个兄弟节点

元素类:

children 所有子元素的集合

firstElementChild 第一个子元素 IE9+

lastElementChild 最后一个子元素 IE9+

previousElementSibling 上一个兄弟元素 IE9+

nextElementSibling 下一个兄弟元素 IE9+

3.1 parentNode

  • 获取当前节点唯一的父节点
<div class="box333">
  <div>
  <!-- 111 -->
  <div id="box">111</div>
</div>
</div>

<script>
    console.log(box.parentNode)
</script>

3.2 childNodes

  • 获取当前节点所有的子节点
 <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>

3.3 firstChild

  • 获取被选节点的第一个子节点
 <ul id="main">
    <!-- 我是注释 -->
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
  </ul>

  <script>
    console.log(main.firstChild);//#text
  </script>

3.4 lastChild

  • 获取被选子节点的最后一个子节点
 <ul id="main">
    <!-- 我是注释 -->
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
  </ul>

  <script>
    console.log(main.lastChild);//#text
  </script>

3.5 previousSibling

  • 获取上一个哥哥节点
 <ul id="main">
    <!-- 我是注释 -->
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
  </ul>
  <script>
    console.log(main.previousSibling);//#text
  </script>

3.6 nextSibling

  • 获取下一个弟弟节点
<ul id="main">
    <!-- 我是注释 -->
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
  </ul><!-- 1111 -->
  <script>
    console.log(main.nextSibling);//<!-- 1111 -->
  </script>

3.7 children

  • 获取当前元素的全部子节点,但是在ie6-ie8下不兼容
<ul id="main">
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
  </ul>
  <script>
    console.log(main.children);//HTMLCollection(4) [li, li, li, li]
  </script>

3.8 firstElementChild

  • 获取当前节点的第一个元素子节点 ie6-ie8不兼容
 <ul id="main">
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
  </ul>
  <script>
    console.log(main.firstElementChild); 
  </script>

3.9 lastElementChild

  • 获取当前节点的最后一个元素子节点 ie6-ie8不兼容
<ul id="main">
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!111</li>
  </ul>
  <script>
    console.log(main.lastElementChild); 
  </script>

3.10 previousElementSibling

  • 获取上一个哥哥元素节点 ie6-ie8不兼容
  <div>1111</div>
  <ul id="main">
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!111</li>
  </ul>
  <script>
    console.log(main.previousElementSibling); 
  </script>

3.11 nextElementSibling

  • 获取下一个弟弟元素节点 ie6-ie8不兼容
 <div>1111</div>
  <ul id="main">
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!</li>
    <li>选择珠峰的,都是明智的!111</li>
  </ul>
  <script>
    console.log(main.nextElementSibling); 
  </script>

练习题

  • 自己动手封装一个获取节点下面的所有子元素,要求考虑兼容性
<!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<nodeLists.length;i++){
        nodeLists[i].nodeType===1?result.push(nodeLists[i]):null;
     }
     return result;
 }
 console.log(children(main));
</script>
  • 自己封装一个previousElementSibling,考虑兼容性
<!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;
 }

  console.log(previousElmentSibling(span2))
</script>

三、DOM的增删改

1、createElement:创建一个元素

document.createElement("div");

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

var otext=document.createTextNode("哈哈!");

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

  • 代码:[context].appendChild([元素]);
 <div id="id1"></div>
  <script>
    var odiv = document.createElement("div");
    id1.appendChild(odiv);
    console.log(id1)
  </script>

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

  • 首先要指定一个父节点
    var inserttedNode = 父节点.insertBefore(要插入的节点,差在这个节点之前)
    例:var insertedNode = parentNode.insertBefore(newNode, referenceNode)
  • newNode:将要插入的节点
  • referenceNode:被参照的节点(即要插在该节点之前)
  • insertadNode:插入后的节点
  • parentNode:父节点
<div id="id1">
  <p class="p1" id="p1">这是P1</p>
</div>
<script>
  var div = document.getElementById('id1');
  var p1 = document.getElementById('p1');
  var odiv = document.createElement("div");
  var returnDom = div.insertBefore(odiv, p1);
console.log(div)
</script>

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

  • [ele].cloneNode(); //浅克隆:只是克隆一个节点,里面的内容和样式都没克隆
  • [ele].cloneNode(ture); //深克隆:把节点包含的所有内容进行克隆
 <div id="id1">
    <p class="p1" id="p1">这是P1</p>
  </div>
  <script>
    var res = id1.cloneNode();
    var res2 = id1.cloneNode(true)
    console.log(res)
    console.log(res2)
  </script>

6、removeChild:移除某个节点

  • [context].removeChild(ele);
<div id="id1">
    1111
    <p class="p1" id="p1">这是P1</p>
  </div>
  <script>
    id1.removeChild(p1);
    console.log(id1)
  </script>

7、set/get/removeAttribute

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

  • setAttribute
  • getAttribute
  • removeAttrbute
box.setAttribute("index", 1);
box.getAttribute("index");
box.removeAttribute("index");
console.log(box)
// 设置
// box["aa"] = 22;
// 获取
// box["aa"]
//移除
// delete box["aa"];
  • 基于键值对方式 增删改:修改当前对象的堆内存空间完成的(在堆内存空间可以看到)
  • 基于Attribute dom方式增删改,是修改html结构来完成的(此种方法设置在结构上可以看到)
  • 以上两种方式不能混用

题目

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    var str = "http://www.zhufengpeixun.cn?name=lili&age=18#123"
    /* 
     search: "?name=lili&age=18"
     hash: "#123"
    */

    function queryParams(str) {
      var a = document.createElement("a");
      a.href = str;
      var obj = {};
      console.log(a.search); //?name=lili&age=18
      console.log(a.hash); //#123
      //[name=lili,age=18]
      var search = a.search.substr(1);
      console.log(search);//name=lili&age=18
      obj.hash = a.hash ? a.hash : null;
      if (search) {
        var searchAry = search.split("&");
        console.log(searchAry)// ['name=lili', 'age=18']
        for (var i = 0; i < searchAry.length; i++) {
          var itemAry = searchAry[i];//name=lili  age=18
          var item = itemAry.split("=");
          console.log(item)//['name', 'lili']['age', '18']
          var key = item[0];
          var value = item[1];
          obj[key] = value;
        }
      }
      return obj;
    }
    var dd = queryParams(str);
    console.log(dd)
  </script>
</body>

</html>