Js Dom

DOM,全称Document Object Model,中文翻译为文档对象模型。DOM属于Web API的一部分。Web API中定义了非常多的对象,通过这些对象可以完成对网页的各种操作(添加删除元素、发送请求、操作浏览器等)
DOM中的D意为Document,即文档。所谓文档就是指整个网页,换言之,DOM是用来操作网页的。O意为Object,即对象。DOM将网页中的每一部分内容都转换为了对象,div有div的对象,input有input的对象,甚至一段文本,一段注释也有其所对应的对象。转换对象以后,我们就可以以面向对象的方式去操作网页,想要操作哪个元素就获取哪个元素的对象,然后通过调用其方法或属性完成各种操作。M意为Model,即模型。模型用来表示对象之间的关系,也就是父子元素、祖先后代、兄弟元素等,明确关系后我们便可以通过任意一个对象去获取其他的对象
网页中的各种元素(如标签、文本、属性等)被表示为对象,这些对象以树状结构组织起来,称为 DOM 树

例如,对于一个简单的 HTML 页面

Hello, World!
,其 DOM 树的根节点是html,html节点下有body子节点,body节点下又有p子节点,p节点包含文本内容 "Hello, World!"

document对象

要使用DOM来操作网页,我浏览器至少得先给我一个对象才能去完成各种操作,浏览器已经为我们提供了一个document对象,它是一个全局变量可以直接使用

html 复制代码
<body>

    <button id = "btn">按钮</button>

<script>

    // 通过id获取btn对象
    const btn = document.getElementById("btn")
    // 将按钮文案更改为新按钮
    // innerText的用于获取或者设置元素内部的文本内容
    btn.innerText = "新按钮"
    
</script>

</body>
  • document对象表示的是整个网页
    • document对象的原型链
      HTMLDocument -> Document -> Node -> EventTarget -> Object.prototype -> null
    • 凡是在原型链上存在的对象的属性和方法都可以通过Document去调用
    • 部分属性:
      document.documentElement --> html根元素
      document.head --> head元素
      document.title --> title元素
      document.body --> body元素
      document.links --> 获取页面中所有的超链接
    • 用法大全

节点

在DOM标准下,网页中的每一个部分都会转换为对象。这些对象有一个共同的称呼------节点(Node)。一个页面将会由多个节点构成,虽然都称为节点,但是它们却有着不同的类型:文档节点、元素节点、文本节点、属性节点....

每一个节点都有其不同的作用,文档节点表示整个网页,元素节点表示某个标签,文本节点表示网页中的文本内容,属性节点表示标签中的各种属性。如果从对象的结构上来讲,这些对象都有一个共同的父类Node。总的来说,都是属于节点,但是具体类型不同

元素节点

元素节点对象(element),在网页中,每一个标签都是一个元素节点

html 复制代码
<body>


    <button id = "btn" class="btn-class">按钮</button>
    <input type="radio" name="one">
    <script>

        // 通过id获取元素节点对象
        const btnId = document.getElementById("btn")
        console.log(btnId) // <button id="btn" class="btn-class">按钮</button>

        // 通过class获取所有符合条件的元素条件对象,返回的是一个类数组对象
        // 该方法返回的结果是一个实时更新的集合,当网页中新添加元素时,集合也会实时更新
        const btnClass = document.getElementsByClassName("btn-class")
        console.log(btnClass) // HTMLCollection[button#btn.btn-class, btn: button#btn.btn-class]


        // 根据标签名获取一组元素节点对象
        // 该方法返回的结果是一个实时更新的集合,当网页中新添加元素时,集合也会实时更新
        // document.getElementsByTagName("*") 获取页面中所有的元素
        const btnTag = document.getElementsByTagName("button")
        console.log(btnTag) // HTMLCollection[button#btn.btn-class, btn: button#btn.btn-class]


        // 根据name属性获取一组元素节点对象, 返回一个实时更新的集合,主要用于表单
        const inputName = document.getElementsByName("one")
        console.log(inputName) // NodeList[input]

        // 根据各种css选择器去页面中查询元素,会返回一个类数组(不会实时更新)
        const query = document.querySelectorAll(".btn-class")
        console.log(query) // NodeList[button#btn.btn-class]

        // 根据选择器去页面中查询第一个符合条件的元素,没有则返回null
        const firstQuery = document.querySelector("button")
        console.log(firstQuery) //  <button id="btn" class="btn-class">按钮</button>

        // 根据标签名创建一个元素节点对象,只是创建,未插入到网页
        const newTag = document.createElement("h1")
        console.log(newTag) // <h1></h1>


    </script>

</body>
html 复制代码
/*
通过元素节点对象获取其他节点
*/

<body>

    <div id = "box">
        <button id = "btn" class="btn-class">按钮</button>
        <input type="radio" name="one">
    </div>
    <button class="btn-class">按钮2</button>

    <script>


        const element = document.getElementById("box")

        // document对象的方法元素节点对象可以同样使用,区别是在于获取的是当前对象内的指定对象
        // 获取element内的button对象
        const btn = element.getElementsByTagName("button")
        console.log(btn) // HTMLCollection[button#btn.btn-class, btn: button#btn.btn-class]


        // 获取当前元素的子节点(包含空白的子节点)
        const childNodes = element.childNodes
        console.log(childNodes) // NodeList(5) [text, button#btn.btn-class, text, input, text]

        // 获取当前元素的子元素
        const children = element.children
        console.log(children) // HTMLCollection(2)[button#btn.btn-class, input, btn: button#btn.btn-class, one: input]

        // 获取当前元素的第一个子元素
        const firstElementChild = element.firstElementChild
        console.log(firstElementChild) // <button id = "btn" class="btn-class">按钮</button>

        // 获取当前元素的最后一个子元素
        const lastElementChild = element.lastElementChild
        console.log(lastElementChild) // <input type="radio" name="one">

        // 获取当前元素的下一个兄弟元素
        const nextElementSibling = element.nextElementSibling
        console.log(nextElementSibling) //     <button class="btn-class">按钮2</button>

        // 获取当前元素的前一个兄弟元素
        const previousElementSibling = element.previousElementSibling
        console.log(previousElementSibling) // null

        // 获取当前元素的父节点
        const parentNode = element.parentNode
        console.log(parentNode) // <body>...</body>

        // 获取当前元素的标签名
        const tagName = element.tagName
        console.log(tagName) // DIV

    </script>

</body>
文本节点

在DOM中,网页中所有的文本内容都是文本节点对象,包括空文本、换行和空格

html 复制代码
<body>

    <div id = "box">
        asdasd
        <button id = "btn" class="btn-class">按钮</button>
        <input type="radio" name="one">
    </div>
    <button class="btn-class">按钮2</button>

    <script>
		// 可以通过元素来获取其中的文本节点对象,但是我们通常不会这么做,我们可以直接通过元素去修改其中的文本

        const element = document.getElementById("box")
        const t = element.firstChild
        console.log(t) //  asdasd
        
    </script>

</body>

直接通过元素去修改其中的文本

html 复制代码
<body>

    <div id = "box">
        asdasd
        <button id = "btn" class="btn-class">按钮</button>
        <input type="radio" name="one">
    </div>
    <button class="btn-class">按钮2</button>



    <script>


        const element = document.getElementById("box")

        // textContent获取或修改元素中的文本内容,获取的是标签中的内容,不会考虑css样式
        // 当字符串中有标签时,会自动对标签进行转义显示在页面,不会渲染
        // 如果修改会在网页直接生效
        console.log(element.textContent) // 获取文本:asdasd 按钮
        console.log(element.firstChild.textContent) // 获取element第一个子节点文本:asdasd
        element.firstChild.textContent = "new t" // 将第一个文本修改为new t



        // innerText获取或修改元素中的文本内容,会考虑css样式,会触发网页的重排(计算CSS样式)
        // 当字符串中有标签时,会自动对标签进行转义显示在页面,不会渲染
        // 如果修改会在网页直接生效

        // 获取element的第一个元素子节点文本
        console.log(element.firstElementChild.innerText) // 按钮
        element.firstElementChild.innerText = "大按钮" // 修改element的第一个元素子节点文本



        // innerHTML获取或修改元素中的html代码,可以直接向元素中添加html代码,innerHTML插入内容时,有被xss注入的风险
        // 当字符串中有标签时,会在网页生效
        console.log(element.firstElementChild.innerHTML) // 大按钮'
        element.firstElementChild.innerHTML = "<h1>新大按钮</h1>" // h1效果会在网页生效


    </script>

</body>
属性节点

属性节点(Attr)在DOM也是一个对象,通常不需要获取对象而是直接通过元素即可完成对其的各种操作

js 复制代码
<input class="admin" type="text" name="username" value="user">

<input class="admin1" type="text" name="username1" value="user1">


<script>



    const input = document.querySelector("[name=username]")
    const input2 = document.querySelector(".admin1")



    // 通过元素.属性名 可以读取、修改对应的数据
    console.log(input.className) // admin,读取class属性比较特殊,需要使用className
    console.log(input.type) // text
    console.log(input.name) // username
    input.value = "new user" // 修改value的值为new user
    console.log(input.disabled) // false 读取一个布尔值时,会返回true或false


    // 通过方法读取、修改对象的属性
    // 读取属性
    console.log(input2.getAttribute("type")) // text
    // 修改属性,将value属性的值修改为uuu
    input2.setAttribute("value","uuu")
    // 删除属性
    input2.removeAttribute("value")



    </script>

</body>

事件

事件(event)

  • 事件就是用户和页面之间发生的交互行为
    比如:点击按钮、鼠标移动、双击按钮、敲击键盘、松开按键...
  • 可以通过为事件绑定响应函数(回调函数),来完成和用户之间的交互
  • 绑定响应函数的方式:
    1.可以直接在元素的属性中设置
    2.可以通过为元素的指定属性设置回调函数的形式来绑定事件(一个事件只能绑定一个响应函数)
    3.可以通过元素addEventListener()方法来绑定事件
html 复制代码
<body>


<button id="btn">点击</button>

<script>


    // 获取按钮对象
    const btn = document.getElementById("btn")

    // 为该按钮的点击事件设置响应函数,该方式一个事件只能绑定一个响应函数,如果后续重复绑定会被覆盖
    btn.onclick = function (){
        alert("onclick!!!")
    }



    // 通过addEventListener绑定事件 第一个参数是触发的事件,第二个参数是响应函数
    // 该方式一个事件可以绑定多个响应函数,根据绑定顺序执行
    btn.addEventListener("click",function (){
        console.log("addEventListener")
    })
    btn.addEventListener("click",function (){
        alert("addEventListener")
    })
</script>

</body>

文档的加载

网页是自上向下加载的,如果将js代码编写到网页的上边,js代码在执行时,网页还没有加载完毕,这时会出现无法获取到DOM对象的情况

如何解决这个问题:

1. 将script标签编写到body的最后 
2. 将代码编写到window.onload的回调函数中
3. 将代码编写到document对象的DOMContentLoaded的回调函数中(执行时机更早)
4. 将代码编写到外部的js文件中,然后以defer的形式进行引入(执行时机更早,早于DOMContentLoaded)
html 复制代码
<script>
    // window.onload 事件会在窗口中的所有内容(一个网页可能有外嵌页面)加载完毕之后才触发
    // 窗口内容加载完之后触发响应函数执行js代码
    window.onload = function () {
        // 获取按钮对象
        const btn = document.getElementById("btn")

        btn.addEventListener("click", function () {
            alert("addEventListener")
        })
    }
 

</script>

<body>

<button id="btn">点击</button>

</body>
js 复制代码
    // DOMContentLoaded事件会在当前文档加载完后执行
    document.addEventListener("DOMContentLoaded",function (){
        // 获取按钮对象
        const btn = document.getElementById("btn")

        btn.addEventListener("click", function () {
            alert("addEventListener")
        })
        
        
    })
html 复制代码
<!-- 引入外部js文件 -->
<!-- 如果默认引入在body上,依然会有加载时机问题 -->
<!-- defer 关键字会延迟加载,当前文档加载完再加载,解决该问题-->

<script defer src = "./script/index.js"></script>

DOM元素的修改

创建元素
appendChild

appendChild()是 DOM节点对象的一个方法。它的主要作用是将一个节点添加到另一个节点的子节点列表的末尾。这是一种在 HTML 文档结构中动态添加元素的方式

html 复制代码
<body>

<button id="btn">按钮</button>


<ul id="list">
    <li id="one">1</li>
    <li id="two">2</li>
    <li id="three">3</li>
</ul>

</body>

<script>
    // 获取ul
    const list = document.getElementById("list")

    // 获取按钮
    const  btn = document.getElementById("btn")

    // 点击按钮的时候触发
    btn.onclick = function (){
        // 创建一个li
        const li = document.createElement("li")
        // li的文本
        li.textContent = "4"
        // 设置li的id
        li.id = "four"

        // 把创建的li元素添加到ul里面
        // appendChild把一个节点添加到另一个节点
        list.appendChild(li)

    }

</script>
insertAdjacentElement

insertAdjacentElement()是js的一个 DOM 操作方法。它用于将一个指定的元素插入到另一个元素的附近特定位置

js 复制代码
 // 点击按钮的时候触发
    btn.onclick = function (){
        // 创建一个li
        const li = document.createElement("li")
        // li的文本
        li.textContent = "4"
        // 设置li的id
        li.id = "four"

        // 把创建的li元素添加到ul里面

        // 参数:1. 要添加的位置 2. 要添加的元素

        list.insertAdjacentElement("afterbegin",li)

        // beforeend 标签的最后 afterbegin 标签的开始
        // beforebegin 在元素的前边插入元素(兄弟元素) afterend 在元素的后边插入元素(兄弟元素)


    }
insertAdjacentHTML

insertAdjacentHTML()用于在指定元素的附近插入 HTML 字符串的方法。允许你将 HTML 代码片段作为字符串插入到元素的不同位置,有XSS攻击的风险

js 复制代码
    // 点击按钮的时候触发
    btn.onclick = function (){
    

        // 参数:1. 要添加的位置 2. 要插入的html代码字符串

        list.insertAdjacentHTML("afterbegin","<li id='5'>5</li>>")

        // beforeend 标签的最后 afterbegin 标签的开始
        // beforebegin 在元素的前边插入元素(兄弟元素) afterend 在元素的后边插入元素(兄弟元素)


    }
替换元素
js 复制代码
        // 获取 id=one的li
        const one = document.getElementById("one")
        // 创建一个新的li
        const newOne = document.createElement("li")
        newOne.textContent = "1111"
        newOne.id="new-one"
        
        // replaceWith 使用一个元素替换当前元素
        // newOne替换one
        one.replaceWith(newOne)
删除元素
js 复制代码
  const two = document.getElementById("two")
        // remove()方法用来删除当前元素
        two.remove()
取消元素的默认行为
html 复制代码
<!--javascript:; 执行代码 但是代码是空的,即不发生任何行为操作-->
<a href="javascript:;"></a>

使用return false来取消默认行为,只在 xxx.xxx = function(){}这种形式绑定的事件中才适用

js 复制代码
    // 响应函数
    function delUserInfo(){
        // 为所有超链接绑定了该响应函数,点击哪个链接,this就是哪个
        // parentNode是一个节点属性。它用于获取当前节点的父节点
        const tr = this.parentNode.parentNode

        // 获取用户昵称
        const userName = tr.firstElementChild.textContent

        // confirm()是一个浏览器提供的全局函数,用于显示一个带有 "确定" 和 "取消" 按钮的模态对话框。这个对话框用于向用户询问一个是 / 否的问题,并且等待用户做出响应
        if (confirm(`确认要删除${userName}吗`)){
            tr.remove()
        }


        // 取消默认事件
        return false
    }


    // 获取所有超链接
    const links = document.links

    // 为所有的链接绑定单级响应函数
    for (let link of links){
        link.onclick = delUserInfo


    }
节点的复制
js 复制代码
    // 获取ul列表
    const list = document.getElementById("list")


    // 获取id为li1的元素节点
    const li1 = document.getElementById("li1")

    //  使用li1节点复制一份,赋值给newLi
    const newLi = li1.cloneNode(true)

    newLi.id = "li2"
    list.appendChild(newLi)

    /* 
       使用 cloneNode() 方法对节点进行复制时,它会复制节点的所有特点包括各种属性
       这个方法默认只会复制当前节点,而不会复制节点的子节点
       可以传递一个true作为参数,这样该方法也会将元素的子节点一起复制
                   
   */

通过DOM操作css

使用style读取、修改内联样式
js 复制代码
    // 获取box元素
    const box = document.getElementById("box")
    
    // 修改box的内联样式样式
    box.style.width = "400px"
    box.style.height = "400px"
    // 如果样式名中含有-,则需要将样式表修改为驼峰命名法,background-color --> backgroundColor
    box.style.backgroundColor = "yellow"    
    // 读取内联样式
    console.log(box.style.height)
读取当前生效的样式
js 复制代码
     // 获取box元素
    const box = document.getElementById("box")

    // getComputedStyle返回一个只读对象,这个对象中包含了当前元素所有的生效的样式,无法通过该方式修改样式
    // 参数:要获取样式的对象,要获取的伪元素
    const styleObj = box.getComputedStyle(box)
    // 当前生效的宽度
    console.log(styleObj.width)
    // 当前生效的高度
    console.log(styleObj.height)


    // 获取before伪元素的样式
    const styleObj1 = box.getComputedStyle(box,"::before")
    console.log(styleObj1.width)
其他读取样式的方式
js 复制代码
    // 获取box元素
    const box = document.getElementById("box")


    //  获取元素内部的宽度和高度,包含内容区和内边距
    console.log(box.clientWidth)
    console.log(box.clientHeight)

    // 获取元素的可见框的大小,包含内容区、内边距和边框
    console.log(box.offsetWidth)
    console.log(box.offsetHeight)

    // 获取元素滚动区域的宽度和高度
    console.log(box.scrollWidth)
    console.log(box.scrollHeight)

    // 获取元素的定位父元素,离当前元素最近的开启了定位的祖先元素,如果所有的元素都没有开启定位则返回body
    console.log(box.offsetParent)

    // 获取元素相对于其定位父元素的偏移量
    console.log(box.offsetTop)
    console.log(box.offsetLeft)

    //获取 or 设置元素滚动条的偏移量
    console.log(box.scrollTop)
    console.log(box.scrollLeft)
    box.scrollTop = 100 // 设置偏移量
操作class修改css样式

除了直接修改样式外,也可以通过修改class属性来间接的修改样式

html 复制代码
<style>
    
    .btn{
        width: 200px;
    }
    
</style>

<script>


    const btn1 = document.getElementById("btn1")
    const btn2 = document.getElementById("btn2")


    // 在原先class名字的基础上 加一个btn的class
    // css样式中的类选择器命中设置样式
    btn1.className += " btn"
    btn2.className += " btn"
    

</script>

通过class修改样式的好处:

         1. 可以一次性修改多个样式
         2. 对JS和CSS进行解耦
js 复制代码
    const btn1 = document.getElementById("btn1")

    // 元素.classList 是一个对象,对象中提供了对当前元素的类的各种操作方法

    // 向元素中添加一个或者多个class
    btn1.classList.add("btn2","btn3")
    // 移除元素中的一个或多个class
    btn1.classList.remove("btn2")

    // 切换元素中的class,如果有btn2的class,则删除,如果没有,就加上
    btn1.classList.toggle("btn2")

    // 替换class,将btn1替换为btn2
    btn1.classList.replace("btn1","btn2")

    // 检查class,有该class返回true,否则false
    btn1.classList.contains("btn1")

事件对象

事件对象:

  • 事件对象是有浏览器在事件触发时所创建的对象,这个对象中封装了事件相关的各种信息

  • 通过事件对象可以获取到事件的详细信息,比如:鼠标的坐标、键盘的按键..

  • 浏览器在创建事件对象后,会将事件对象作为响应函数的参数传递,

  • 所以我们可以在事件的回调函数中定义一个形参来接收事件对象

js 复制代码
    const box = document.getElementById("box")

		// 监听事件,在创建事件对象后,会将事件对象作为响应函数的参数传递,在事件的回调函数中定义一个形参来接收事件对象
    box.addEventListener("mousemove",event => {
        console.log(event) // 包含很多属性和方法的对象,具体取决于触发的事件类型
        console.log(event.clientX) // 获取X轴
        console.log(event.clientY) // 获取Y轴
    })

   box.addEventListener("click",function (event){
        console.log(event)
    })
Event对象

在DOM中存在着多种不同类型的事件对象,多种事件对象有一个共同的祖先 Event

js 复制代码
const box = document.getElementById("box")

    box.addEventListener("click", event => {
        // 触发事件的对象
        console.log(event.target) // id 为box的元素
        // 绑定事件的对象(与事件冒泡有关),同this
        console.log(event.currentTarget)

        /*
        - 事件的冒泡就是指事件的向上传导
        - 当元素上的某个事件被触发后,其祖先元素上的相同事件也会同时被触发,由里向外
        - 冒泡的存在大大的简化了代码的编写,但是在一些场景下我们并不希望冒泡存在,可以通过事件对象来取消冒泡
        - 事件的冒泡和元素的样式无关,和结构相关
        */
        // 取消事件传导(冒泡)
        event.stopPropagation()
        
        //  取消默认行为
        event.preventDefault()

    })
事件委派

只绑定一次事件,即可以让所有的元素对象,包括当前和未来新建的都具有这些事件

委派就是将本该绑定给多个元素的事件,统一绑定给document然后根据判断逻辑由什么元素触发,这样可以降低代码复杂度方便维护

html 复制代码
<body>
<button id="btn">按钮</button>

<ul id="list">
    <li><a href="#">1</a></li>
    <li><a href="#">2</a></li>
    <li><a href="#">3</a></li>
</ul>

</body>


<script>


    const list = document.getElementById("list")
    const btn = document.getElementById("btn")
    // 获取list中所有超链接
    const links = list.getElementsByTagName("a")
    //  可以将事件统一绑定给document,这样点击事件的冒泡,会导致document上的点击事件被触发
    //  这样只绑定一次,所有的点击都会具有这些事件

    document.addEventListener("click", (event) => {
        // 判断事件是由谁触发,event.target 是否在 links 中存在
        // 只有list中的超链接点击生效,如果不判断事件触发,页面所有点击效果都会生效
        if ([...links].includes(event.target)) {
            alert(event.target.textContent)

        }
    })



    // 点击按钮后,在ul中加一个新的li
    btn.addEventListener("click", () => {
        list.insertAdjacentHTML("beforeend", "<li><a href='#'>new</a></li>")}
    )


</script>
事件捕获

在DOM中,事件的传播可以分为三个阶段:

​ 1.捕获阶段 (由祖先元素向目标元素进行事件的捕获)(默认情况下,事件不会在捕获阶段触发)

​ 2.目标阶段 (触发事件的对象)

​ 3.冒泡阶段 (由目标元素向祖先元素进行事件的冒泡)
事件的捕获,指事件从外向内的传导:

  • 当前元素触发事件以后,会先从当前元素最大的祖先元素开始向当前元素进行事件的捕获
  • 冒泡是由里向外,而捕获是是由外向里
js 复制代码
    const box = document.getElementById("box")

    box.addEventListener("click", event => {
        // eventPhase 表示事件触发的阶段
        console.log(event.eventPhase)

        // 如果希望在捕获阶段触发事件,可以将addEventListener的第三个参数设置为true

    }, true)
相关推荐
weixin_516875655 分钟前
使用 axios 拦截器实现请求和响应的统一处理(附常见面试题)
前端·javascript·vue.js
皮卡穆8 分钟前
JavaScript 变量作用域与函数调用机制:var 示例详解
javascript
_oP_i8 分钟前
Unity 中使用 WebGL 构建并运行时使用的图片必须使用web服务器上的
前端·unity·webgl
H_HX12611 分钟前
https服务器访问http资源报Mixed Content混合内容错误
前端·vue.js·安全策略·https访问http
羊小猪~~20 分钟前
前端入门一之CSS知识详解
前端·javascript·css·vscode·前端框架·html·javas
哪 吒23 分钟前
华为OD机试 - 无重复字符的元素长度乘积的最大值(Python/JS/C/C++ 2024 C卷 100分)
javascript·python·华为od
ReBeX26 分钟前
【GeoJSON在线编辑平台】(1)创建地图+要素绘制+折点编辑+拖拽移动
前端·javascript·gis·openlayers·webgis
阿宝分享技术30 分钟前
从xss到任意文件读取
前端·xss
今天也想MK代码35 分钟前
在Swift开发中简化应用程序发布与权限管理的解决方案——SparkleEasy
前端·javascript·chrome·macos·electron·swiftui
宝子向前冲1 小时前
纯前端生成PDF(jsPDF)并下载保存或上传到OSS
前端·pdf·html2canvas·oss·jspdf