Go转JS-第六弹(DOM)

311 阅读8分钟

JavaScript中的DOM学习

1、DOM

  • DOM -> Document Object Model ,文本对象模型
  • DOM以树状结构表示整个HTML文档
dom.webp

2、延迟执行(window.onload)

  • window.onload:用于在网页加载完毕后立刻执行的操作,即当 HTML 文档加载完毕后,立刻执行某个方法。
    window.onload = function(){
        // 希望执行的操作,如访问元素节点等
    }

3、节点与操作节点

3.1、访问元素节点的常用方法

  • document.getElementById():通过id获取元素
  • document.getElementsByTagName():通过标签名获取元素数组
  • document.getElementsByClassName():通过类名获取元素数组
  • document.querySelector():通过选择器获取元素
  • document.querySelectorAll():通过选择器获取元素数组

3.1.1、getElementById

   <p id = "kun">喝茶吗?坤哥🍵</p>
   var k = document.getElementById('kun')

3.1.2、getElementsByTagName和getElementsByClassName

  <div id="box1">
    <p></p><p></p><p></p>
  </div>
  <div id="box2">
    <p></p><p></p><p></p><p></p><p></p>
  </div>
   var p = document.getElementsByTagName('p')
   console.log(p)    //  获取了8个P节点
   // 若只需要box2中的p节点
   var box2 = document.getElementById('box2')
   var ps_box2 = box2.getElementsByTagName('p')
   console.log(ps_box2)    

3.1.3、querySelector和querySelectorAll

  <div id="box2">
    <p></p><p></p><p></p><p></p><p></p>
  </div>
    var p1 = document.querySelector("#box2 p")
    console.log(p1)  // <p>只</p>  , 只获取符合的第一个
    var p2 = document.querySelectorAll("#box2 p")
    console.log(p2)  // 获取5个p节点的数组
方法兼容性

getElementById

IE6

getElementsByTagName

IE6

getElementsByClassName

IE9

querySelector

IE8部分兼容、IE9完全兼容

querySelectorAll

IE8部分兼容、IE9完全兼容

3.2、节点的关系

节点的关系.png

  • 节点 = 元素节点 + 文本节点(包括空白文本节点)

3.2.1、排除文本节点的干扰

关系考虑所有节点只考虑元素节点(IE9兼容)

子节点

childNodes

children

父节点

parentNode

parentNode

第一个子节点

firstChild

firstElementChild

最后一个子节点

lastChild

lastElementChild

前一个兄弟节点

previousSibling

previousElementSibling

后一个兄弟节点

nextSibling

nextElementSibling

3.2.2、示例

  <div id="box">
    <p></p>
    <p id="para"></p>
    <p></p><p></p><p></p>
  </div>
    var b = document.getElementById('box')
    var p = document.getElementById('para')
    console.log(b.childNodes)  // NodeList(9) [text, p, text, p#para, text, p, p, p, text]  div标签与p标签中间的空白换行也算是节点(空白文本节点)
    console.log(b.children)    // HTMLCollection(5) [p, p#para, p, p, p]
    console.log(p.previousSibling)  // #text 前一个兄弟节点为空白文本节点
    console.log(p.previousElementSibling)  // <p>只</p>

3.2.3、常用的节点函数(兼容IE6)

  • 获取所有的子元素节点(兼容IE6)
    function getChildren(node){
        var arr = []
        for (var i = 0 ;i < node.childNodes.length;i++){
            if (node.childNodes[i].NodeType == 1){
                arr.psuh(node.childNodes[i])
            }
        }
        return arr
    }
  • 获取前一个元素兄弟节点(兼容IE6)
    function getElementPreSibling(node){
        var o = node
        // 确保node节点有前一个节点
        while (o.previousSibling != null){
            if (o.previousSibling.NodeType == 1){
                return o.previousSibling
            }
            // 让o称为它的前一个节点
            o = o.previousSibling
        }
    }
  • 获取所有的元素兄弟节点(兼容IE6)
    function getAllElementSibling(node){
        var o = node
        var pre = []
        var next = []
        while (o.previousSibling != null){
            if (o.previousSibling.NodeType == 1){
                pre.unshift(o.previousSibling)
            }
            // 让o称为它的前一个节点
            o = o.previousSibling
        }
        
        o = node 
        while (o.nextSibling != null){
            if (o.nextSibling.NodeType == 1){
                next.push(o.nextSibling)
            }
            // 让o称为它的前一个节点
            o = o.nextSibling
        }
        
        // 合并数组
        return pre.concat(next)
    }

3.3、改变节点中的内容

  • innerHTML属性:以HTML语法设置节点中的内容
  • innerText属性:以纯文本的形式设置节点中的内容
  <div id="box">
    <p></p><p id="para"></p><p></p><p></p><p></p>
  </div>
 <script>
    var b = document.getElementById('box')
    b.innerHTML = "<p>坤</p>"
    // b.innerText = "坤"
  </script>

注意:inner会把新内容覆盖旧内容,而不是插入。

3.4、改变元素节点中的css样式

  • 节点.style.属性 = '属性值' -- 属性值的写法与css中的写法保持一致
    <div id="box" wid></div>
    var b = document.getElementById('box')
    b.style.color = 'red'

3.5、改变元素节点中的HTML属性(标准W3C属性)

  • 节点.属性 = '属性值'
    <img id="box" src="" alt="坤哥没了">
    var b = document.getElementById('box')
    b.src = 'url'

3.6、改变元素节点中的HTML属性(非标准W3C属性)

  • 节点.setAttribute('属性','属性值'):设置属性
  • 节点.getAttribute('属性'):获取属性
    <div id="box"></div>
    var b = document.getElementById('box')
    b.setAttribute('data-n',10)
    var d = b.getAttribute('data-n')
    console.log(d)  // 10

3.7、创建节点

  • document.createElement()方法创建一个指定的tagName的HTML元素
  • 新创建的节点是“孤儿节点”,意味着并没有挂载到DOM树上,无法看见它
    // 创建一个div节点
    var oDiv = document.createElement('div')

3.8、插入节点

  • appendChild():将某个节点插入到父节点的内部,成为它的最后一个子节点
  • insertBefore():将某个节点插入到父节点的内部,成为某个兄弟节点的前节点
    父节点.appendChild(某个节点)
    父节点.insertBefore(某个节点,兄弟节点)

3.9、移动节点

  • 原理:一个节点不能同时位于DOM树的两个位置。故可以使用appendChild()insertBefore()来进行移动
    新父节点.appendChild(某个节点)
    新父节点.insertBefore(某个节点,兄弟节点)

3.10、删除节点

  • removeChild():从DOM树中删除一个子节点
  • 节点不能删除自己,只能由父节点进行删除
    父节点.removeChild(删除节点)

3.11、克隆节点(深浅克隆)

  • 克隆出的节点是孤儿节点
  • 浅克隆:cloneNode()
  • 深克隆:cloneNode(true)
    var 孤儿节点 = 克隆节点.cloneNode()
    var 孤儿节点 = 克隆节点.cloneNode(true)

4、事件

  • 用户与网页的交互动作称为事件

4.1、onxxx事件监听

  • 常见的鼠标事件监听
  • 常见的键盘事件监听
  • 常见的表单事件监听

4.1.1、常见的鼠标事件监听

事件名事件描述

onclick

鼠标单击某个对象

ondblclick

鼠标双击某个对象

onmousedown

鼠标按键在某个对象上被按下

onmousedup

鼠标按键在某个对象上被松开

onmousemove

鼠标按键在某个对象上移动

onmouseenter

鼠标进入某个对象(不冒泡)

onmouseover

鼠标进入某个对象(冒泡)

onmouseleave

鼠标离开某个对象

onmousewheel

鼠标滚动事件

    <div id = "box"></div>
    var oBox = document.getElementById('box')
    oBox.onclick = function(){
        console.log('我是onclick')
    }

4.1.2、常见的键盘事件监听

事件名事件描述

onkeypress

键盘按键被按下(无法识别系统按钮,如:箭头键)

onkeydown

键盘按键被按下(优于onkeypress)

onkeyup

键盘按键被松开

    <input type="text" id="nameField">
    var nameField = document.getElementById('nameField')
    // 当按下`shift`等按键时,无效 -- 无法识别
    nameField.onkeypress = function(){
        console.log('onkeypress')
    }

4.1.3、常见的表单事件监听

事件名事件描述

onchange

输入框输入完内容后,输入框失焦后触发

oninput

输入框中输入时就会触发(IE9以下不支持)

onfocus

某元素获得焦点(点击/tab)

onblur

某元素失去焦点

onsubmit

表单被提交

onreset

表单被重置

  <form id="myform">
    <p>
      姓名:
      <input type="text" name="nameField">
    </p>
  </form>
    var myform = document.getElementById('myform')
    var nameField = myform.nameField;
    nameField.onchange = function(){
      console.log("onchange")
    }
    nameField.oninput = function(){
      console.log("input")
    }

4.2、addEventListener()监听

  • 节点.addEventListener('事件名',function(){},布尔值)
  • 事件名:不加on
  • 布尔值:true监听捕获阶段,false监听冒泡阶段
    var myform = document.getElementById('myform')
    myform.addEventListener('click',function(){
        console.log("click")
    },true)

4.3、事件传播

  • 事件传播的顺序:先从外到内,再从内到外。(捕获阶段 --> 冒泡阶段)

事件传播模型.jpeg

4.4、事件监听之间的区别

  • DOM0级事件监听:只能监听冒泡阶段,如onxxx监听
    <div id="box1">
      <div id ="box2">
        <div id="box3">
        </div>
      </div>
    </div>
    box1.onclick = function(){
      console.log("这个是onclick box1")
    }
    box2.onclick = function(){
      console.log("这个是onclick box2")
    }
    box3.onclick = function(){
      console.log("这个是onclick box3 -- 先写的")
    }
    box3.onclick = function(){
      console.log("这个是onclick box3 -- 后写的")
    }
    // 从最里层的box3开始冒泡
    这个是onclick box3 -- 后写的
    这个是onclick box2
    这个是onclick box1

注意:当给元素设置两个或者多个相同的同名事件,DOM0级后写的事件会覆盖先写的

  • DOM2级事件监听:可以监听捕获阶段和冒泡阶段,如addEventListener()监听
    <div id="box1">
      <div id ="box2">
        <div id="box3">
        </div>
      </div>
    </div>
    box1.addEventListener('click',function(){
      console.log("这个是click box1")
    },true)
    box2.addEventListener('click',function(){
      console.log("这个是click box2")
    },true)
    box3.addEventListener('click',function(){
      console.log("这个是click box3 -- 先写的")
    },true)
    box3.addEventListener('click',function(){
      console.log("这个是click box3 -- 后写的")
    },true)
    // 从最外层的box1开始捕获
    这个是onclick box1
    这个是onclick box2
    这个是click box3 -- 先写的
    这个是click box3 -- 后写的

注意:当给元素设置两个或者多个相同的同名事件,DOM2级会按照顺序执行

4.5、事件对象

  • 事件处理函数提供一个封装本次事件细节的对象参数,该对象称为事件对象
    节点.onmousemove = function(e){}

4.5.1、鼠标位置

属性属性描述

clientX

鼠标指针相对于浏览器的水平坐标

clientY

相对于浏览器的垂直坐标

pageX

相对于整张网页的水平坐标

pageY

相对于整张网页的垂直坐标

offsetX

相对于事件源元素的水平坐标

offsetY

相对于事件源元素的垂直坐标

deltaY

鼠标滚动方向,下滚动为正值,上滚动为负值

  • 演示例子:

4.5.2、charCode和keyCode属性

  • charCode属性:通常用于onkeypress事件中,表示用户输入字符的“字符码”
  • keyCode属性:通常用于onkeydownonkeyup属性中, 表示拥护按下按键的“键码”

charCode字符码:

字符字符码

0~9

48~57

A~Z

65~90

a~z

97~122

keyCode字符码:

字符字符码

0~9

48~57

a~z/A~Z

65~90不分大小写

← ↑ → ↓

37、38、39、40

回车键

13

空格键

32

  • 演示例子:

4.5.3、preventDefault()方法

  • 阻止事件产生的默认动作 -- 如:“键盘输入a是不显示,a显示在输入框中为默认动作”

4.5.4、stopPropagation()方法

  • 阻止事件后续传播 -- 如:“在捕获阶段,阻止内层的事件捕获,需要在外层调用该方法来进行事件传播阻止”
    <div id="box1">
      <div id ="box2">
      </div>
    </div>
    box1.addEventListener('click',function(e){
      e.stopPropagation()
      console.log("这个是click box1")
    },true)
    box2.addEventListener('click',function(){
      console.log("这个是click box2")
    },true)
    这个是onclick box1

4.6、事件委托

  • 事件委托是指:通过冒泡机制,将目标元素事件委托给祖先元素(不仅仅指父元素)
  • 事件委托通常结合e.target属性使用 | 属性 | 属性描述 | | --- | --- | |

    target

    |

    事件的真正发出者

    | |

    currentTarget

    |

    始终是监听事件者

    |

事件委托使用的场景和优点

  • 需要为大量元素添加事件监听时,减少事件监听内存开销
  • 新元素需要上树并添加事件监听时

事件委托需要注意的事项

  • 冒泡机制的事件,不能使用事件委托
  • 例如:onmouseenter天生不冒泡,onmouseover冒泡,不可为onmouseenter添加事件委托
  • 最内层元素不能再有额外的内层元素,如下示例分析:
    <ul>
        // 这时这个`span`实际才是最内层元素,当点击`谁啊?`时,整个`li`变红,但点击`额外内层元素`标签时,只有`span`标签变红
        <li><span>额外内层元素</span>谁啊?</li>
    </ul>

4.7、定时器和延时器

  • 定时器:每固定间隔,重复调用函数
  • 延时器:固定间隔后,只调用一次函数

4.7.1、定时器

  • setInterval(function(){},间隔时间)
    // 每一秒打印一次 -- 我是鸡哥定时器
    setInterval(function(){
       console.log("我是鸡哥定时器")
    },1000) 
  • setInterval(function(){},间隔时间,参数...)
    // 每一秒打印一次 -- 30
    setInterval(function(a,b){
       console.log(a+b)
    },1000,10,20) 
  • 清除定时器 --> clearInterval(定时器变量)
    // 不会执行打印
    var timer = setInterval(function(){
       console.log("我是鸡哥定时器")
    },1000) 
    clearInterval(timer)

4.7.2、延时器

  • setTimeout(function(){},间隔时间)
    // 每一秒打印一次 -- 我是鸡哥定时器
    setTimeout(function(){
       console.log("我是鸡哥定时器")
    },1000) 
  • 清除延时器 --> clearTimeout(延时器变量)
    // 不会执行打印
    var timer = setTimeout(function(){
       console.log("我是鸡哥定时器")
    },1000) 
    clearTimeout(timer)

定时器与延时器操作实例: