JavaScript-Web APIs-BOM

日期对象

目标:掌握日期对象,可以让网页显示日期,动态获取当前计算机的时间。

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

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

日期对象实例化

在代码中发现了 new 关键字时,一般将这个操作称为实例化
创建一个时间对象并获取时间:
// 获取系统当前默认时间
const date = new Date(); 
 // 获取指定时间
const date = new Date('2022-5-1 08:30:00') 
console.log(typeof date)

注意:

ECMAScript 中内置了获取系统时间的对象 Date,使用 Date 时与之前学习的内置对象 console 和 Math 不同,它需要借助 new 关键字才能使用。

日期对象方法

使用场景:因为日期对象返回的数据我们不能直接使用,所以需要转换为实际开发中常用的格式

     // 获得日期对象
    const date = new Date()
    // 使用里面的方法
    console.log(date.getFullYear())  //获取四位年份
    console.log(date.getMonth() + 1)  // 月份要 + 1
    console.log(date.getDate())
    console.log(date.getDay())  // 星期几

方法
作用说明
date.getFullYear()
获取四位年份
date.getMonth()
获取月份,取值为 0 ~ 11月份要 + 1
date.getDate()
获取月份中的每一天,不同月份取值也不相同
date.getDay()
获取星期,取值为 0 ~ 6
date.getHours()
获取小时,取值为 0 ~ 23
date.getMinutes()
获取分钟,取值为 0 ~ 59
date.getSeconds()
获取秒,取值为 0 ~ 59

date.toLocaleString()

获取本地具体全时间2025/10/2 23:35:54

date.toLocaleDateString()

获取本地年月日时间2025/10/2

date.toLocaleTimeString()

获取本地时分秒时间23:37:40

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    div {
      width: 300px;
      height: 40px;
      border: 1px solid pink;
      text-align: center;
      line-height: 40px;
    }
  </style>
</head>

<body>
  <div></div>
  <script>
    const div = document.querySelector('div')
    function getMyDate() {
      const date = new Date()
      let h = date.getHours()
      let m = date.getMinutes()
      let s = date.getSeconds()
      h = h < 10 ? '0' + h : h
      m = m < 10 ? '0' + m : m
      s = s < 10 ? '0' + s : s
      return `今天是: ${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}号 ${h}:${m}:${s}`
    }

    div.innerHTML = getMyDate()
     setInterval(function () {
      div.innerHTML = getMyDate()
    }, 1000) 
  </script>
</body>

</html>

 <script>
    const div = document.querySelector('div')
    // 得到日期对象
    const date = new Date()
    div.innerHTML = date.toLocaleString()  // 2022/4/1 09:41:21
    // div.innerHTML = date.toLocaleDateString()  // 2022/4/1
    // div.innerHTML = date.toLocaleTimeString()  // 2022/4/1
      setInterval(function () {
      const date = new Date()
      div.innerHTML = date.toLocaleString()  // 2022/4/1 09:41:21

    }, 1000)  
    
  </script>

   // 我要根据日期 getDay()   0 ~ 6  返回的是 星期一
    const arr = ['星期天', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
    console.log(new Date().getDay()) // 0  1  2  3  4 5  6 
    console.log(arr[new Date().getDay()]) //  星期五

时间戳

使用场景: 如果计算倒计时效果,前面方法无法直接计算,需要借助于时间戳完成
什么是时间戳:
 是指1970年01月01日00时00分00秒起至现在的毫秒数,它是一种特殊的计量时间的方式
倒计时算法:
将来的某个具体时间的时间戳 - 现在的时间的时间戳 = 剩余时间毫秒数
将剩余时间毫秒数 转换为 剩余时间的 年月日时分秒 就是 倒计时时间
(比如 将来时间戳 2000ms - 现在时间戳 1000ms = 1000ms
1000ms 转换为倒计时时间就是 0小时0分1秒)

三种方式获取时间戳

方法1:使用 getTime() 方法获取时间戳

 // 1. 实例化
  const date = new Date()
  // 2. getTime()获取时间戳
  console.log(date.getTime())
方法2:简写 +new Date()  (重点记住)
  +new Date() 因为可以返回当前时间戳或者指定的时间戳
  // 2. +new Date()获取时间戳
    console.log(+new Date()) //获取默认当前时间的时间戳
  console.log(+new Date('2022-4-1 18:30:00')) // 获取指定以前的时间的时间戳
console.log(+new Date('2035-10-1 18:30:00')) // 获取指定将来的时间的时间戳

方法3:使用 Date.now() :只能得到当前的时间戳

无需实例化
但是只能得到当前的时间戳, 而前面两种可以返回指定时间的时间戳
  // 3. Date.now() 获取当前的时间戳
    console.log(Date.now());

国庆结束倒计时案例

  // 1. 得到当前的时间戳

    const now = +new Date()

    // 2. 得到将来的时间戳

    const last = +new Date('2025-10-9 00:00:00')

    console.log(now, last)

// 3. 得到剩余的时间戳 count(毫秒)  记得转换为 剩余的总秒数

const count = (last - now) / 1000

  // 4. 将剩余的总秒数count   转换为  剩余的天/剩余的时/剩余的分/剩余的秒

   //d = parseInt(剩余的总秒数/ 60/60 /24);      // 将 剩余的总秒数  转换为 剩余的天

  // h = parseInt(剩余的总秒数 / 60 / 60 % 24)   //   将 剩余的总秒数   转换为  剩余的小时

  // m = parseInt(剩余的总秒数 / 60 % 60);     //   将 剩余的总秒数   转换为   剩余的分数

  // s = parseInt(剩余的总秒数 % 60);      //将 剩余的总秒数   转换为  剩余的秒数

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
      .countdown {
      width: 260px;
      height: 305px;
      text-align: center;
      line-height: 1;
      color: #fff;
      background-color: brown;
      /* background-size: 240px; */
      /* float: left; */
      overflow: hidden;
    }

     .countdown .next {
      font-size: 16px;
      margin: 25px 0 14px;
    }

    .countdown .title {
      font-size: 33px;
    }

      .countdown .tips {
      margin-top: 80px;
      font-size: 23px;
    }

    .countdown small {
      font-size: 17px;
    }

     .countdown .clock {
      width: 150px;
      margin: 18px auto 0;
      overflow: hidden;
    }

     .countdown .clock span,
    .countdown .clock i {
      display: block;
      text-align: center;
      line-height: 20px;
      font-size: 14px;
      float: left;
    }

      .countdown .clock span {
      width: 20px;
      height: 20px;
      border-radius: 2px;
      background-color: #303430;
    } 

    .countdown .clock i {
      width: auto;
      font-style: normal;
    }



  </style>
</head>
<body>

  <div class="countdown">
    <p class="next">今天是2225年10月03日</p>
    <p class="title">国庆结束倒计时</p>
    <p class="clock">
       <span id="day">--</span>
      <i>天</i>
      <span id="hour">--</span>
      <i>时</i>
      <span id="minutes">--</span>
      <i>分</i>
      <span id="scond">--</span>
      <i>秒</i>
    </p>
   <!--  <p class="tips">18:30:00下课</p> -->
  </div>

 <script>

  // 函数封装倒计时方法 getCountTime
 function getCountTime() {

  // 1. 得到当前的时间戳
    const now = +new Date()
    // 2. 得到将来的时间戳
    const last = +new Date('2025-10-9 00:00:00')
    console.log(now, last)
// 3. 得到剩余的时间戳 count(毫秒)  记得转换为 剩余的总秒数
const count = (last - now) / 1000

  // 4. 将剩余的总秒数count   转换为  剩余的天/剩余的时/剩余的分/剩余的秒
   //d = parseInt(剩余的总秒数/ 60/60 /24);      // 将 剩余的总秒数  转换为 剩余的天
  // h = parseInt(剩余的总秒数 / 60 / 60 % 24)   //   将 剩余的总秒数   转换为  剩余的小时
  // m = parseInt(剩余的总秒数 / 60 % 60);     //   将 剩余的总秒数   转换为   剩余的分数
  // s = parseInt(剩余的总秒数 % 60);      //将 剩余的总秒数   转换为  剩余的秒数 
    
   
 let d=parseInt(count/60/60 /24)
     d=d<10? '0' +d:d
 let h = parseInt(count / 60 / 60 % 24)
     h = h < 10 ? '0' + h : h
let m = parseInt(count / 60 % 60)
    m = m < 10 ? '0' + m : m
let s = parseInt(count % 60)
    s = s < 10 ? '0' + s : s
  console.log(d,h, m, s)

      //  5. 把天时分秒写到对应的盒子里面
       document.querySelector('#day').innerHTML = d
      document.querySelector('#hour').innerHTML = h
      document.querySelector('#minutes').innerHTML = m
      document.querySelector('#scond').innerHTML = s

 }

      // 先调用一次
    getCountTime()

    // 开启定时器
    setInterval(getCountTime, 1000)

 </script>
  
</body>
</html>

节点操作

 获取/创建节点 

什么是DOM节点

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

节点类型
节点类型
元素节点(重点)
属性节点
文本节点
所有的标签 比如 body、
div都是元素节点
html 是根节点
所有的属性都是属性节点
比如 href  class属性
所有的文本
比如标签里面的文字 标题

查找节点

DOM 树中的任意节点都不是孤立存在的, 任何当前节点元素对象 都有父节点对象 兄弟节点对象 

当前节点元素对象查找/获取父节点

当前节点元素对象.parentNode     

  • 获取/查找父节点
  •  返回最近一级的父节点 找不到返回为null
<!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>

  <div class="yeye">
    <div class="dad">
      <div class="baby">x</div>
    </div>
  </div>
  
  <script>
    //  获取当前元素节点对象  
  const baby = document.querySelector('.baby')
 console.log(`获取当前元素节点对象 ${baby}`)  // 获取当前元素节点对象 [object HTMLDivElement]
console.log(`获取当前元素节点对象名称 ${baby.tagName}`)
console.log(`获取当前元素节点对象的父节点对象 ${baby.parentNode}`) // [object HTMLDivElement]
console.log(`获取当前元素节点对象的父节点对象的父节点对象 ${baby.parentNode.parentNode}`) //[object HTMLDivElement]


  </script>

</body>
</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>
  <style>
    .box {
      position: relative;
      width: 1000px;
      height: 200px;
      background-color: pink;
      margin: 100px auto;
      text-align: center;
      font-size: 50px;
      line-height: 200px;
      font-weight: 700;
    }

    .box1 {
      position: absolute;
      right: 20px;
      top: 10px;
      width: 20px;
      height: 20px;
      background-color: skyblue;
      text-align: center;
      line-height: 20px;
      font-size: 16px;
      cursor: pointer;
    }


  </style>
</head>
<body>

  <div class="box">
    我是广告
    <div class="box1">X</div>
  </div>
    
    <script>
       // 1.  获取当前子节点元素对象
      const box1 = document.querySelector('.box1')
        // 2.  给当前元素对象设置事件 
     box1.addEventListener('click', function () {
        // 影藏父节点元素
       this.parentNode.style.display = 'none'
     }) 

 


   </script>
  
</body>
</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>
  <style>
    .box {
      position: relative;
      width: 1000px;
      height: 200px;
      background-color: pink;
      margin: 100px auto;
      text-align: center;
      font-size: 50px;
      line-height: 200px;
      font-weight: 700;
    }

    .box1 {
      position: absolute;
      right: 20px;
      top: 10px;
      width: 20px;
      height: 20px;
      background-color: skyblue;
      text-align: center;
      line-height: 20px;
      font-size: 16px;
      cursor: pointer;
    }


  </style>
</head>
<body>

  <div class="box">
    我是广告
    <div class="box1">X</div>
  </div>
  <div class="box">
    我是广告
    <div class="box1">X</div>
  </div>
  <div class="box">
    我是广告
    <div class="box1">X</div>
  </div>
    
    <script>
   //  获取所有的box1类的元素节点对象  的集合 NoteList
 const closeBtnNoteList = document.querySelectorAll('.box1')
  // 遍历 NoteList 获取每一个 元素节点对象 
for (let i = 0; i < closeBtnNoteList.length; i++) { 
  const everyCloseBtn = closeBtnNoteList[i]
    console.log(`获取每一个 元素节点对象的名称 ${everyCloseBtn.tagName}`) 
      everyCloseBtn.addEventListener('click', function () {
        // 关闭我的爸爸 所以只关闭当前的父元素
        //this= everyCloseBtn
         this.parentNode.style.display = 'none'
      })
 } 
 


   </script>
  
</body>
</html>

当前节点元素对象查找/获取子节点

当前节点元素对象.childNodes

  • 获得所有子节点、包括文本节点(包括空格、换行也算[被认为是空白文本节点 ])、注释节点等
  • 返回一个数组 NodeList   长度.length 

当前节点元素对象.children(重点用这个)

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

    

<!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>

   <ul>
    <li>1    2      3</li>
    <li>HTML</li>
    <li>CSS</li>
    <!-- 说明 -->
    <li>JavaScript 基础</li>
    <li>Web APIs</li>
  </ul>

  <script>


   //获取当前节点元素对象 
const ul = document.querySelector('ul')   
 // 所有的子节点
  console.log(`获取所有的子节点:${ul.childNodes} ${ul.childNodes.length}`) //获取所有的子节点:[object NodeList] 13
 // 只包含元素子节点
  console.log(`获取只包含元素子节点:${ul.children} ${ul.children.length} `) //获取只包含元素子节点:[object HTMLCollection] 5 

 

  </script>
  
</body>
</html>

当前节点元素对象查找/获取兄弟节点

当前节点元素对象.nextElementSibling

获取下一个兄弟节点对象

当前节点元素对象.previousElementSibling

获取上一个兄弟节点对象

  

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=
  , initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <ul>
    <li>hello</li>
    <li>world</li>
    <li>JavaScript 基础</li>
    <li>Web APIs</li>
     <li>5</li>
  </ul>

 <script>
   // 获取一个指定的元素节点对象
    const li2 = document.querySelector('ul li:nth-child(2)')
    // 获取当前节点的上一个兄弟节点
   console.log(`${li2.previousElementSibling}  ${li2.previousElementSibling.tagName} ${li2.previousElementSibling.textContent}`)   //[object HTMLLIElement]  LI hello
   // 获取当前节点的下一个兄弟节点
   console.log(`${li2.nextElementSibling}  ${li2.nextElementSibling.tagName} ${li2.nextElementSibling.textContent}`)    //[object HTMLLIElement]  LI JavaScript 基础
     

 </script>
  
</body>
</html>

增加/插入节点

在已有的 DOM 节点中插入新的 DOM 节点时,需要关注两个关键因素:首先要得到新的 DOM 节点,其次在哪个位置插入这个节点。
很多情况下,我们需要在页面中增加元素
 增加元素节点对象,按照如下操作:
  • 新创建一个新的节点对象 
    //创造一个新的元素节点对象
    const newElement = document.createElement('标签名')

  • 把新创建的节点对象插入到指定的某个节点元素内部   
      // 插入到父元素的最后一个子元素:
       parentElement.appendchild(要插入的元素)
    // 插入到父元素中某个子元素的前面
      parentElement.insertBefore(要插入的元素,在哪个元素前面) 

 

 appendchild 与insertBefore 的区别

特性appendChild()insertBefore()
插入位置追加到末尾 (最后一个子节点之后)插入到指定节点之前
参数1 个参数:newChild2 个参数:newChildreferenceChild
灵活性较低,只能添加到末尾较高,可以精确控制插入位置
当 referenceChild 为 nullN/A行为等同于 appendChild(),将节点添加到末尾
 parentElement.appendChild(newChild)

功能:将一个新节点(newChild对象 添加到父元素的子节点列表的末尾

语法

parentElement.appendChild(newChild);

行为

  • 如果 newChild 是一个新创建的节点,它会被直接添加到 parentElement父元素节点 的最后。
  • 如果 newChild 已经存在于 DOM 中的某个位置,它会先从原来的位置被移除,然后再被添加到 parentElement父元素节点 的末尾。
 <!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>
    // 1. 创建一个可以作为父节点的容器元素
const container = document.createElement('div');

// 2. 创建一个新元素
const newParagraph = document.createElement('p');
newParagraph.textContent = 'This is a new paragraph.';

// 3. 将新元素插入到容器内(container 是父节点)
container.appendChild(newParagraph);

// 4. 最后,把整个容器插入到页面的 body 中
document.body.appendChild(container);

   </script>
  
 </body>
 </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>
     // 1. 创建节点
    const div = document.createElement('div')
     div.innerHTML = '我是div'
     div.style.color='red'
   // 2. 给父元素节点body, 追加元素节点  作为最后一个子元素
   const parentElement =  document.body
    parentElement.appendChild(div)   
 </script>
</body>
</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>

  <ul id="list">
    <li>Item 1</li>
    <li>Item 2</li>
</ul>
  
  <script>
     const parent_ul = document.getElementById('list');
     const new_li = document.createElement('li');  
     console.log(`获取父元素节点${parent_ul.tagName} 只包含元素子节点: ${parent_ul.children}  ${parent_ul.children.length} `)//2
      new_li.textContent = 'Item 3';
      new_li.style.color='red'
      //   给父元素节点parent_ul, 追加元素节点  作为最后一个子元素
      parent_ul.appendChild(new_li);
      console.log(`获取父元素节点${parent_ul.tagName} 只包含元素子节点: ${parent_ul.children}  ${parent_ul.children.length} `)//3
 </script>
</body>
</html>

 

 parentElement.insertBefore(newChild, referenceChild)

功能:在父元素的子节点列表中,将一个新节点元素对象(newChild插入到指定的参考节点(referenceChild)之前

语法: 

parentElement.insertBefore(newChild, referenceChild);

行为

  • newChild 会被插入到 referenceChild 的前面
  • 同样,如果 newChild 在父元素的子节点列表中已经存在,它会先被移除再插入。
  • referenceChild 必须是 parentElement 的一个直接子节点,否则会抛出异常(除非 referenceChild 为 null,此时行为等同于 appendChild)。
 <!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>

  <ul id="list">
    <li>Item 1</li>
    <li>Item 2</li>
</ul>
  
  <script>
     const parent_ul = document.getElementById('list');
     const new_li = document.createElement('li');  
     console.log(`获取父元素节点${parent_ul.tagName} 只包含元素子节点: ${parent_ul.children}  ${parent_ul.children.length} `)//2
      new_li.textContent = 'Item 3';
      new_li.style.color='red'
      //   给父元素节点parent_ul, 追加元素节点  作为最后一个子元素
      parent_ul.appendChild(new_li);
      console.log(`获取父元素节点${parent_ul.tagName} 只包含元素子节点: ${parent_ul.children}  ${parent_ul.children.length} `)//3

      //在父元素节点parent_ul 的 子元素节点referenceChild   前插入 一个新的子节点元素对象new_li0
      const referenceChild = parent_ul.children[0];  
       const new_li0 = document.createElement('li');
         new_li0.textContent = 'Item 0';
         new_li0.style.color='blue'
        parent_ul.insertBefore(new_li0,referenceChild)

 </script>
</body>
</html>

  

只能将一个新创建的元素节点插入到一个“可以拥有子节点”的父元素节点内

  • 在DOM树中,大多数元素节点(如 <div><p><ul> 等)都可以作为父节点,包含其他子节点。 这些元素可以插入新创建的子元素节点。
  • 但有一些节点不能作为父节点,或者说不能包含子节点,例如:
    • 文本节点 (Text Node):它本身是文本内容,不能再包含其他元素。
    • 注释节点 (Comment Node):同上。
    • 自闭合标签 (Void Elements):如 <img><br><input><hr> 等。

       这些HTML元素不允许有子节点。虽然它们在DOM中是元素节点,但你不能向它们内部        插入子元素。

const img = document.createElement('img');
const textNode = document.createTextNode('Hello');

// ❌ 错误!不能向 <img> 这种自闭合标签内部插入子节点
img.appendChild(textNode); // 运行时可能不会报错,但不符合HTML规范,行为不可预测

// ✅ 正确!<div> 可以拥有子节点
const div = document.createElement('div');
div.appendChild(textNode); // 正常工作

 这个“父元素节点对象”从哪来的?

“父元素节点对象”是你通过各种方式获取创建的一个可以拥有子节点的DOM元素对象

“父元素节点对象”是你代码中已经存在的一个DOM元素,它必须是一个可以拥有子节点的容器型元素(如 div, p, ul, section 等)。

使用 document.createElement() 创建父元素节点对象:
 //这个 parentDiv 就是父元素节点对象
const parentDiv = document.createElement('div'); // 创建一个新的 <div> 元素
const childP = document.createElement('p');
parentDiv.appendChild(childP); // 现在 parentDiv 是 childP 的父节点
使用选择器从现有DOM中获取父元素节点对象:

//existingDiv 是从页面中通过 getElementById、querySelector 
//等方法获取的,它就是父元素节点对象。
const existingDiv = document.getElementById('myDiv'); // 获取页面上已有的一个 <div>
const newSpan = document.createElement('span');
existingDiv.appendChild(newSpan); // existingDiv 是父节点
通过遍历DOM树获取父元素节点对象:
const body = document.body; // <body> 元素
const firstChild = body.children[0]; // 获取 body 的第一个子元素
const newElement = document.createElement('strong');
firstChild.appendChild(newElement); // firstChild 是父节点

如果我把这个新创建的元素节点对象插入到非父元素节点对象内会报错吗?

不一定报错,但行为是无效或不符合规范的。

这取决于你试图插入到哪种“非父元素节点对象”:

  1. 插入到自闭合标签内(如 <img>, <input>): 

    通常不会抛出JavaScript错误,但这是不符合HTML规范的。

    浏览器可能会忽略你的操作,或者以不可预测的方式处理。

    不应该这样做。

  2. 插入到文本节点或注释节点:

    文本节点和注释节点没有 appendChild 或 insertBefore 方法。

    如果你尝试调用,会抛出 TypeError。

const textNode = document.createTextNode('Some text');
const newSpan = document.createElement('span');

// ❌ TypeError: textNode.appendChild is not a function
textNode.appendChild(newSpan);
const nonExistent = document.getElementById('not-exist');
// nonExistent 是 null

const newDiv = document.createElement('div');
// ❌ TypeError: Cannot read property 'appendChild' of null
nonExistent.appendChild(newDiv);

复制(克隆)节点

cloneNode() 是 JavaScript 中用于复制现有 DOM 节点的方法。

它非常有用,尤其是在你需要创建与现有节点结构完全相同的新节点时,可以避免重复编写创建和设置属性的代码。

cloneNode() 的基本语法:

const clonedNode = originalNode.cloneNode(deep);

 originalNode: 你想要复制的原始节点(可以是元素、文本节点、注释等)。

 deep (可选): 一个布尔值,表示是否进行“深拷贝”。

  • true: 进行深拷贝。复制该节点及其所有子节点
  • false 或 省略: 进行浅拷贝。只复制该节点本身,不复制其子节点

  返回值: 返回一个新的节点对象,它是原始节点的一个副本。

   浅拷贝 (cloneNode(false) 或 cloneNode())

只复制节点本身,不复制它的子节点。

浅拷贝 使用场景

  • 复制一个空容器,后续动态填充内容:如复制一个 <div class="card"> 模板,再往里加标题、图片等
  • 只需要复制外层结构,不要内容:如复制一个列表项外壳,再设置新文本
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
     /* 原节点对象originalDiv的css样式 :  一个 200x100 的浅蓝色矩形 */
     #original {
            width: 200px;
            height: 100px;
            background-color: lightblue;
            border: 2px solid blue;
            margin: 10px;
             
        }

      /* 
        克隆节点 shallowCloneDiv 的css样式 
        与原节点 originalDiv 结构和内容 css样式 属性 完全相同 

        #original {
            width: 200px;
            height: 100px;
            background-color: lightblue;
            border: 2px solid blue;
            margin: 10px;
             
        }
        */

  </style>
   
</head>
<body>

  <div id="original">
    <p>这是第一个段落。</p>
    <p>这是第二个段落。</p>
</div>

<script>
   // 获取原有节点对象originalDiv
  const originalDiv = document.getElementById('original');
   // 浅拷贝:只复制 <div> 标签本身,不复制里面的 <p> 元素 。 
    //克隆节点对象 shallowCloneDiv 与  原有节点对象originalDiv的 结构和内容 css样式 属性 一模一样 的 <div> 元素。 但它内部是空的。
   const shallowCloneDiv = originalDiv.cloneNode(false);
 
   console.log(shallowCloneDiv.innerHTML); // 输出: "" (空字符串,因为子节点没有被复制)
 
 // 将 克隆节点对象 shallowCloneDiv 插入到父元素节点body 末尾
  document.body.appendChild(shallowCloneDiv);

</script>
  
</body>
</html>

 注意:

   这里一定要对 克隆节点 shallowCloneDiv 的id 进行修改    避免与原节点originalDiv的id重复  

   因为 原节点originalDiv 会使用id选择器 设置css样式

   如果 克隆节点 shallowCloneDiv不修改id(shallowCloneDiv.id === 'original')

    那么就会与原节点originalDiv的id(originalDiv.id === 'original')一样    

   

     那么  克隆节点 shallowCloneDiv  就是  <div id="original" class="cloned">

       有两个选择器:

       1.id选择器#original {}(特异性最高100) : 该选择器生效 背景色仍然是 lightblue (与原节点originalDiv的id选择器#original {}一样)

       2.  类选择器.cloned{}(特异性底10) : 该选择器 不会生效 被覆盖了,

     

    所以为了让 克隆节点 shallowCloneDiv的  自定义 类选择器.cloned{}生效匹配,

    就需要修改克隆节点的 id = 'cloned-div' 这样就过滤掉了   id选择器#original {}

   

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

  <style>
    /* 原节点对象originalDiv的css样式 :  一个 200x100 的浅蓝色矩形 */
     #original { /* 特异性高 */
            width: 200px;
            height: 100px;
            background-color: lightblue;
            border: 2px solid blue;
            margin: 10px;
             
        }
    
        /* 克隆节点 shallowCloneDiv 的自定义css样式  */
     .cloned { /* 特异性低 */
            width: 100px;
            height: 50px;
            background-color: lightcoral;
            border: 2px solid blue; 
            margin: 10px;
        }

  </style>
</head>
<body>
  <!-- 原节点元素: 有id  有内容 -->
  <div id="original">
    <p>这是第一个段落。</p>
    <p>这是第二个段落。</p>
</div>
  

<script>
   // 获取原节点对象originalDiv
  const originalDiv = document.getElementById('original');
    // 浅拷贝:只复制 <div> 标签本身,不复制里面的 <p> 元素 。 
    //克隆节点对象 shallowCloneDiv 与  原有节点对象originalDiv的 结构和内容 css样式 属性 一模一样 的 <div> 元素。 但它内部是空的。
   const shallowCloneDiv = originalDiv.cloneNode(false);

  console.log(shallowCloneDiv.innerHTML); // 输出: "" (空字符串,因为子节点没有被复制)

  // ✅ 给克隆节点对象 shallowCloneDiv  设置自定义样式 与  原节点对象originalDiv 不一样
  // 方式1:给克隆节点对象shallowCloneDiv  添加 class='cloned'(推荐,便于维护)  
   shallowCloneDiv.classList.add('cloned');

  
/*  注意:
   这里一定要对 克隆节点 shallowCloneDiv 的id 进行修改    避免与原节点originalDiv的id重复   
   因为 原节点originalDiv 会使用id选择器 设置css样式 
   如果 克隆节点 shallowCloneDiv不修改id(shallowCloneDiv.id === 'original') 
    那么就会与原节点originalDiv的id(originalDiv.id === 'original')一样    
    
     那么  克隆节点 shallowCloneDiv  就是  <div id="original" class="cloned">
       有两个选择器:
       1.id选择器#original {}(特异性最高100) : 该选择器生效 背景色仍然是 lightblue (与原节点originalDiv的id选择器#original {}一样)
       2.  类选择器.cloned{}(特异性底10) : 该选择器 不会生效 被覆盖了,
      
    所以为了让 克隆节点 shallowCloneDiv的  自定义 类选择器.cloned{}生效匹配,
    就需要修改克隆节点的 id = 'cloned-div' 这样就过滤掉了   id选择器#original {} 
    
 */
    // ✅ 克隆节点 shallowCloneDiv 设置一个 ID,避免重复 
    shallowCloneDiv.id = 'cloned-div';

  // 将 克隆节点对象 shallowCloneDiv 插入到父元素节点body   末尾
  document.body.appendChild(shallowCloneDiv);



</script>
  
</body>
</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>

  <style>
    /* 原节点对象originalDiv的css样式 :  一个 200x100 的浅蓝色矩形 */
     #original {
            width: 200px;
            height: 100px;
            background-color: lightblue;
            border: 2px solid blue;
            margin: 10px;
             
        }
    
  </style>
</head>
<body>
  <!-- 原节点元素: 有id  有内容 -->
  <div id="original">
    <p>这是第一个段落。</p>
    <p>这是第二个段落。</p>
</div>

<script>
   // 获取原节点对象originalDiv
  const originalDiv = document.getElementById('original');
 // 浅拷贝:只复制 <div> 标签本身,不复制里面的 <p> 元素 。 
    //克隆节点对象 shallowCloneDiv 与  原有节点对象originalDiv的 结构和内容 css样式 属性 一模一样 的 <div> 元素。 但它内部是空的。
   const shallowCloneDiv = originalDiv.cloneNode(false);

  console.log(shallowCloneDiv.innerHTML); // 输出: "" (空字符串,因为子节点没有被复制)

 // ✅ 给克隆节点对象 shallowCloneDiv  设置自定义样式 与  原节点对象originalDiv 不一样
    // 方式2:给克隆节点对象 shallowCloneDiv 直接设置内联样式 内联样式优先级最高(1000),会覆盖所有 CSS 规则。
     shallowCloneDiv.style.width = '100px';
     shallowCloneDiv.style.height = '50px';
     shallowCloneDiv.style.backgroundColor = 'lightcoral';
     shallowCloneDiv.style.border = '2px solid red';
     shallowCloneDiv.style.margin='10px';
  
 
    // ✅ 注意: 这里一定要对 克隆节点 shallowCloneDiv 的id 进行修改    避免与原节点originalDiv的id重复  
    // 否则在使用到id属性时 会与原节点originalDiv的id冲突
    shallowCloneDiv.id = 'cloned-div';

  // 将 克隆节点对象 shallowCloneDiv 插入到父元素节点body   末尾
  document.body.appendChild(shallowCloneDiv);

</script>
  
</body>
</html>

深拷贝 (cloneNode(true))

复制节点本身以及它的所有后代节点(子节点、孙子节点等)。

在 DOM 结构和属性层面,深拷贝的结果是原节点“结构一致、属性一致、样式表现一致”的副本。

克隆的目的是“快速生成一个相似的模板”,然后进行“差异化修改”

深拷贝一个包含大量子节点的复杂元素可能会消耗较多性能。在性能敏感的场景下需注意。

  深拷贝使用场景

  • 复制整个组件(包括内容)作为模板:如复制一个完整的新闻卡片、表单项、弹窗结构
  • 动态添加表格行、列表项:预先隐藏一个 <tr> 或 <li> 模板,克隆后填入数据
  • 创建可复用的 UI 模块:如模态框、轮播图项等

深拷贝实际项目中 用得更多

  • 大多数场景需要复制“完整结构”,比如从模板生成新元素。
  • 浅拷贝通常用于“轻量级容器复制”,但不如深拷贝通用。
  • 现代框架(React/Vue)减少了直接 DOM 操作,但在原生 JS 项目或性能敏感场景,cloneNode(true) 是高效手段。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>

    /* 原节点对象originalDiv的css样式 :  一个 200x100 的浅蓝色矩形 */
     #original {
            width: 200px;
            height: 100px;
            background-color: lightblue;
            border: 2px solid blue;
            margin: 10px;
             
        }

         /* 
        克隆节点 deepCloneDiv 的css样式  
        与原节点 originalDiv 结构和内容 css样式 属性 完全相同  
        #original {
            width: 200px;
            height: 100px;
            background-color: lightblue;
            border: 2px solid blue;
            margin: 10px;
             
        }
        */

  </style>
</head>
<body>

  <div id="original">
    <p>这是第一个段落。</p>
    <p>这是第二个段落。</p>
</div>

<script>
   // 获取原节点对象
  const originalDiv = document.getElementById('original');
    // 深拷贝:复制整个 <div> 及其所有内容
    //克隆节点对象deepCloneDiv 是一个与原节点 originalDiv 结构和内容 css样式 属性 一模一样  的 <div> 元素。
   const deepCloneDiv = originalDiv.cloneNode(true);
   console.log(deepCloneDiv.innerHTML); //  输出: "<p>这是第一个段落。</p><p>这是第二个段落。</p>"

  // 将 克隆节点对象 shallowCloneDiv 插入到父元素节点body 末尾
  document.body.appendChild(deepCloneDiv);

</script>
  
</body>
</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>
  <style>

    /* 原节点对象originalDiv的css样式 :  一个 200x100 的浅蓝色矩形 */
     #original {
            width: 200px;
            height: 100px;
            background-color: lightblue;
            border: 2px solid blue;
            margin: 10px;
             
        }
 
  </style>
</head>
<body>

  <div id="original" class="original-class">
    <p>这是第一个段落。</p>
    <p>这是第二个段落。</p>
</div>

<script>
   // 获取原节点对象
  const originalDiv = document.getElementById('original');
    // 深拷贝:复制整个 <div> 及其所有内容
    //克隆节点对象deepCloneDiv 是一个与原节点 originalDiv 结构和内容 css样式 属性 一模一样  的 <div> 元素。
   const deepCloneDiv = originalDiv.cloneNode(true);
   console.log(deepCloneDiv.innerHTML); //  输出: "<p>这是第一个段落。</p><p>这是第二个段落。</p>"

  // 1. 修改 ID(必须!避免重复)
    deepCloneDiv.id = 'cloned-' + originalDiv.id;

 // 2. 修改 class(可选)
  console.log(`deepCloneDiv类名:${deepCloneDiv.className}`); //deepCloneDiv类名:original-class
   deepCloneDiv.classList.remove('original-class');
   deepCloneDiv.classList.add('cloned-class');
   console.log(`deepCloneDiv类名:${deepCloneDiv.className}`); //deepCloneDiv类名:cloned-class

   // 3. 修改内联样式
   deepCloneDiv.style.width = '300px';
     deepCloneDiv.style.height = '150px';
     deepCloneDiv.style.backgroundColor = 'lightcoral';
     deepCloneDiv.style.color = 'purple';
     deepCloneDiv.style.border = '4px solid blue';
     deepCloneDiv.style.margin='10px';

 // 4. 修改内容
const firstP = deepCloneDiv.querySelector('p');
if (firstP) {
    firstP.textContent = '这是克隆后的段落';
}

// 5. 添加新子元素
const newSpan = document.createElement('span');
newSpan.textContent = '新增内容';
deepCloneDiv.appendChild(newSpan);
 
 // 6. 给克隆节点对象deepCloneDiv  绑定新的事件监听器(原事件不会被复制)
deepCloneDiv.addEventListener('click', () => {
    alert('这是克隆节点的点击事件!');
}); 
  
   //7. 将 克隆节点对象 shallowCloneDiv 插入到父元素节点body 末尾
  document.body.appendChild(deepCloneDiv);
 


</script>
  
</body>
</html>

浅拷贝 vs 深拷贝:到底复制了什么?

  • 样式(如宽高、颜色)不是直接“复制”的数据,而是因为克隆节点保留了 class 和 style 属性,浏览器根据 CSS 规则重新计算出样式。
  • 所以只要 class 被复制,且 CSS 存在,视觉上看起来样式完全一样
特性浅拷贝 cloneNode(false)深拷贝 cloneNode(true)
标签名✅ 复制✅ 复制
属性(id, class, data- 等)✅ 复制✅ 复制
内联样式(style="..."✅ 复制✅ 复制
CSS 类(class)✅ 复制(但样式来自外部 CSS)✅ 复制
宽/高/背景色/字体大小等样式✅ 只要这些样式通过 class 或 style 属性定义,就会“继承”或“表现出来”✅ 完全一样
子节点(HTML 内容)❌ 不复制✅ 完全复制(递归复制所有后代)
事件监听器(addEventListener 绑定的)❌ 不复制❌ 不复制
avaScript 扩展属性/方法❌ 不复制❌ 不复制

cloneNode()注意事项

1.克隆的节点是独立的元素对象

克隆出来的新节点是一个全新的、独立的 DOM 对象。修改原始节点或克隆节点,不会影响对方

 // 深拷贝:复制整个 <div> 及其所有内容
    //deepCloneDiv 是一个与 originalDiv 结构和内容完全相同的 <div> 元素。
  const deepCloneDiv = originalDiv.cloneNode(true);
  // 只影响克隆体元素deepCloneDiv对象里面的子元素p    原节点对象originalDiv 内的段落p颜色不变
  deepCloneDiv.querySelector('p').style.color = 'red'; 
2.事件监听器不会被复制

这是一个关键点!使用 cloneNode() 复制节点时,通过 addEventListener 添加的事件监听器不会被复制到克隆节点上

originalDiv.addEventListener('click', () => alert('Clicked!'));

const deepCloneDiv= originalDiv.cloneNode(true);
// deepCloneDiv不会响应 click 事件!
3.ID 属性也会被复制

如果原始节点对象有 id 属性,克隆节点对象也会有相同的 id。这会导致页面中出现重复的 ID,而 ID 在 HTML 中应该是唯一的。

最佳实践:克隆后,立即修改克隆节点对象的 id,或者将其设为空。

const deepCloneDiv= originalDiv.cloneNode(true);
deepCloneDiv.id = ''; 
// 或者 deepCloneDiv.id = 'cloned-' + originalDiv.id;
4.可以插入到DOM中

克隆出来的节点对象最初是“游离”的(不在当前文档中)。你可以像操作其他新节点一样,使用 appendChildinsertBefore 等方法将它(克隆节点对象)插入到页面的任何合适位置。

document.body.appendChild(deepClone);

删除节点

若一个节点在页面中已不需要时,可以删除它,在 JavaScript 原生DOM操作中,要删除元素必须通过父元素删除

语法 

父元素.removeChild(要删除的子元素)

注意:

  • 删除现有的 DOM 节点,也需要关注两个因素:首先由父节点删除子节点,其次是要删除哪个子节点。
  • 如不存在父子关系则删除不成功
  • 删除节点和隐藏节点(display:none) 有区别的: 隐藏节点还是存在的,但是删除,则从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>

  <ul>
    <li>HTML</li>
    <li>CSS</li>
    <li>Web APIs</li>
  </ul>

  <script>

     // 获取 ul 父节点
      let ul = document.querySelector('ul')
      // 待删除的子节点
      let lis = document.querySelectorAll('li')

      // 正确:为每个 li 添加点击删除事件 
      for(let i=0;i<lis.length;i++){
         
       lis[i].addEventListener('click', function () {
               //this=lis[i] this 指向当前被点击的 li 元素
               // 删除子节点  父元素.removeChlid(子元素)
             ul.removeChild(this)
       })
       
        
       } 
     
  </script>
  
</body>
</html>

 

移动端常见的事件

触屏事件 touch(也称触摸事件)

  • Android 和 IOS 都有。
  • touch 对象代表一个触摸点。触摸点可能是一根手指,也可能是一根触摸笔。触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。
常见的触屏事件
触屏touch事件说明
touchstart手指触摸到一个 DOM 元素时触发
touchmove手指在一个 DOM 元素上滑动时触发
touchend手指从一个 DOM 元素上移开时触发

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    div {
      width: 300px;
      height: 300px;
      background-color: pink;
    }
  </style>
</head>

<body>
  <div></div>
  <script>
    const div = document.querySelector('div')
    // 1. 触摸
    div.addEventListener('touchstart', function () {
      console.log('开始摸我了')
    })
    // 2. 离开
    div.addEventListener('touchend', function () {
      console.log('离开了')
    })
    // 3. 移动
    div.addEventListener('touchmove', function () {
      console.log('一直摸,移动')
    })
  </script>
</body>

</html>

重绘和回流

浏览器是如何进行界面渲染的

  1. 解析(Parser)HTML,生成DOM树(DOM Tree)
  2. 同时解析(Parser) CSS,生成样式规则 (Style Rules)
  3. 根据DOM树和样式规则,生成渲染树(Render Tree)
  4. 进行布局 Layout(回流/重排):根据生成的渲染树,得到节点的几何信息(位置,大小)
  5. 进行绘制 Painting(重绘): 根据计算和获取的信息进行整个页面的绘制
  6. Display: 展示在页面上

回流(重排)

当 Render Tree 中部分或者全部元素的尺寸、结构、布局等发生改变时,浏览器就会重新渲染部分或全部文档的过程称为 回流。

重绘

由于节点(元素)的样式的改变并不影响它在文档流中的位置和文档布局时(比如:color、background-color、outline等), 称为重绘。

重绘不一定引起回流,而回流(重排)一定会引起重绘。

会导致回流(重排)的操作:

简单理解影响到布局了,就会有回流

  1. 页面的首次刷新
  2.  浏览器的窗口大小发生改变
  3. 元素的大小或位置发生改变
  4. 改变字体的大小
  5. 内容的变化(如:input框的输入,图片的大小)
  6. 激活css伪类 (如::hover)
  7. 脚本操作DOM(添加或者删除可见的DOM元素)

学生信息管理案例:

css

 * {
  margin: 0;
  padding: 0;
}

 h1{
   text-align: center; 
   margin: 20px 0;
 }

 .info{
   width: 900px;
   height: 60px;
   margin: 50px auto;
   text-align: center;
   line-height:60px;
   background-color: lightgrey;
 }
 /* 后代选择器 :类 元素 */
 .info input,.info select{
  width: 80px;
  height: 27px; 
  padding-left: 8px;
  margin-right: 25px;
   outline: none;
   border:2px solid #b8daff;
    border-radius: 5px;
    box-sizing: border-box;
   background-color: lightpink;
 }

 .info  button{
  width: 60px;
  height: 27px;
   outline: none;
   border: 0;
    border-radius: 5px;
    color: #fff;
    cursor: pointer; /* 小手图标 */
     background-color: lightblue;
 }


 table {
  margin:0 auto;
  width: 800px;
  height: auto;
  border-collapse: collapse; /* 表格单元格边框线合并 */
  color:#004085;
  background-color: antiquewhite;
}

th {
  padding: 10px;
  background: #cfe5ff;
  
  font-size: 20px;
  font-weight: 400;
  border:2px solid #b8daff;
}





 tbody tr{
   background: #fff;
 }

 /* 伪选择器:鼠标悬浮时状态 */
 tbody tr:hover{
 background: #e1ecf8;
 }

td{
   padding:10px;
  color:#666;
  text-align: center;
  font-size: 16px;
  border:2px solid #b8daff;
}

a {
  text-decoration: none;
  color:red;
}

 html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>我的学生信息管理</title>
   <link rel="stylesheet" href="css/index.css" />
</head>
<body>
  <h1>新增学员</h1>
  <!-- 表单输入学生信息 -->
  <form class="info">

    姓名:<input type="text"  class="uname" name="uname" placeholder="请输入姓名"/>
     年龄:<input type="text"  class="age" name="age" placeholder="请输入年龄"/>
     薪资:<input type="text" class="salary" name="salary" placeholder="请输入薪资" />
 性别: 
 <select name="gender" class="gender">
       <option value="男">男</option>
      <option value="女">女</option>
 </select>
   就业城市:
  <select name="city" class="city">
     <option value="北京">北京</option>
      <option value="上海">上海</option>
      <option value="广州">广州</option>
      <option value="深圳">深圳</option>
      <option value="曹县">曹县</option>
  </select> 

    <button class="add" type="submit">录入</button> 
 </form> 


<h1>就业榜</h1>
<table>
  <thead>
      <tr>
        <th>学号</th>
        <th>姓名</th>
        <th>年龄</th>
        <th>性别</th>
        <th>薪资</th>
        <th>就业城市</th>
        <th>操作</th>
      </tr>
  </thead>

  <tbody>
     <tr>
         <td>1001</td>
          <td>欧阳霸天</td>
          <td>19</td>
          <td>男</td>
          <td>15000</td>
          <td>上海</td>
          <td><a href="javascript:">删除</a></td>
     </tr>
  </tbody>

</table>


<script>
  // 获取元素
    const uname = document.querySelector('.uname')
   const age = document.querySelector('.age')
    const gender = document.querySelector('.gender')
    const salary = document.querySelector('.salary')
    const city = document.querySelector('.city')
    const tbody = document.querySelector('tbody')





     // 声明一个空的数组, 增加和删除都是对这个数组进行操作
    const arr = []




    // 获取所有具有   name属性的  元素 把它们放到一个“类数组”(NodeList)中
     /* 所以 items 会获取到 5 个元素:
     <input name="uname">
     <input name="age">
     <input name="salary"> 
     <select name="gender">  select 默认选第一个选项  男 有值 
   <select name="city">    select 默认选第一个  北京  有值 
       */

    const items = document.querySelectorAll('[name]')

    // 1. 录入模块
    // 1.1 表单提交事件  
     /*   
 1 .在 HTML 中,一个 <button> 元素如果在 <form> 里面,并且没有设置 type 属性,
  它的默认类型是 type="submit"。 即 <button type="submit" class="add">录入</button>
  这意味着:只要点击这个按钮button,就会触发表单的提交(submit)行为,从而触发 submit 事件。
  
  2.submit 事件只有在“表单被提交”时才会触发
  submit 事件不是“点击事件”,它是一个表单提交事件,只在以下情况触发:
   点击了 type="submit"(默认) 的按钮(如你的 .add 按钮) 触发“表单被提交”submit事件
   用户在输入框中按了回车键(如果表单中有可提交的输入项)
   点击表单的其他地方(比如输入框、空白区域、下拉框)不会提交表单, 不会触发 submit 事件。
  
    */
   const info = document.querySelector('.info')
 info.addEventListener('submit', function (e) {
   // 阻止默认行为  不跳转
      e.preventDefault()
     console.log(11)

     // 遍历 获取每一表单元素对象item  通过item.value  判断每一个表单元素对象是否输入内容  
       items.forEach((item, index) => {
        console.log(`name=${item.name}, value=${item.value}`)
        if (item.value === '') {
          return alert(`${item.name}输入内容不能为空`)
        }
    })
      
    // 创建新的对象
    const obj = {
        stuId: arr.length + 1,
        uname: uname.value,
        age: age.value,
        gender: gender.value,
        salary: salary.value,
        city: city.value
      }
   console.log(obj)

      // 追加给数组里面
      arr.push(obj)
    console.log(arr.length,arr)
      // 每次录入用户信息后,清空录入用户信息表单  重置 
       // this.reset()

     // 调用渲染函数
      render()
 })


// 2. 渲染函数 因为增加和删除都需要渲染
    function render() {
    // 先清空tbody 表格以前的行数据 ,把最新数组里面的数据渲染完毕 
       tbody.innerHTML = ''
   // 遍历arr数组
   for (let i = 0; i < arr.length; i++) {
    // 创建一个子节点对象tr 
        const tr = document.createElement('tr')
        //给子节点对象tr添加内容
        tr.innerHTML=`
          <td>${arr[i].stuId}</td>
          <td>${arr[i].uname}</td>
          <td>${arr[i].age}</td>
          <td>${arr[i].gender}</td>
          <td>${arr[i].salary}</td>
          <td>${arr[i].city}</td>
          <td>
            <a href="javascript:" data-id=${i}>删除</a>
          </td>
        `
        
         // 父节点tbody  追加子元素tr   父元素.appendChild(子元素)
         tbody.appendChild(tr)

      }
    }

    // 3. 删除操作
    // 3.1 事件委托 tbody
      tbody.addEventListener('click', function (e) {
        if (e.target.tagName === 'A') {
          // 得到当前元素(a标签)的自定义属性 data-id
             console.log(e.target.dataset.id)
             // 删除arr 数组里面对应的数据
            arr.splice(e.target.dataset.id, 1)
             console.log(arr)
            // 从新渲染一次
            render()
        }
      })




</script>

</body>
</html>

   

BOM(Browser Object Model,浏览器对象模型)

BOM 提供了与浏览器窗口进行交互的对象和方法。它没有官方的标准化组织,但被所有现代浏览器广泛支持。

BOM 是非标准但事实存在的 API 集合,允许 JavaScript 控制浏览器行为。

 window全局对象(根对象)

window全局根对象,其他对象大多是 window属性或子对象,构成一个层级结构

所有全局变量和函数也都是 window 的属性。

  • window 是浏览器中 JavaScript 的全局执行环境
  • 所有通过var定义在全局作用域中的变量、函数、内置对象(如 ArrayObject)都属于 window对象的属性和方法
  • window对象下的属性和方法调用的时候可以省略window
  • 在浏览器中,window对象是一个全局对象,也可以说是JavaScript中的顶级对象也代表浏览器窗口。
  • 像document、alert()、console.log()这些都是window的属性,基本BOM的属性和方法都是window的。

地位:window低位最高层,所有其他对象都是它的属性。

window结构层级

对象是否为 window 的属性说明
window✅ 自身是根对象全局对象,所有其他对象的容器
document✅ window.documentDOM 的入口,操作页面内容
navigator✅ window.navigator浏览器信息
location✅ window.location当前 URL 及跳转控制
history✅ window.history浏览历史
screen✅ window.screen屏幕信息
window
 ├── document       → DOM 的入口,用于操作页面内容
 ├── navigator      → window.navigator,提供浏览器信息
 ├── location       → window.location,表示当前 URL
 ├── history        → window.history,管理浏览历史
 ├── screen         → window.screen,提供屏幕信息
 ├── frames         → window.frames,表示 iframe 集合
 ├── setTimeout, setInterval, alert, confirm, prompt → window 的方法
 └── self, parent, top → 窗口引用

    window常用属性/方法

在严格模式或某些框架中,建议显式写出 window 避免歧义。

window.innerWidth/Height
window.open('popup.html', 'popup', 'width=400,height=300') //打开新窗口
window.close()
window.alert("hi")   ↔   alert("hi")    // 弹窗提示
window.confirm()
window.prompt()
window.setTimeout(fn, 1000)  ↔  setTimeout(fn, 1000) //定时任务
window.setInterval()

console.log(window); // 整个浏览器窗口对象
var a = 10;
// 10 —— 全局变量自动成为 window 的属性
console.log(window.a); 

document — window.document

  • 是 window 的一个属性:window.document
  • document 不是和 window 并列的,而是 window 的子属性
  • 是 DOM(文档对象模型) 的入口点,用于操作 HTML 页面内容。
  • 虽然属于 BOM 的范畴,但它是 DOM 的核心。
document.getElementById('app');           // 操作页面元素
document.title = "新标题";                 // 修改页面标题

navigator — window.navigator

navigator的数据类型是对象,该对象下记录了浏览器自身的相关信息
  • navigator 提供浏览器的识别信息,如浏览器名称、版本、操作系统、是否启用 JavaScript、是否移动端等。
  • 它是 window 的一个只读子属性,不可修改
console.log(navigator.userAgent);     // 获取用户代理字符串 //检测设备 判断是否为移动端
console.log(navigator.platform);      // 操作系统平台
console.log(navigator.language);      // 浏览器语言
console.log(navigator.onLine);        // 是否在线

location — window.location

location 表示当前页面的 URL 信息,并可用于跳转或刷新页面

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

locationwindowdocument 都拥有的属性:

  1. window.location(推荐)
  2. document.location(只读,等同于 window.location
location 常用属性和方法:
属性/方法说明
href 属性
location.href   : 获取完整的 URL 地址  

给location.href赋值  则是跳转到新地址

location.href = 'http://www.baidu.com'

search 属性
获取地址中携带的参数,获取符号 ?后面地址
hash 属性
获取地址中的啥希值,获取符号 # 后面地址
reload() 方法
用来刷新当前页面,传入参数 true 时表示强制刷新

console.log(location.href);           // 完整 URL
console.log(location.protocol);       // "https:"
console.log(location.host);           // "example.com:8080"
console.log(location.pathname);       // "/home"
console.log(location.search);         // "?id=123"
console.log(location.hash);           // "#section1"

// 常用操作
location.href = "https://baidu.com";  // 跳转页面
location.reload();                    // 刷新
location.replace("new.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>

  <form action="">
    <input type="text" name="username">
    <input type="password" name="pwd">
    <button>提交</button>
  </form>

  <a href="#/my">我的</a>
  <a href="#/friend">关注</a>
  <a href="#/download">下载</a>
  <button class="reload">刷新</button>

   <script>
     console.log(window.location) //Location对象
     console.log(location)  //Location对象
    // 1. location.href   : 获取完整的 URL 地址
     console.log(location.href) //http://127.0.0.1:5501/1-location.html
     
      // 1.1   给location.href赋值  则是跳转到新地址
     // location.href = 'http://www.baidu.com'

     // 2. search属性  得到 ? 后面的地址 
    console.log(location.search)  // ?search=笔记本 

     
    // 3. hash属性  得到 # 后面的地址
     console.log(location.hash)

      // 4. reload 方法  刷新页面
     const reload = document.querySelector('.reload')
    reload.addEventListener('click', function () {
      
      // location.reload() // f5 刷新页面
       location.reload(true) // 强制页面刷新  ctrl+f5
    })

   </script>
  
</body>
</html>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    span {
      color: red;
    }
  </style>
</head>

<body>
  <a href="http://www.itcast.cn">支付成功<span>5</span>秒钟之后跳转到首页</a>
  <script>
    // 1. 获取元素
    const a = document.querySelector('a')
    // 2.开启定时器
    // 3. 声明倒计时变量
    let num = 5
    let timerId = setInterval(function () {
      num--
      a.innerHTML = `支付成功<span>${num}</span>秒钟之后跳转到首页`
      // 如果num === 0 则停止定时器,并且完成跳转功能
      if (num === 0) {
        clearInterval(timerId)
        // 4. 跳转  location.href
        location.href = 'http://www.itcast.cn'
      }
    }, 1000)
  </script>
</body>

</html>

history — window.history

  • 管理用户的浏览历史记录(前进、后退、跳转)。
  • 用于实现 SPA(单页应用)中的“无刷新跳转”。
history.back();           // 后退一页
history.forward();        // 前进一页
history.go(-2);           // 后退两页

// HTML5 新增:操作历史记录而不刷新页面
history.pushState(state, title, url);   // 添加一条历史记录
history.replaceState(state, title, url); // 替换当前记录

screen — window.screen

  • 提供用户屏幕的信息,常用于响应式设计或适配。
  • 多用于判断设备尺寸或做全屏适配。
console.log(screen.width);        // 屏幕宽度,如 1920
console.log(screen.height);       // 屏幕高度,如 1080
console.log(screen.availWidth);   // 可用宽度(减去任务栏)
console.log(screen.colorDepth);   // 颜色深度,通常是 24 或 32

定时器-延时函数

JavaScript 内置的一个用来让代码延迟执行的函数,叫 setTimeout
语法:
let timerId= setTimeout(回调函数, 延迟时间)
clearTimeout(timerId) //清除延时函数
注意点:
  • setTimeout 仅仅只执行一次,所以可以理解为就是把一段代码延迟执行, 平时省略window
  • 延时器需要等待,所以后下面的代码先执行
  • 每一次调用延时器都会产生一个新的延时器
  • 返回值是一个正整数,表示定时器的编号
  <body> 
    <script>  
  // 定时器之延迟函数   
 // 1. 开启延迟函数    
     let timerId = setTimeout(function () { 
     console.log('我只执行一次') 
      document.querySelector('img').style.display = 'none'
   }, 3000)
  
  // 1.1 延迟函数返回的还是一个正整数数字,表示延迟函数的编号  
  console.log(timerId)  
 
 // 1.2 延迟函数需要等待时间,所以下面的代码优先执行  
  // 2. 关闭延迟函数   
 clearTimeout(timerId)  
 </script>
</body>

JS 执行机制

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

这是因为 Javascript 这门脚本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作
DOM 而诞生的。比如我们对某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是:
如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个
线程。于是,JS 中出现了同步异步。

同步

前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。比如做饭的同
步做法:我们要烧水煮饭,等水开了(10分钟之后),再去切菜,炒菜。

 同步任务

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

主线程:

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

异步

你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。比如做饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。

异步任务

JS 的异步是通过回调函数实现的。

一般而言,异步任务有以下三种类型:
1、普通事件,如 click、resize 等
2、资源加载,如 load、error 等
3、定时器,包括 setInterval、setTimeout 等
异步任务相关添加到任务队列中(任务队列也称为消息队列)。

同步任务与异步任务

1. 先执行执行栈中的同步任务

2. 异步任务放入任务队列中。
3. 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,
于是被读取的异步任务结束等待状态,进入执行栈,开始执行。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值