操作节点

获取节点的常用的两种方式

1:利用DOM提供的方法获取元素,这种方式获取节点逻辑性不强且很繁琐。(比较老的用法不太推荐)

ducument.getElementById();

ducument.getElementByTagName();

ducument.querySelector()等

2:利用节点层级关系通过节点的属性来获取元素

即利用父子兄弟节点关系来获取元素,这种方式获取节点更加简单,逻辑性强但是兼容性稍差

节点基础概念在上面

---------------------------------案例准备
<!-- 准备一系列节点 -->
<div id="box" class="container">
    <!-- 你好世界 -->
    <span>hello</span>
    hello world
</div>

节点层级

父子节点

根据DOM树,可以把节点划分为不同的层级,常见的是父子兄弟层级,

  • 父级节点

    格式:元素.parentNode

    作用:返回该元素的父节点(亲爸爸),如果指定的节点没有父级节点则返回null

    父节点:大部分都是元素,有特殊的document

console.log(div.parentNode.parentNode.parentNode);   
//找到document后的parentNode返回null
  • 父元素节点

    格式:元素.parentELement

    返回元素的父元素节点

  • 子节点

    • 格式:元素.childNodes

    作用:返回包含在指定节点内的子节点的伪数组,该集合是即时更新的集合

    注意:返回值里包含了所有的子节点,包括元素节点,文本节点等(文本节点包括文字,空格,换行),如果想要通过这个属性只获取元素子节点需要再做处理(遍历返回值,判断每一项的nodeType),所以不推荐使用该属性获取子节点

    • 格式:元素.firstChild

    作用:返回第一个子节点,没有则返回null,同样是包含所有的类型的节点

    • 格式:元素.lastChild

    作用:返回最后一个子节点,没有则返回null,同样是包含所有类型的节点

    以上三个属性不是值返回子元素节点,所以不推荐使用

    使用typeof 检测节点类型 返回的是object

    • 格式:元素.children 这是一个只读属性,没有兼容性问题

      作用:元素.children属性只返回元素节点内的所有的子元素节点(伪数组),其余类型的节点不会返回

      如果元素内没有子元素节点则返回的是[]

    • 格式:元素.firstElementChild 有兼容性为题,ie9以上才支持

      作用:返回第一个子元素,找不到则返回null

    • 格式:元素.lastElementChild 有兼容性为题,ie9以上才支持

      作用:返回最后一个子元素,找不到则返回null

    获取到元素节点后我们可以对元素进行操作

    总结:

    在实际开发中使用:元素.children属性(没有兼容性问题且直接返回子元素节点)

    返回所有子元素节点:元素.children

    返回第一个子元素节点 :元素.children[0]

    返回最后一个子元素节点:元素.children[parenNode.children.length-1]

兄弟节点
  • 元素.nextElementSibling 有兼容性问题,ie9以上才支持

    返回当前元素下一个兄弟节点,找不到则返回null。 (弟弟元素)

  • 元素.previousElementSibling 有兼容性问题,ie9以上才支持

    翻译:previous 在…以前 sibling兄弟姐妹

    返回当前元素上一个兄弟元素节点,找不到则返回null。(哥哥元素)

  • 元素.nextSibling (弟弟节点)

    返回下一个兄弟节点,包含元素节点或者文本节点等

  • 元素.previousSibling (哥哥节点)

    返回元素的上一个兄弟节点,包含元素节点或者文本节点

属性节点
  • 格式:元素.attributes

    返回该元素的所有属性节点

    属性节点是元素身上设置的属性,每一个属性都是一个节点

创建添加节点 ***

想要在页面中通过js添加一个新的节点需要分两步:创建节点,添加节点

  • document.creatElement(‘标签名’)

    作用:创建指定标签名的html元素

    返回值:返回一个元素节点

  • 创建文本节点

    document.creatTextNode(‘文本内容’)

  • 添加节点

    父节点.appendChild(要添加进去的子节点)

    作用:在父节点的末尾添加一个子节点,类似于class中的::aftetr伪元素

var li=document.creatElement('li');
var ul=document.querySelector('ul');
ul.appendChild(li);
在获取到的ul标签里添加一个新创建的li标签

---------

var div=document.createElement('div')
div.innerText='66';
div.classList.add('77');
div.id='88';
box.appendChild(div)
console.log('>>>>',div)
      var img=new Image();
      img.src='../imgs/cat.png';
      img.onload=function(){
          console.log('图片加载成功');
      }
      img.onerror=function(){
        console.log('图片加载失败');
      }
      document.body.appendChild(img)
  • 在父元素中指定元素的前面添加子节点,类似于class中的::before伪元素

    父节点.insertBefore(子节点,指定元素)

ul.insertBefore(li,ul.children[0])
在ul的第一个子元素前添加新的节点li
删除节点
  • 父节点.removeChild(子节点)

作用:将子节点从父节点里移除

返回值是被删除的节点

该方法可以在创建的节点里删除,也可以直接从页面元素里删除父节点中的一个子节点

var ul=document.querySelector('ul');
ul.removeChild(ul.children[0]);  //删除ul中的第一个子元素
  • 节点.remove()

    作用:把自己从父节点中移除

替换节点

用一个节点替换一个已经存在的节点,可以直接替换页面元素,可以替换我们自己创建的节点

格式:父节点.replaceChild(新节点,旧节点);

作用:在父节点下,用新节点替换旧节点,所有的节点都可以替换,但是不能用元素节点替换属性节点

<!-- 准备一系列节点 -->

<div id="box" class="container">
    <!-- 你好世界 -->
    <span>hello</span>
    hello world
</div>

----js

 var p=document.createElement('p');
 p.innerText='我是新来的';
 box.replaceChild(p,box.firstElementChild);  
 //将div#box的第一个元素节点替换成新创建的p标签
复制节点(克隆节点,拷贝节点)

作用:把某一个节点复制一份一模一样的

格式:要被复制的节点.cloneNode(参数)

返回值:返回被复制节点的一个副本

参数:参数为空或者false,是浅拷贝,即只拷贝节点本身,不拷贝节点里的子节点等内容

参数为true时,则是深拷贝,会复制节点本身以及节点里子节点等内容

<!-- 准备一系列节点 -->
<div id="box" class="container">
    <!-- 你好世界 -->
    <span>hello</span>
    hello world
</div>
-------------
  var newBox=box.cloneNode();//只赋值了box元素,不包含元素的内容
  var newBox=box.cloneNode(true);
  console.log(newBox);
  document.body.appendChild(newBox)

三种动态创建元素方式及区别

  • document.write()

  • element.innerHTML

  • document.createElement()

区别:

  • document.write

    是直接将内容写入页面的内容流,但是文档流执行完毕,document.write会导致页面全部重绘

        <div id="box">
            布布妈妈
        </div>
        <button>write写入</button>
        -----------------------------------
        var p=document.createElement('p');
        p.innerText='希望赶快好起来';
        console.log(p)
        box.appendChild(p)
        var btn=document.querySelector('button');
        ----------------页面绘制完后点击按钮使用write写入内容,会导致页面的全部重绘
        btn.onclick=function(){
            document.write('<p>郑州加油</p>')
        }
  • innerHTML 是将内容写入某个DOm节点,不会导致页面的全部重绘

    innerHTML 创建多个元素效率更高(不要以拼接字符串的形式,而要采取数组形式拼接),结构稍微复杂

  • creatElement() 创建多个元素效率稍微低一点点,但是结构更加清晰

    创建节点,插入节点会频繁操作DOM,页面浪费性能最大的就是操作dom,所以要尽量减少操作DOM

  • 总结:在不同的浏览器下,innerHTML以数组形式效率要比creatElement高

        var box1=document.querySelector('#box1');
        // 方法1: innerHTML以拼接字符串的形式来创建添加元素,一次次的创建添加这样的效率很低
        for(var i=0;i<=100;i++){
            box1.innerHTML+='<span>你好<span>'
        }
        
        // 方法2:  innerHTML 先将要创建的元素通过数组将字符串拼接好,再一次性的使用innerHTML创建添加,效率最高
        var arr=[];
        for(var i=0;i<=100;i++){
            arr.push('<span>你好<span>')
        }
        console.log(arr)
        box1.innerHTML=arr.join('')
        
        // 方法3 :以document.creatElement()创建元素  效率比方法2要低
        for(var i=0;i<=100;i++){
            var a=document.createElement('a');
            a.innerHTML='你好';
            box1.appendChild(a)
        }

    案例作业:

        <table>
            <thead>
                <tr>
                    <th>姓名</th>
                    <th>年龄</th>
                    <th>性格</th>
                </tr>
            </thead>
            <tbody>
                <!-- 动态添加表格数据 ,原表格无数据-->
            </tbody>
        </table>  
        
        --------------------------
        
           var userList=[
                {name:'孙悟空',age:1800,gender:'男'},
                {name:'猪八戒',age:1800,gender:'男'},
                {name:'沙僧',age:1800,gender:'男'},
                {name:'唐三藏',age:1800,gender:'男'}
            ]
            var str='';
            userList.forEach(function(item){
                str+=`<tr>
                            <td>
                                ${item.name}
                            </td>
                            <td>
                                ${item.age}
                            </td>
                            <td>
                                ${item.gender}
                            </td>
                        </tr>`
            })
    
            var tbody=document.querySelector('tbody')
            tbody.innerHTML=str;
            console.log(str)
            console.log(div)
  • 文档碎片(筐)

    我们可以使用js创建一个文档碎片节点,它的作用是可以承载(其他)节点,当我们把筐向页面元素里添加的时候,筐是不会进入页面的,是把筐里的内容倒进去

    格式:document.createDocumentFragment()

    返回值:一个文档碎片节点

        ----
          <table>
            <thead>
                <tr>
                    <th>姓名</th>
                    <th>年龄</th>
                    <th>性格</th>
                </tr>
            </thead>
            <tbody>
             <!-- 动态增加表格数据, 保留原数据-->
             <tr>
                <td>bubu</td>
                <td>2</td>
                <td>nan</td>
              </tr>
            </tbody>
        </table>
        -----------
            var userList=[
                {name:'孙悟空',age:1800,gender:'男'},
                {name:'猪八戒',age:1800,gender:'男'},
                {name:'沙僧',age:1800,gender:'男'},
                {name:'唐三藏',age:1800,gender:'男'}
            ]
            //创建一个文档碎片节点 筐
            var frg=document.createDocumentFragment()
            userList.forEach(function(item){
               var tr=document.createElement('tr')
               tr.innerHTML= `<td>
                                ${item.name}
                            </td>
                            <td>
                                ${item.age}
                            </td>
                            <td>
                                ${item.gender}
                            </td>`
                //把每次循环创建好的tr标签添加到筐里
                frg.appendChild(tr)
            })
           console.log(frg)
           var tbody=document.querySelector('tbody')
           //将筐插入进tbody,实际插入的是筐内的内容,筐不进入
           tbody.appendChild(frg)
offset

offset翻译为偏移量,通过offset系列相关属性可以得到该元素的位置,大小等

offset系列常用属性,返回数值不能带单位

  • 获取元素自身的大小(宽高)

    • 元素.offsetWidth

    • 元素.offsetHeight

    返回值:得到元素的 内容+padding+border 区域的宽/高度

    返回值不带单位

    元素设置display:none后,返回值为0

  • 获取元素距离带有定位父元素的位置

    • E.offset.offsetParent

      返回元素带有定位的父元素,如果没有父元素或父元素没有开启定位则返回body

      (元素本身可以没有开启绝对定位,假设我们给这个元素开启绝对定位时,他会根据谁来定位,返回的offsetParent就是谁)

    • E.offset.offsetTop

      返回元素相对带有定位的父元素,上方的偏移量,

      如果没有父元素或父元素没有开启定位就相对于body

    • E.offset.offsetLeft

      返回元素相对带有定位的父元素左边框的偏移量,

      如果没有父元素或父元素没有开启定位就相对于body

    当我们在css中给元素设置定位时,采用的时right和bottom的时候,会自动给我们转换为left和top的值返回

  • offset和style的区别

    • offset可以得到任意样式表中样式值

    • offset获取到的数值没有单位

    • offsetWith和offsetHeight 为可见框尺寸 包含 conten( width height) padding border

    • offsetWidth,offsetHeight 属性为只读属性,不能赋值

    • style 只能得到行内样式表中的样式值

    • shyle.width 返回的是带有单位的字符串

    • style.width 返回的是不包含 padding 和border,可读写属性

      所以,我们想要获取元素的大小位置,使用offset更合适,修改元素的值,使用style改变

client

client(翻译 客户端)系列相关属性可以获取元素可视区域的相关信息,可以动态得到该元素的边框大小,元素大小

  • 元素.clientWidth

返回值:自身content(width)+padding 的宽度,不包含边框. ???自己获取时包含了border??

返回数值不带单位

获取浏览器窗口的尺寸:
- BOM级别的获取
	window.innerWidth
	window.innerHeight
		拿到的是包含浏览器滚动条的尺寸
- DOM 级别的获取
	其实就是获取页面的那一部分的尺寸
  通过DOM节点获取浏览器窗口尺寸,不包含滚动条
  本质是获取的时html的width
  格式:document.documentELement.clientWidth;
  • 元素.clientHeight

    返回值:自身包括content(height)+padding 的高度,不包含边框

    返回数值不带单位

  • 元素.clientTop

    返回元素上边框的大小 boder-top

  • 元素.clientLeft。

    返回元素左边框的大小 border-left