Web前端 JS WebAPI

1、操作DOM


1.1、什么DOM?

DOM(Document Object Model——文档对象模型):DOM是浏览器提供的一套专门用来操作网页内容的功能

DOM作用:开发网页内容特效和实现用户交互

DOM树是什么?

  • 将 HTML 文档以树状结构直观的表现出来,称之为文档树或 DOM 树
  • DOM树直观的体现了标签与标签之间的关系

在这里插入图片描述

DOM对象:浏览器根据HTML标签生成的 JS对象

  • 所有的标签属性都可以在这个对象上面找到
  • 修改这个对象的属性会自动映射到标签身上

DOM的核心思想:**把网页内容当做对象来处理!**它提供的属性和方法都是用来访问和操作网页内容的

1.2、获取DOM对象

前提条件:要操作一个Dom节点, 就必须要先获得这个Dom节点,通过JS选择页面中的HTML元素

打印对象:console.dir('对象名')

querySelector

选择匹配的第一个元素

返回值:CSS选择器匹配的第一个元素,一个 HTML Element对象。 如果没有匹配到,则返回null

具体语法:

<body>
    <div>这是第一个盒子</div>
    <div id="box">这是第二个盒子</div>

    <ul>
        <li>Java</li>
        <li>JavaScript</li>
        <li>Python</li>
    </ul>

    <script>
        // 获取匹配的第一个元素
        const div = document.querySelector('div')
        console.log(div);

        const box = document.querySelector('#box')
        console.log(box);

        const li = document.querySelector('ul li')		// 只能获取第一个元素
        console.dir(li)		// Java 
    </script>
</body>

querySelectorAll

选择匹配的多个元素

返回值:CSS选择器匹配的NodeList 对象集合

具体语法:

<body>
    <ul class="course">
        <li>Java</li>
        <li>JavaScript</li>
        <li>Python</li>
    </ul>

    <script>
        // 获取匹配的多个元素
        const lis = document.querySelectorAll('.course li')
        console.log(lis)
    </script>
</body>    

注:通过querySelector系列获得的元素得到的是一个伪数组

  • 有长度有索引号的数组
  • 但是没有 pop() push() 等数组方法
  • 要想得到里面的每一个对象,则需要遍历(for)的方式获得

getElement系列

getElement系列方法获取DOM元素

<body>
    <h1>我是h1标题</h1>
    <div id="box">我是div盒子</div>

    <ul>
        <li class="item">1</li>
        <li class="item">2</li>
        <li class="item">3</li>
        <li class="item">4</li>
    </ul>

    <input type="checkbox" name="hobbies"> code
    <input type="checkbox" name="hobbies"> girl

    <script>
        let box = document.getElementById('box')			// id
        let items = document.getElementsByClassName('item')		// 类
        let h1 = document.getElementsByTagName('h1')		// 标签名称
        let hobbies = document.getElementsByName('hobbies')		// 相同 name 属性

        console.log(h1)
        console.log(box)
        console.log(items)
        console.log(hobbies)
    </script>
</body>

操作元素内容

  • 对象.innerText 属性
  • 对象.innerHTML 属性

DOM对象都是根据标签生成的,所以操作标签,本质上就是操作DOM对象!

<body>
    <div class="box">我是一个div盒子</div>
    
    <script>
        // 获取元素
        const box = document.querySelector('.box')
        // 修改文本内容
        console.log(box.innerText)      // 获取文本内容
        box.innerText = '<em>我是div标签 我可以布局网页</em>'       // 不解析标签
        box.innerHTML = '<em>我是div标签 我可以布局网页</em>'       // 会解析标签
    </script>
</body>

两者区别:

相同点:

  • 都可以修改标签文本内容

不同点:

  • innerText属性,只会显示纯文本,不解析标签
  • 而innerHTML属性,既可以显示纯文本,也会解析标签,多标签建议使用模板字符

1.3、操作元素属性

元素常用属性

还可以通过 JS 设置/修改标签元素属性,常见的属性有: href、title、src 等

<body>
    <img src="./images/1.jpg" title="漂亮可爱的小姐姐图片">

    <button onclick="changeImg()">点我更换图片</button>

    <script>
        function changeImg() {
      
      
            let arr = ['1.jpg', '2.jpg', '3.jpg', '4.jpg', '5.jpg', '6.jpg', '7.jpg', '8.jpg']
            // 生成随机数
            let ran = Math.floor(Math.random() * arr.length)
            // 获取图像元素
            const img = document.querySelector('img')
            // console.log(img)
            img.src = './images/' + arr[ran]
            img.title = '漂亮可爱的小姐姐图片'
        }
    </script>
</body>

元素样式属性

通过 style 属性操作CSS
<body>
    <div class="box"></div>
    
    <script>
        // 获取元素
        const box = document.querySelector('.box')
        // console.log(box)
        // 修改样式属性  对象名.style.样式属性 = '值'
        box.style.width = '350px'
        box.style.height = '350px'
        box.style.backgroundColor = 'orange'
    </script>
</body>

注:

  • CSS属性名写法为驼峰命名法
  • 赋值的时候,需要的时候不要忘记加CSS单位
  • 通常用于修改样式较少时使用,而且添加的是行内样式(权重较高)
通过类名(className) 操作CSS

如果修改的样式比较多,直接通过style属性修改比较繁琐,代码比较长,可以通过借助于CSS类名的形式

<style>
    .circle {
      
      
        width: 300px;
        height: 300px;
        background-color: sandybrown;
        border-radius: 50%;
        cursor: pointer;
    }

    .box {
      
      
        color: white;
        text-align: center;
        line-height: 300px;
    }
</style>

<body>
    <div class="box">这是一个圆圈</div>

    <script>
        // 获取元素
        const div = document.querySelector('div')
        // 添加类名
        div.className = 'box circle'
    </script>
</body>

注:

  • 由于class是关键字,所以使用className去代替
  • className是使用新值换旧值(覆盖),如果需要添加一个类,需要保留之前的类
通过 classList 操作类控制CSS

为了解决className 容易覆盖以前的类名,可以通过classList方式追加和删除类名

<style>
    .wrap {
      
      
        display: flex;
        justify-content: space-around;
        width: 200px;
        margin: 60px auto;
    }

    .item {
      
      
        list-style: none;
        width: 20px;
        height: 20px;
        background-color: gray;
        text-align: center;
        line-height: 20px;
        border-radius: 50%;
        color: white;
    }

    .active {
      
      
        background-color: coral;
        cursor: pointer;
    }
</style>

<body>
    <ul class="wrap">
        <li class="item">1</li>
        <li class="item">2</li>
        <li class="item">3</li>
        <li class="item">4</li>
        <li class="item">5</li>
    </ul>

    <script>
        // 获取元素
        const item = document.querySelector('.item')
        // 修改样式     追加一个类 add()
        item.classList.add('active')
        // 修改样式     移除一个类  remove()
        item.classList.remove('active')
        // 切换样式     切换一个类  toggle()    没有添加,有则移除
        item.classList.toggle('active')
    </script>
</body>

className与classList两者区别:

  • className 会覆盖之前类名,如果需要以前的类名,在编写的时候还要手动加上!
  • classList 是追加和删除,不影响之前类名,修改样式也比较方便

表单元素属性

  • 获取:DOM对象.属性名
  • 设置:DOM对象.属性名 = 新值
<input type="text" name="g-name" id="g-name" value="笔记本电脑">

<script>
    // 获取元素
    const gName = document.querySelector('#g-name')
    // 获取值
    console.log(gName.value)        // 笔记本电脑
    // 设置文本框的值
    gName.value = '平板'
    // 修改文本框 type 属性
    gName.type = 'date'
</script>

表单属性中添加就有效果,移除就没有效果,一律使用布尔值表示,如果为true 代表添加了该属性,如果是false代表移除了该属性

例如: disabled、checked、selected

在这里插入图片描述

<input type="checkbox" name="hobbies"> code
<input type="checkbox" name="hobbies"> girl
<input type="checkbox" name="hobbies"> swim

<script>
    // 获取元素
    const ipts = document.querySelectorAll('input')
    for (let i = 0; i < ipts.length; i++) {
      
      
        console.log(ipts[i].checked)        // 默认没有勾选,返回false
        // 让三个多选框全部选中
        ipts[i].checked = true
    }
    // 让第一个复选框为禁用状态
    ipts[0].disabled = true
</script>

自定义属性

  • 标准属性:也就是标签天生自带的属性,比如class id title等,可以直接使用点语法操作,比如:disabled、checked、selected
  • 自定义属性:
    • 在html5中推出来了专门的data-自定义属性
    • 在标签上一律以data-开头
    • 在DOM对象上一律以dataset对象方式获取
<ul>
    <li data-gid="1001" data-hot="热卖商品">1</li>
    <li data-gid="1002">2</li>
    <li data-gid="1003">3</li>
</ul>

<script>
    const li = document.querySelector('ul>li:first-child')
    console.log(li.dataset)
    console.log(li.dataset.gid)     	// 1001
    console.log(li.dataset['hot'])      // 热卖商品
</script>

1.4、定时器

网页中经常会需要一种功能:每隔一段时间需要自动执行一段代码,不需要我们手动去触发,例如:网页中的倒计时

定时器-间歇函数

开启定时器

具体语法:setInterval(函数,间隔时间)

<script>
    function love() {
      
      
        document.write(`<p>我爱你到天长地久</p>`)
    }

    let id = setInterval(love, 2000)        
    console.log(id)     	// 1
</script>

作用:

  • 每隔一段时间都会调用这个函数
  • 间隔时间单位是毫秒

注:

  • 定时器调用函数名字的时候不需要加括号

  • 定时器返回的是一个id数字

关闭定时器

一般不会刚创建就停止,而是满足一定条件才停止

具体语法:clearInterval(变量名)

<script>
    function love() {
      
      
        document.write(`<p>我爱你到天长地久</p>`)
    }

    let id = setInterval(love, 2000)
    console.log(id)         // 1

    // 关闭定时器
    clearInterval(id)
</script>

2、事件


2.1、何为事件?

事件是指用户在访问页面时,对页面内容做出的动作或者产生的行为

什么是事件监听?

就是让程序检测是否有事件产生,一旦有事件触发,就立即调用一个函数做出响应,也称为绑定事件或者注册事件

比如鼠标经过显示下拉菜单,比如点击可以播放轮播图等等

2.2、事件监听

具体语法:元素对象.addEventListener('事件类型',要执行的函数)

事件监听三要素:

  • 事件源: 那个dom元素被触发了,要获取dom元素
  • 事件类型: 用什么方式触发,比如鼠标单击 click、鼠标经过 mouseover 等
  • 事件调用的函数: 要做什么事
<body>
    <div class="box">
        <img src="./images/jd.webp">
        <div class="close">x</div>
    </div>

    <script>
        // 点击 按钮 x 号,关闭广告
        // 事件源:x 号
        // 事件类型:点击
        // 处理程序:关闭广告
        let btn = document.querySelector('.close')
        let box = document.querySelector('.box')
        btn.addEventListener('click', function () {
      
      
            box.style.display = 'none'
        })
    </script>
</body>

注:

  • 事件类型必须要加引号,否则无效果
  • 函数是点击之后再去执行,每次点击都会执行一次

事件监听版本

DOM L0:事件源.on事件 = function() { }

DOM L2:事件源.addEventListener(事件,事件处理函数)

<button>点我</button>

<script>
    const btn = document.querySelector('button')
    btn.onclick = function () {
      
      
        alert(1)
    }
    // 会覆盖之前的点击事件,只要下面点击事件才会触发
    btn.onclick = function () {
      
      
        alert(2)
    }

    // 不会覆盖之前的点击事件,点击事件都会触发
    btn.addEventListener('click', function () {
      
      
        console.log(1)
    })

    btn.addEventListener('click', function () {
      
      
        console.log(2)
    })
</script>

两者区别:

  • on方式会被覆盖,addEventListener方式可绑定多次,拥有事件更多特性,推荐使用

2.3、事件类型

  • 鼠标事件
    • click 鼠标点击
    • mouseenter 鼠标经过
    • mouseleave 鼠标离开
  • 焦点事件
    • focus 获得焦点
    • blur 失去焦点
  • 键盘事件
    • keydown 键盘按下触发
    • keyup 键盘抬起触发
  • 文本事件
    • input 用户输入事件
<div></div>

<script>
    // 鼠标经过事件
    div.addEventListener('mouseenter', function () {
      
      
        console.log('轻轻地 我来了')
    })
    
    // 鼠标离开事件
    div.addEventListener('mouseleave', function () {
      
      
        console.log('正如轻轻的 我走了')
    })
</script>
<div class="mi">
    <input type="search" placeholder="小米笔记本">

    <ul class="goods-list">
        <li><a href="#">全部商品</a></li>
        <li><a href="#">空调</a></li>
        <li><a href="#">小米10S</a></li>
        <li><a href="#">小米笔记本</a></li>
        <li><a href="#">小米手机</a></li>
        <li><a href="#">冰箱</a></li>
        <li><a href="#">空调</a></li>
    </ul>
</div>

<script>
    // 获取元素
    const input = document.querySelector('[type=search]')
    const list = document.querySelector('.goods-list')
    // 文本框获得焦点 显示下拉菜单
    input.addEventListener('focus', function () {
      
      
        list.style.display = 'block'
        input.classList.add('active')
    })

    // 文本失去焦点 隐藏下拉菜单
    input.addEventListener('blur', function () {
      
      
        list.style.display = 'none'
        input.classList.remove('active')
    })
</script>
<input type="text" class="comment">

<script>
    const comment = document.querySelector('.comment')
    comment.addEventListener('keydown', function () {
      
      
        console.log('键盘按下了...');
    })

    comment.addEventListener('keyup', function () {
      
      
        console.log('键盘弹起了...');
    })
</script>
<input type="text" class="uname" placeholder="请输入你的用户名">

<script>
    const uname = document.querySelector('.uname')
    uname.addEventListener('input', function () {
      
      
        console.log(`用户输入了:${ 
        uname.value}`);
    })
</script>

2.4、事件对象

事件对象是什么?

  • 它是个对象,这个对象里有事件触发时的相关信息

应用场景:可以判断用户按下哪个键,比如按下回车键可以发布评论

如何获取事件对象?

  • 在事件绑定的回调函数的第一个参数就是事件对象
  • 一般命名为event、ev、e

具体语法:元素.addEventListener('事件类型',function(e){})

常用属性

  • type
    • 获取当前的事件类型
  • clientX/clientY
    • 获取光标相对于浏览器可见窗口左上角的位置
  • offsetX/offsetY
    • 获取光标相对于当前DOM元素左上角的位置
  • key
    • 用户按下的键盘键的值
    • 现在不提倡使用keyCode

MDN

<input type="text">

<script>
    const input = document.querySelector('input')
    input.addEventListener('keyup', function (e) {
      
      
        // console.log(e)
        if (e.key === 'Enter') {
      
      
            console.log('回车键被按下了');
        }
    })
</script>

环境对象

环境对象:指的是函数内部特殊的变量 this ,它代表着当前函数运行时所处的环境

作用:弄清楚this的指向,可以使代码更简洁

  • 函数的调用方式不同,this 指代的对象也不同
  • 【谁调用, this 就是谁】 是判断 this 指向的粗略规则
  • 直接调用函数,其实相当于是 window.函数,所以 this 指代 window
<button>点我</button>
<script>
    // 每个函数里面都有this对象 环境对象 普通函数里面this指向的是window
    // function fn() {
      
      
    //     console.log(this);
    // }

    // window.fn()
    // fn()

    const btn = document.querySelector('button')
    btn.addEventListener('click', function () {
      
      
        // console.log(this)           // btn
        this.style.color = 'orange'
    })
</script>

回调函数

当一个函数作为参数来传递给另外一个函数的时候,这个函数就是回调函数

  • 回调函数本质还是函数,只不过把它当成参数使用
  • 使用匿名函数做为回调函数比较常见
<script>
    function fn() {
      
      
        console.log('回调函数...')
    }

    setInterval(fn, 1000)
</script>

2.5、事件流

事件流指的是事件完整的流动过程

在这里插入图片描述

假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段

经验:捕获阶段是 从父到子, 冒泡阶段是从子到父

事件冒泡

当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡

<div class="father">
    father
    <div class="son">son</div>
</div>

<script>
    const father = document.querySelector('.father')
    const son = document.querySelector('.son')

    father.addEventListener('click', function () {
      
      
        alert('父元素被点击了')
    })
    
    son.addEventListener('click', function () {
      
      
        alert('子元素被点击了')
    })
</script>
  • 当一个元素触发事件后,会依次向上调用所有父级元素的同类型事件
  • 事件冒泡是默认存在的

阻止冒泡

因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素

  • 若想把事件就限制在当前元素内,就需要阻止事件冒泡

前提:阻止事件冒泡需要拿到事件对象

具体语法:事件对象.stopPropagation()

<div class="father">
    father
    <div class="son">son</div>
</div>

<script>
    son.addEventListener('click', function (e) {
      
      
        alert('子元素被点击了')
        // 阻止事件流动传播
        e.stopPropagation()
    })
</script>

注:此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效

在某些情况下需要阻止默认行为的发生,比如 阻止链接的跳转,表单域跳转

<form action="https://hua.com">
    <input type="text" name="请输入你想要搜索的礼品">
    <input type="submit">
</form>

<p>
    <a href="https://www.baidu.com">百度一下 你就知道</a>
</p>

<script>
    const form = document.querySelector('form')
    form.addEventListener('submit', function (e) {
      
      
        // 阻止默认行为
        e.preventDefault()
    })

    const a = document.querySelector('a')
    a.addEventListener('click', function (e) {
      
      
        e.preventDefault()
    })
</script>

事件解绑

on事件方式,直接使用null覆盖就可以实现事件的解绑

<button>点我</button>
<script>
    const btn = document.querySelector('button')
    btn.onclick = function () {
      
      
        alert('onclick')
    }

    // L0 事件移除解绑
    btn.onclick = null
</script>

addEventListener方式,必须使用:removeEventListener(事件类型, 事件处理函数, [获取捕获或者冒泡阶段])

<button>点我</button>
<script>
    const btn = document.querySelector('button')
    
    function sayHello() {
      
      
        alert('addEventListener')
    }
    // 绑定事件
    btn.addEventListener('click', sayHello)
    
    // 解绑事件
    btn.removeEventListener('click', sayHello)
</script>

注:匿名函数无法被解绑!

鼠标经过事件的区别

  • mouseover 和 mouseout 会有冒泡效果
  • mouseenter 和 mouseleave 没有冒泡效果 (推荐)

两种注册事件的区别:

  • 传统on注册(L0)
    • 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
    • 直接使用null覆盖就可以实现事件的解绑
    • 都是冒泡阶段执行的
  • 事件监听注册(L2)
    • 后面注册的事件不会覆盖前面注册的事件(同一个事件)
    • 必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)
    • 可以通过第三个参数去控制是在冒泡或者捕获阶段执行
    • 匿名函数无法被解绑

2.6、事件委托

事件委托是利用事件流的特征解决一些开发需求的知识技巧

优点:减少注册次数,提高程序性能

原理:事件委托其实是利用事件冒泡的特点

  • 父元素注册事件,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件

在这里插入图片描述

事件对象.target可以获得真正触发事件的元素

<ul>
    <li>第1个child</li>
    <li>第2个child</li>
    <li>第3个child</li>
    <li>第4个child</li>
    <li>第5个child</li>
    <li>第6个child</li>
    <p>I not change color</p>
</ul>

<script>
    // 点击每个小 li,当前 li 背景色变为橙色
    // const lis = document.querySelectorAll('ul li')
    // for (let i = 0; i < lis.length; i++) {
      
      
    //     lis[i].addEventListener('click', function () {
      
      
    //         lis[i].style.backgroundColor = 'orange'
    //     })
    // }

    // 利用事件委托方式 改写 
    // 获取父元素  将事件委托给父级
    const ul = document.querySelector('ul')
    ul.addEventListener('click', function (e) {
      
      
        // console.dir(e.target)       // 点击的对象
        if (e.target.tagName === 'LI') {
      
      
            e.target.style.backgroundColor = 'orange'
        }
    })
</script>

2.7、其他事件

页面加载事件

加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件

事件名:load

监听页面所有资源加载完毕:给 window 添加 load 事件

具体语法:window.addEventListener('load', function () {}

<script>
    // 等待所有页面所有资源加载完毕 就会去执行回调函数
    window.addEventListener('load', function () {
      
      
        // 执行的操作
        const btn = document.querySelector('button')
        btn.addEventListener('click', function () {
      
      
            alert(1)
        })
    })
</script>

注:不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件

当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像等完全加载

事件名:DOMContentLoad

监听页面DOM加载完毕:给 document 添加 DOMContentLoaded 事件

具体语法:document.addEventListener('DOMContentLoaded', function () {}

<script>
    // HTML标签加载完成 就会去执行回调函数    
    document.addEventListener('DOMContentLoaded', function () {
      
      
        const btn = document.querySelector('button')
        btn.addEventListener('click', function () {
      
      
            alert(2)
        })
    })
</script>

元素滚动事件

滚动条在滚动的时候持续触发的事件

应用场景:很多网页需要检测用户把页面滚动到某个区域后做一些处理, 比如固定导航栏,比如返回顶部

事件名:scroll,用于监听整个页面滚动

给 window 或 document 添加 scroll 事件

具体语法:window.addEventListener('scroll', function () {}

<script>
    // 页面滚动事件
    window.addEventListener('scroll', function () {
      
      
        // 执行的操作
        console.log('滚动条滚动了')
    })
</script>

注:监听某个元素的内部滚动直接给某个元素加即可

页面滚动事件-获取位置

scrollLeft和scrollTop (属性),两个值是可读写的

  • 获取被卷去的大小,也就是元素内容往左、往上滚出去看不到的距离

开发中,经常检测页面滚动的距离,比如页面滚动200像素,就可以显示一个元素,或者固定一个元素

HTML 文档返回对象为HTML元素:document.documentElement

<div>我是div</div>

<script>
    const div = document.querySelector('div')

    // 页面滚动事件
    window.addEventListener('scroll', function () {
      
      
        console.log(document.documentElement.scrollTop)
        const scrollHeight = document.documentElement.scrollTop

        if (scrollHeight >= 200) {
      
      
            div.style.display = 'block'
        } else {
      
      
            div.style.display = 'none'
        }
    })
</script>

注:尽量在scroll事件里面获取被卷去的距离

scrollTo() 方法可把内容滚动到指定的坐标

具体语法:元素.scrollTo(x, y)

<script>
    const backTop = document.querySelector('#backTop')

    backTop.addEventListener('click', function () {
      
      
        // document.documentElement.scrollTop = 0
        // 让页面滚动到 y 轴 200像素的位置
        window.scrollTo(0, 200)
    })
</script>

页面尺寸事件

会在窗口尺寸改变的时候触发事件:resize

<script>
    // 浏览器窗口大小发生变化时候触发的事件
    window.addEventListener('resize', function () {
      
      
        console.log(1);
    })
</script>
获取元素宽高
  • clientWidth和clientHeight
  • 获取元素的宽高(不包含border,margin,滚动条,用于js获取元素大小,只读属性
<script>
    const div = document.querySelector('div')
    console.log(div.clientWidth)
</script>
元素尺寸大小与位置

获取宽高:

获取元素的宽高包含border、padding,滚动条等,只读

  • offsetWidth和offsetHeight
  • 获取出来的是数值,方便计算

注:获取的是可视宽高, 如果盒子是隐藏的,获取的结果是0

获取位置:

  • offsetLeft和offsetTop,是只读属性
  • 获取元素距离自己定位父级元素的左、上距离
<style>
    .box {
      
      
        position: relative;
        width: 200px;
        height: 200px;
        background-color: salmon;
        margin: 100px;
    }

    .item {
      
      
        width: 100px;
        height: 100px;
        margin: 50px;
        background-color: sandybrown;
    }
</style>

<body>
    <div class="box">
        <div class="item"></div>
    </div>
    
    <script>
        const box = document.querySelector('.box')
        const item = document.querySelector('.item')
        console.log(box.offsetLeft)		// 108
        console.log(item.offsetLeft)	// 58
    </script>
</body>

3、DOM节点操作


3.1、日期对象

日期对象:用来表示时间的对象

作用:可以得到当前系统时间

用new 关键字创建对象时,一般将这个操作称为实例化

<script>
    // 获取当前系统时间
    const date = new Date()
    console.log(date)

    // 获取指定时间
    const assignDate = new Date('2025 06-06 12:30:00')
    console.log(assignDate)
</script>

日期对象方法

方法 作用 说明
getFullYear() 获得年份 获取四位年份
getMonth() 获得月份 取值为 0 ~ 11
getDate() 获取月份中的每一天 不同月份取值也不相同
getDay() 获取星期 取值为 0 ~ 6
getHours() 获取小时 取值为 0 ~ 23
getMinutes() 获取分钟 取值为 0 ~ 59
getSeconds() 获取秒 取值为 0 ~ 59
<p id="currentTime"></p>

<script>
    // 动态显示当前时间
    const currentTime = document.querySelector('#currentTime')

    function getCurrentTime() {
      
      
        let nowTime = new Date()
        let year = nowTime.getFullYear();      // 年
        let month = nowTime.getMonth() + 1;    // 月
        let date = nowTime.getDate();          // 日
        let hour = nowTime.getHours();         // 时
        let minutes = nowTime.getMinutes();         // 分
        let seconds = nowTime.getSeconds();         // 秒

        month = fillZero(month)
        date = fillZero(date)
        hour = fillZero(hour)
        minutes = fillZero(minutes)
        seconds = fillZero(seconds)
        // console.log(year, month, date, hour, minutes, seconds)

        nowTime = `${ 
        year}${ 
        month}${ 
        date}${ 
        hour}${ 
        minutes}${ 
        seconds}`

        currentTime.innerHTML = nowTime
    }

    // 补零操作函数
    function fillZero(value) {
      
      
        return value < 10 ? '0' + value : value
    }

    // 先手动调用一次,衔接定时器空窗期
    getCurrentTime()

    let timerId = setInterval(getCurrentTime, 1000)
</script>

<script>
    const time = document.querySelector('.time')
    const date = new Date()
    time.innerHTML = date.toLocaleDateString()        // 2022/3/15
    time.innerHTML = date.toLocaleTimeString()        // 13:45:55
    time.innerHTML = date.toLocaleString()          // 2022/3/15 13:46:05
</script>

时间戳

什么是时间戳?

  • 是指1970年01月01日00时00分00秒起至现在的毫秒数,它是一种特殊的计量时间的方式

计算方式:

  • 将来的时间戳 - 现在的时间戳 = 剩余时间毫秒数
  • 剩余时间毫秒数 转换为 剩余时间的 年月日时分秒 就是 倒计时时间
三种方式获取时间戳

方式一:使用 getTime() 方法

方式二:简写 +new Date()

方式三:使用 Date.now()

  • 无需实例化
  • 只能得到当前的时间戳, 而前面两种可以返回指定时间的时间戳
<div class="countdown">
    <p class="next">今天是2222年2月22日</p>
    <p class="title">距离实现远景目标还剩</p>
    <div class="clock">
        <p id="date">3000</p>
        <span></span>
        <p id="hour">00</p>
        <span>:</span>
        <p id="minutes">25</p>
        <span>:</span>
        <p id="scond">20</p>
    </div>
    <p class="tips">2035年01月01日 00:00:00</p>
</div>
<script>
    // 获取元素
    const next = document.querySelector('.next')

    const time = new Date()
    next.innerHTML = `今天是${ 
        time.getFullYear()}${ 
        time.getMonth() + 1}${ 
        time.getDate()}`

    // 先手动调用一次,补充定时器空窗期
    getTwoTimestamps()

    let timerId = setInterval(getTwoTimestamps, 1000)

    // 获取当前时间与指定时间之间时间戳的毫秒数
    function getTwoTimestamps() {
      
      
        // 获得当前时间戳
        const now = +new Date()
        // 获取将来时间戳
        const future = + new Date('2035-1-1 00:00:00')
        // console.log(now, future)
        // 得到剩余的时间戳   转换为秒
        const seconds = (future - now) / 1000
        // console.log(seconds)

        const times = getDHSS(seconds)
        // console.log(times)
        document.querySelector('#date').innerHTML = times[0]
        document.querySelector('#hour').innerHTML = times[1]
        document.querySelector('#minutes').innerHTML = times[2]
        document.querySelector('#scond').innerHTML = times[3]
    }

    // 通过秒数 计算天、小时、分钟、秒 函数
    function getDHSS(seconds) {
      
      
        let d = parseInt(seconds / 60 / 60 / 24);   // 计算天数
        let h = parseInt(seconds / 60 / 60 % 24)    // 计算小时
        let m = parseInt(seconds / 60 % 60);      // 计算分数
        let s = parseInt(seconds % 60);         // 计算当前秒数

        d = fillZero(d)
        h = fillZero(h)
        m = fillZero(m)
        s = fillZero(s)
        return [d, h, m, s]
    }

    // 补零操作函数
    function fillZero(value) {
      
      
        return value < 10 ? '0' + value : value
    }
</script>

节点类型

DOM节点:DOM树里每一个内容都称之为节点

  • 元素节点

    • 所有的标签 比如 body、 div
    • html 是根节点
  • 属性节点

    • 所有的属性 比如 href
  • 文本节点

    • 所有的文本

3.2、查

节点关系:根据标签的层次关系查找对应元素对象

  • 父节点
  • 子节点
  • 兄弟节点

父节点查找

parentNode 属性:返回最近一级的父节点,找不到返回为null

具体语法:子元素.parentNode

<div class="grandpa">
    grandpa
    <div class="father">
        father
        <div class="son">child</div>
    </div>
</div>

<script>
    const son = document.querySelector('.son')
    console.log(son.parentNode)                 // 返回dom对象
    console.log(son.parentNode.parentNode)      // 返回dom对象
</script>

子节点查找

childNodes属性:获得所有子节点、包括文本节点(空格、换行)、注释节点等

children 属性

  • 仅获得所有元素节点
  • 返回的还是一个伪数组

具体语法:子元素.children

<ul>
    <li>这是第1个小li</li>
    <li>这是第2个小li</li>
    <li>这是第3个小li</li>
    <li>这是第4个小li</li>
    <li>这是第5个小li</li>
    <li>这是第6个小li</li>
</ul>
<script>
    const ul = document.querySelector('ul')
    console.log(ul.children)        // 伪数组
</script>

兄弟关系查找

nextElementSibling 属性:下一个兄弟节点

previousElementSibling 属性:上一个兄弟节点

<ul>
    <li>这是第1个小li</li>
    <li>这是第2个小li</li>
    <li>这是第3个小li</li>
    <li>这是第4个小li</li>
    <li>这是第5个小li</li>
    <li>这是第6个小li</li>
</ul>
<script>
    const li4 = document.querySelector('ul>li:nth-child(4)')
    console.log(li4.previousElementSibling)       // 上一个兄弟 这是第3个小li
    console.log(li4.nextElementSibling)         // 下一个兄弟 这是第5个小li
</script>

3.3、增

很多情况下,需要在页面中增加元素,比如点击评论按钮,就可以在页面新增一条评论信息

一般情况下,新增节点,都会按照如下操作

  • 创建一个新的节点
  • 把创建的新的节点放入到指定的元素内部

即创造出一个新的网页元素,再添加到网页内,一般先创建节点,然后插入节点

创建元素节点方法:document.createElement('节点名称')

要想在界面显示并看到,还得插入到某个父元素中

添加到最后

插入到父元素的最后一个子元素: 父元素.appendChild(要插入的元素)

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>
<script>
    // 创建节点
    const li = document.createElement('li')
    // console.log(li)
    li.innerHTML = '我是使用js创建的li'
    const ul = document.querySelector('ul')
    ul.appendChild(li)          // 追加到父元素的末尾   
</script>

添加到指定元素前面

插入到父元素中某个子元素的前面:父元素.insertBefore(要插入的元素,添加到那个元素前面)

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>

<script>
    const li = document.createElement('li')
    li.innerHTML = '我是会插队的li'
    ul.insertBefore(li2, ul.children[0])           // 将新创建的li插入到ul第一个子元素前面
</script>

克隆节点

特殊情况下,新增节点,按照如下操作:

  • 复制一个原有的节点
  • 把复制的节点放入到指定的元素内部

具体语法:元素.cloneNode(布尔值)

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>

<script>
    const ul = document.querySelector('ul')
    // 克隆节点
    const liOne = ul.children[0].cloneNode(true)
    // console.log(liOne)
    // 追加
    ul.appendChild(liOne)
</script>

cloneNode会克隆出一个跟原标签一样的元素,括号内传入布尔值

  • 若为true,则代表克隆时会包含后代节点一起克隆
  • 若为false,则代表克隆时不包含后代节点
  • 默认为false

3.5、删

若一个节点在页面中已不需要时,可以删除它

在 JavaScript 原生DOM操作中,要删除元素必须通过父元素删除

具体语法:父元素.removeChild(子元素)

<ul>
    <li>我是将要被移除的li</li>
</ul>

<script>
    // 获取父节点
    const ul = document.querySelector('ul')
    // 删除 ul下面第一个li
    ul.removeChild(ul.children[0])      
</script>

注:

  • 如不存在父子关系则删除不成功
  • 删除节点和隐藏节点(display:none) 有区别的: 隐藏节点还是存在的,但是删除,则从html结构中删除节点

4、操作BOM


41、window对象

BOM(Browser Object Model ) 是浏览器对象模型,BOM提供了独立于内容的、可以与浏览器窗口进行互动的对象结构

在这里插入图片描述

  • window对象是一个全局对象,也可以说是JavaScript中的顶级对象
  • 像document、alert()、console.log()这些都是window的属性,基本BOM的属性和方法都是window的
  • window对象下的属性和方法调用的时候可以省略window

JavaScript 内置的一个用来让代码延迟执行的函数,叫 setTimeout

具体语法:setTimeout(回调函数,延迟的毫秒数)

setTimeout 仅仅只执行一次,所以可以理解为就是把一段代码延迟执行,平时编写会省略window

<script>
    // 延迟函数
    let timer = setTimeout(function () {
      
      
        console.log('5秒钟后我会自动执行一次');
    }, 5000)

    // 清除延时器
    clearTimeout(timer)
</script>

注:

  • 延时器需要等待,所以在它后面的代码先执行
  • 每一次调用延时器都会产生一个新的延时器

两种定时器对比:执行的次数

  • 延时函数:只执行一次(定时炸弹)
  • 间歇函数:每隔一段时间就执行一次,除非手动清除(笛音雷)

JS 执行机制

JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事

这是因为 Javascript 这门脚本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作DOM 而诞生的。比如我们对某个 DOM 元素进行添加和删除操作,不能同时进行。应该先进行添加,之后再删除

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是:如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉

为解决该问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许JavaScript 脚本创建多个线程。于是,JS 中出现了同步和异步

同步任务

同步任务都在主线程上执行,形成一个执行栈

异步任务

一般而言,异步任务有以下三种类型:

  • 普通事件,如 click、resize 等
  • 资源加载,如 load、error 等
  • 定时器,包括 setInterval、setTimeout 等

异步任务:异步任务相关添加到任务队列中(任务队列也称为消息队列)

执行顺序
  1. 先执行执行栈中的同步任务
  2. 异步任务放入任务队列中
  3. 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行
<script>
    console.log(1)      // 执行栈
    console.log(2)      // 执行栈
    // 任务队列
    setTimeout(function () {
      
      
        console.log(3)
    }, 0)
    console.log(4)          // 执行栈
</script>

在这里插入图片描述

由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环(event loop)

location对象

location 的数据类型是对象,它拆分并保存了 URL 地址的各个组成部分

href属性

href 属性获取完整的 URL 地址,对其赋值时用于地址的跳转

<script>
    console.log(location.href)
    location.href = 'https://www.hua.com'
</script>
search属性

search 属性获取地址中携带的参数,符号 ?后面部分,通常和表单一起使用(且表单提交为get方式)

<form action="" method="get">
    <input type="text" name="uname">
    <input type="password" name="pwd">
    <input type="submit" value="提交">
    <input type="reset" value="重置">
</form>
<script>
    console.log(location.search)		// ?uname=admin&pwd=666666
</script>
hash属性

hash 属性获取地址中的啥希值,符号 # 后面部分

<a href="#/my/">我的</a>
<a href="#/my/friend">我的关注</a>
<a href="#/my/favorite">我的收藏</a>
<a href="#/download">下载</a>
<script>
    console.log(location.hash)		// #/my/favorite
</script>

经常用于不刷新页面,显示不同页面,比如 网易云音乐

reload方法

reload 方法用来刷新当前页面,传入参数 true 时表示强制刷新

<button>刷新</button>
<script>
    const btn = document.querySelector('button')
    btn.addEventListener('click', function () {
      
      
        location.reload()   	// f5刷新页面
        location.reload(true)   // ctrl + f5 强制刷新
    })
</script>
其他方法
<script>
    console.log(location.host)      	// 设置或返回主机名和当前URL的端口号
    console.log(location.hostname)      // 设置或返回当前URL的主机名
    console.log(location.protocol)      // 协议
    console.log(document.title)         // 获取文档标题
    document.title = '开启探索之旅'      // 设置文档标题
</script>

history对象

history 的数据类型是对象,主要管理历史记录, 该对象与浏览器地址栏的操作相对应,如前进、后退、历史记录等

<script>
    history.forward()     	// 加载下一个url
    history.back()     	    // 加载前一个url

    history.go(1)       // 前进 等价于 history.forward()
    history.go(-1)     // 后退 等价于 history.back()
</script>

navigator对象

navigator的数据类型是对象,该对象下记录了浏览器自身的相关信息

通过 userAgent 检测浏览器的版本及平台

<script>
    // 检测 userAgent(浏览器信息)
    !(function () {
      
      
        const userAgent = navigator.userAgent
        // 验证是否为Android或iPhone
        const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
        const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
        // 如果是Android或iPhone,则跳转至移动站点
        if (android || iphone) {
      
      
            location.href = 'file:///E:/VSCodeProject/Project/m_hua/index.html'
        }
    })()
</script>

4.2、本地存储

  • 数据存储在用户浏览器
  • 设置、读取方便、甚至页面刷新不丢失数据
  • 容量较大,sessionStorage和localStorage约 5M 左右

本地存储分类

localStorage

可以将数据永久存储在本地(用户的电脑),除非手动删除,否则关闭页面也会存在

特性:

  • 可以多窗口(页面)共享(同一浏览器可以共享)
  • 以键值对的形式存储使用

存储:localStorage.setItem(key, value)

查询:localStorage.getItem(key)

删除:localStorage.removeItem(key)

<script>
    let name = 'admin'
    let pwd = '666666'

    // 键值对
    localStorage.setItem('uname', name)
    localStorage.setItem('upwd', pwd)

    console.log(localStorage.getItem(uname))        // admin

    localStorage.removeItem('uname')
</script>

注:本地存储只能存储字符串数据类型

浏览器查看本地存储数据

在这里插入图片描述

sessionStorage

特性:

  • 生命周期为关闭浏览器窗口
  • 在同一个窗口(页面)下数据可以共享
  • 以键值对的形式存储使用
存储复杂数据类型

本地只能存储字符串,无法存储复杂数据类型

解决方案:需要将复杂数据类型转换成JSON字符串,在将其存储到本地

对象转JSON串:JSON.stringify(复杂数据类型)

JSON串转对象:JSON.parse(JSON字符串)

<script>
    const user = {
      
      
        uid: 1001,
        uname: 'admin',
        pwd: 'abc666',
        email: '[email protected]',
        gender: '女'
    }

    // 存储复杂数据类型
    // localStorage.setItem(user.uid, user)
    // console.log(localStorage.getItem(user.uid))

    // 需要将其转换为 json 字符串    对象转json字符串  
    const userJson = JSON.stringify(user)
    localStorage.setItem(user.uid, userJson)
    // json字符串
    console.log(userJson)

    // json字符串转对象
    const u1 = JSON.parse(userJson)
    // 对象
    console.log(u1)
</script>

在这里插入图片描述

5、正则表达式


5.1、什么是正则表达式?

正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在 JavaScript中,正则表达式也是对象

通常用来查找、替换那些符合正则表达式的文本,许多语言都支持正则表达式

应用场景:

  • 验证表单(匹配):用户名表单只能输入英文字母、数字或者下划线
  • 过滤敏感词(替换):过滤掉页面内容中的一些敏感词
  • 提取内容(提取):从字符串中获取想要的特定部分

5.2、正则使用

  1. 定义规则
  2. 查找

定义正则表达式语法:const 变量名 = /表达式/

其中/ / 是正则表达式字面量

test()

判断是否有符合规则的字符串:regObj.test(被检测的字符串)

test() 方法 用来查看正则表达式与指定的字符串是否匹配

<script>
    const str = '昨夜西风凋碧树,独上高楼,望尽天涯路'
    // 定义规则
    const reg = /东风/
    // 是否匹配
    console.log(reg.test(str))      // false
</script>

如果正则表达式与指定的字符串匹配 ,返回true,否则false

exec()

检索(查找)符合规则的字符串:regObj.exec(被检测的字符串)

exec() 方法 在一个指定字符串中执行一个搜索匹配

<script>
    const str = '昨夜西风凋碧树,独上高楼,望尽天涯路'
    // 定义规则
    const reg = /西风/
    // 是否匹配
    console.log(reg.exec(str))      // 返回数组
</script>

如果匹配成功,exec() 方法返回一个数组,否则返回null

5.3、元字符

普通字符:大多数的字符仅能够描述它们本身,这些字符称作普通字符,也就是说普通字符只能够匹配字符串中与它们相同的字符

元字符(特殊字符)

是一些具有特殊含义的字符,可以极大提高了灵活性和强大的匹配功能

  • 比如,规定用户只能输入英文26个英文字母,普通字符的话 abcdefghijklm……
  • 换成元字符写法: [a-z]

正则表达式

元字符分类

  • 边界符(表示位置,开头和结尾,必须用什么开头,用什么结尾)
  • 量词 (表示重复次数)
  • 字符类 (比如 \d 表示 0~9)
边界符

正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符

边界符 描述
^ 匹配字符串的开始
$ 匹配字符串的结束
<script>
    // 必须以a开头 z结尾 中间不能包含任何内容  包含任何内容都会返回 false
    console.log(/^az$/.test('az'))      // true
    console.log(/^az$/.test('abz'))     // false
    console.log(/^az$/.test('aazz'))     // false
</script>

如果 ^ 和 $ 在一起,表示必须是精确匹配

量词

量词用来设定某个模式出现的次数

量词 描述
* 匹配前一项零次或更多次
+ 匹配前一项一次或更多次
? 匹配前一项零次或一次
{n} 匹配前一项n次
{n,} 匹配前一项n次或更多次
{n,m} 匹配前一项n次到m次
<script>
    // 零次或更多次
    console.log(/^a+z$/.test('aaaaaaz'))      // true
    // 零次和一次
    console.log(/^a?z$/.test('z'))     // true
    console.log(/^a?z$/.test('aaz'))     // false
    // 1到3次  最少一次 最多三次
    console.log(/^a{1,3}z$/.test('aaaz'))     // true
    console.log(/^a{1,3}z$/.test('aaaaz'))     // false
</script>
字符类

预定义:指的是 某些常见模式的简写方式

后面的字符串只要包含 abc 中任意一个字符,都返回 true

[ ] 匹配字符集合

<script>
    console.log(/^[adc]$/.test('a'))      // true
    console.log(/^[adc]$/.test('d'))      // true
    console.log(/^[adc]$/.test('c'))      // true
    console.log(/^[adc]$/.test('ad'))      // false
</script>

使用连字符 - 表示一个范围

  • [a-z] 表示 a 到 z 26个英文字母都可以
  • [a-zA-Z] 表示大小写都可以
  • [0-9] 表示 0~9 的数字都可以

[ ] 里面加上 ^ 取反符号

  • [^A-Z] 匹配除了大写字母以外的字符
  • 注意必须要写到中括号里面,否则语义发生变化
字符类 描述
\s 任何空白字符
\S 任何非空白字符
\d 匹配数字字符,等价[0-9]
\D 除了数字之外的任何字符,等价于[^0-9]
\w 匹配一个数字、下划线或字母字符,等价于[A-Za-z0-9_]
\W 除了数字、下划线或字母字符,等价于[^a-zA-z0-9_]
<script>
    const arr = ['15736369663', '18718888898', '15800022263', '15726269663', '15800000056', '15999999766']
    // 匹配以 157开头 中间0-9数字(只能出现五次) 663结尾的号码
    const reg = /^157\d{5}663$/
    let newArr = []
    for (let i = 0; i < arr.length; i++) {
      
      
        if (reg.test(arr[i])) {
      
      
            newArr.push(arr[i])
        }
    }
    console.log(newArr)
</script>

5.4、修饰符

修饰符约束正则执行的某些细节行为,如是否区分大小写、是否支持多行匹配等

具体语法:/表达式/修饰符

  • i 是单词 ignore 的缩写,正则匹配时字母不区分大小写
  • g 是单词 global 的缩写,匹配所有满足正则表达式的结果
replace 替换

具体语法:字符串.replace(/正则表达式/,'替换的文本')

<script>
    const str = 'php是世界上最好的语言 快来学习PHP吧!'
    const newStr = str.replace(/php/ig, 'Java')
    console.log(newStr)
</script>

猜你喜欢

转载自blog.csdn.net/zhang_0202/article/details/129626251