JavaScript学习第十章-第三部分(dom)

发布于:2025-08-18 ⋅ 阅读:(15) ⋅ 点赞:(0)

JavaScript学习第十章-第三部分(dom)

  • 事件对象
  • 冒泡行为
  • 事件委派
  • 事件捕获
  • bom对象
  • 事件循环

1、event事件

  • 事件对象

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

    • 通过事件对象可以获取到事件的详细信息

      • 比如:鼠标的坐标、键盘的按键…
    • 浏览器在创建事件对象后,会将事件对象作为响应函数的参数传递,

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

使用示例

当鼠标滑过的时候,将鼠标当前的坐标实时的显示出来

在这里插入图片描述

代码

<style>
      #box1 {
         width: 300px;
         height: 300px;
         border: 10px green solid;
         text-align: center;
         line-height: 300px;
      }
   </style>
</head>
<body>

   <div id="box1"></div>


  <script>
      /*
         event 事件
            - 事件对象
               - 事件对象是有浏览器在事件触发时所创建的对象,
               
               - 通过事件对象可以获取到事件的详细信息
                  比如:鼠标的坐标、键盘的按键..
               - 浏览器在创建事件对象后,会将事件对象作为响应函数的参数传递,
               所以我们可以在事件的回调函数中定义一个形参来接收事件对象
      */
   const box1 = document.getElementById("box1");

   // box1.onmousemove = event => {
   //     console.log(event)
   // }

   box1.addEventListener("mousemove",event => {
      console.log(event.clientX,event.clientY);

      //将当前鼠标的位置信息展示到box1中
      box1.textContent = '横坐标:'+event.clientX + ",纵坐标:"+event.clientY;


   })

  </script>
</body>

2、事件对象行为

在DOM中存在着多种不同类型的事件对象

  • 多种事件对象有一个共同的祖先 Event
    • event.target 触发事件的对象
    • event.currentTarget 绑定事件的对象(同this)
    • event.stopPropagation() 停止事件的传导
    • event.preventDefault() 取消默认行为
  • 事件的冒泡(bubble)
    • 事件的冒泡就是指事件的向上传到
    • 当元素上的某个事件被触发后,其祖先元素上的相同事件也会同时被触发
    • 冒泡的存在大大的简化了代码的编写,但是在一些场景下我们并不希望冒泡存在
      • 不希望事件冒泡时,可以通过事件对象来取消冒泡

取消超链接的默认行为

当页面上面有一个a标签,当我们点击的时候会触发它的默认事件跳转,这个时候我们想取消有两种方式;

"chao" href="https://lilichao.com">超链接</a>
方式1

方式1,缺点是不够灵活,如果用户是通过addEventListener方式,那么我们就监听不到了

const chao = document.getElementById("chao");
 chao.onclick = function() {
   return false;
}
方式2

preventDefault() 取消默认事件

chao.addEventListener("click",function(event) {
  // 取消默认行为
  event.preventDefault();
  })

event 对象

多种事件对象有一个共同的祖先 Event

使用示例
<div id="box1"></div>
 const box1 = document.getElementById("box1");


 box1.addEventListener("click",function(event) {
     /*
     在事件的响应函数中:
     event.target 表示的是触发事件的对象
     this 绑定事件的对象
     */
     console.log("target",event.target);//<div id="box3"></div >

    console.log("this",this);// box1

    // currentTarget
    console.log("currentTarget:",event.currentTarget);// box1

    alert("我是box1")
})

在这里插入图片描述

冒泡事件

我们在div里面放了三个元素,box1 里面有 box2,box2里面有box3,那么他们的关系就是

box3 -> box2 -> box1 box1是他们的 共同祖先

演示冒泡顺序
<div id="box1">box1
      <div id="box2">box2
          <div id="box3">
             box3
          </div>
      </div>
   </div>

 // 冒泡演示,当我们点击box3的时候,我们看到  box3 -> box2 -> box1 都弹出来了
box2.addEventListener("click", function (event) {

   alert("我是box2")
})

在这里插入图片描述

阻止冒泡

  // 阻止冒泡,我们在 box3上面加了阻止冒泡事件后,当我们点击box3的时候,只有box3的提示弹出来了 
  // box2 继续回冒泡
  box3.addEventListener("click", function (event) {
  //阻止向上冒泡
  event.stopPropagation();
  alert("我是box3")
  })

3、冒泡应用

  • 当我们在讨论,冒泡是好还是坏的时候,实际上冒泡是对我们有利的;

  • 如果没有冒泡,那么我们写的一个事件,需要考虑他在页面任何元素都需要处理,

  • 类似于我们函数绑定事件一样,新加一个元素,我们就需要绑定一次的动作

小案例

写一个圆球,跟随鼠标移动

方式1

我们不采用冒泡的方式,直接绑定到指定的元素上面,看小球启动的效果

  <style>
      #box1 {
         width: 100px;
         height: 100px;
         border-radius: 50%;
         background-color: aqua;
         text-align: center;
         line-height: 100px;
         position: absolute;
      }
      #box2 {
            width: 500px;
            height: 500px;
            background-color: orange;
      }

       #box3{
            width: 200px;
            height: 200px;
            background-color: tomato;
      }

      #box4{
            width: 100px;
            height: 100px;
            background-color: skyblue;
            position: absolute;
            bottom: 0;
      }
   </style>
</head>
<body>
   <div id="box1">
      圆球
   </div>

   <div id="box2"></div>


   <div id="box3" onclick="alert('box3')">
      <div id="box4" onclick="alert('box4')"></div>
   </div>
 </body>  
 
代码实现

当我们绑定box的时候,小球的移动会有迟钝,并且只能向一个方向,这个没有冒泡

  //当我们绑定box的时候,小球的移动会有迟钝,并且只能向一个方向
      box1.addEventListener("mousemove",(event) => {
         //console.log(event.x)
         box1.style.left = event.x + "px"
         box1.style.top = event.y + "px"
      })
方式2
      //如果我们直接绑定到document上面,因为冒泡的原因,最终会作用到document上
       document.addEventListener("mousemove", (event) => {
         //console.log(event.x)
         box1.style.left = event.x + "px"
         box1.style.top = event.y + "px"
      })

在这里插入图片描述

冒泡的好处演示

如果没有冒泡的存在,当小球移动到box2上面,那么就进不去了,我们来设置box2阻止冒泡

document.getElementById(“box2”).addEventListener(“mousemove”,event => {

​ //event.stopPropagation();

})

冒泡是否跟位置有关

  • 事件的冒泡和元素的样式无关,只和结构相关

  • box4是 box3的子元素,我们让box4发生相对定位,

  • 位置看起来是跟box3无关,但是我们点击box4的时候,一样会冒泡到box3上

案例演示

在这里插入图片描述

4、事件的委派

  • 只绑定一次事件,既可以让所有的超链接,包括当前的和未来新建的超链接都具有这些事件
  • 可以将事件统一绑定给document,这样点击超链接时由于事件的冒泡,
    • 会导致document上的点击事件被触发,这样只绑定一次,所有的超链接都会具有这些事件
使用示例
<body>
   <button id="btn">增加超链接</button>
   
   <hr />
   
   <ul id="list">
      <li><a href="javascript:;">链接一</a></li>
      <li><a href="javascript:;">链接二</a></li>
      <li><a href="javascript:;">链接三</a></li>
      <li><a href="javascript:;">链接四</a></li>
   </ul>
   <script>
      /*
            我一个希望:
               只绑定一次事件,既可以让所有的超链接,包括当前的和未来新建的超链接都具有这些事件

            思路:
               可以将事件统一绑定给document,这样点击超链接时由于事件的冒泡,
                  会导致document上的点击事件被触发,这样只绑定一次,所有的超链接都会具有这些事件
      */

      const links = document.querySelector("ul a");
      const btn = document.getElementById("btn");
      const list = document.getElementById("list");


      //我们直接给绑定点击事件的时候直接绑定到最上级元素,document上 
      // 我们发现这种绑定会作用到任意的元素上面,这个时候需要精细的过滤了
      document.addEventListener("click",event => {
         alert(event.target.textContent)
      })
      


      btn.addEventListener("click",event => {
         list.insertAdjacentHTML("beforeend",
            '<li><a href="javascript:;">我是一个新的超链接</a></li>'
         )
      })


   </script>
</body>
事件的委派-过滤

委派就是将本该绑定给多个元素的事件,统一绑定给document,这样可以降低代码复杂度方便维护,但是上面我们发现,无论我们点击哪个地方,都会触发该事件,那么我们需要根据条件过滤一次;

代码
<body>
   <button id="btn">增加超链接</button>
   
   <hr />
   
   <ul id="list">
      <li><a href="javascript:;">链接一</a></li>
      <li><a href="javascript:;">链接二</a></li>
      <li><a href="javascript:;">链接三</a></li>
      <li><a href="javascript:;">链接四</a></li>
   </ul>
   <script>
      /*
            我一个希望:
               只绑定一次事件,既可以让所有的超链接,包括当前的和未来新建的超链接都具有这些事件

            思路:
               可以将事件统一绑定给document,这样点击超链接时由于事件的冒泡,
                  会导致document上的点击事件被触发,这样只绑定一次,所有的超链接都会具有这些事件
            委派就是将本该绑定给多个元素的事件,统一绑定给document,这样可以降低代码复杂度方便维护
      */

      const btn = document.getElementById("btn");
      const list = document.getElementById("list");

      // 获取list中所有的超链接
      const links = list.getElementsByTagName("a");

      console.log("links", Array.from(links))


      //我们直接给绑定点击事件的时候直接绑定到最上级元素,document上 
      // 我们发现这种绑定会作用到任意的元素上面,这个时候需要精细的过滤了
      document.addEventListener("click",event => {
         //在代码执行之前判断一下事件是由谁复发
         // 检查 event.target 是否在links 中存在
         //console.log(event.target)

         if([...links].includes(event.target)) {
            alert(event.target.textContent)
         }
         
      })

      btn.addEventListener("click",event => {
         list.insertAdjacentHTML("beforeend",
            '<li><a href="javascript:;">我是一个新的超链接</a></li>'
         )
      })


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

在这里插入图片描述

5、事件的捕获

事件的传播机制:

  • 在DOM中,事件的传播可以分为三个阶段
    • 捕获阶段 (由祖先元素向目标元素进行事件的捕获)(默认情况下,事件不会在捕获阶段触发)
    • 目标阶段 (触发事件的对象)
    • 冒泡阶段 (由目标元素向祖先元素进行事件的冒泡)
  • 事件的捕获,指事件从外向内的传导,当前元素触发事件以后,会先从当前元素最大的祖先元素开始向当前元素进行事件的捕获
  • 如果希望在捕获阶段触发事件,可以将addEventListener的第三个参数设置为true
    • 一般情况下我们不希望事件在捕获阶段触发,所有通常都不需要设置第三个参数

公共代码

<style>
      #box1 {
         width: 300px;
         height: 300px;
         background-color: greenyellow;
      }
   
      #box2 {
         width: 200px;
         height: 200px;
         background-color: orange;
      }
   
      #box3 {
         width: 100px;
         height: 100px;
         background-color: tomato;
      }
   </style>
</head>
<body>
   <div id="box1">
      <div id="box2">
         <div id="box3"></div>
      </div>
   </div>
   
    const box1 = document.getElementById("box1");
     const box2 = document.getElementById("box2");
     const box3 = document.getElementById("box3");

冒泡阶段 从内向外

 box1.addEventListener("click",event => {
       alert('box1阶段===》' + event.eventPhase);// eventPhase 表示事件触发的阶段
       // //1 捕获阶段 2 目标阶段 3 冒泡阶段
      })

      box2.addEventListener("click", event => {
         alert('box2阶段===》' + event.eventPhase);// eventPhase 表示事件触发的阶段
         // //1 捕获阶段 2 目标阶段 3 冒泡阶段
      })


      box3.addEventListener("click", event => {
         alert('box3阶段===》' + event.eventPhase);// eventPhase 表示事件触发的阶段
         // //1 捕获阶段 2 目标阶段 3 冒泡阶段
      })
      
   

捕获阶段 从外向内

// 当点击box3的时候,先触发 box1(1)  box2(1) box3(2)
       // 当点击box2的时候,先触发 box1(1)  box2(2) 
       // 当点击box1的时候,先触发 box1(2)  
         box1.addEventListener("click", event => {
            alert('box1捕获阶段===》' + event.eventPhase);// eventPhase 表示事件触发的阶段
            // //1 捕获阶段 2 目标阶段 3 冒泡阶段
         },true)

         box2.addEventListener("click", event => {
            alert('box2捕获阶段===》' + event.eventPhase);// eventPhase 表示事件触发的阶段
            // //1 捕获阶段 2 目标阶段 3 冒泡阶段
         }, true)


         box3.addEventListener("click", event => {
            alert('box3捕获阶段===》' + event.eventPhase);// eventPhase 表示事件触发的阶段
            // //1 捕获阶段 2 目标阶段 3 冒泡阶段
         }, true)

在这里插入图片描述

6、bom 对象

BOM (浏览器对象模型)是浏览器提供的一组API,通过JavaScript与浏览器窗口文档、历史记录等组件交互。核心对象是window,其他常用对象包括location(地址栏)、history(历史记录)、navigator(浏览器信息)等

BOM对象:

  • Window —— 代表浏览器窗口(全局对象)
  • Navigator —— 浏览器的对象(可以用来识别浏览器)
  • Location —— 浏览器的地址栏信息
  • History —— 浏览器的历史记录(控制浏览器前进后退)
  • Screen —— 屏幕的信息

BOM对象都是作为window对象的属性保存的,所以可以直接在JS中访问这些对象

console.log(“window”,window);

console.log(“Navigator”, Navigator);

console.log(“Location”, Location);

6.1 Navigator对象

Navigator —— 浏览器的对象(可以用来识别浏览器)

​ userAgent 返回一个用来描述浏览器信息的字符串

​ 文档地址:https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator#%E5%AE%9E%E4%BE%8B%E6%96%B9%E6%B3%95

使用示例

判断浏览器

<body>
   <script>
       /*
            - Navigator —— 浏览器的对象(可以用来识别浏览器)
                  userAgent 返回一个用来描述浏览器信息的字符串
             文档地址:https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator#%E5%AE%9E%E4%BE%8B%E6%96%B9%E6%B3%95        
         */
      // 浏览器对象字符串
      console.log(navigator)
      console.log(navigator.userAgent);



      // 判断浏览器
      let sBrowser
      const sUsrAg = navigator.userAgent

      // The order matters here, and this may report false positives for unlisted browsers.

      if (sUsrAg.indexOf("Firefox") > -1) {
         sBrowser = "Mozilla Firefox"
         // "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0"
      } else if (sUsrAg.indexOf("SamsungBrowser") > -1) {
         sBrowser = "Samsung Internet"
         // "Mozilla/5.0 (Linux; Android 9; SAMSUNG SM-G955F Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/9.4 Chrome/67.0.3396.87 Mobile Safari/537.36
      } else if (
         sUsrAg.indexOf("Opera") > -1 ||
         sUsrAg.indexOf("OPR") > -1
      ) {
         sBrowser = "Opera"
         // "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 OPR/57.0.3098.106"
      } else if (sUsrAg.indexOf("Trident") > -1) {
         sBrowser = "Microsoft Internet Explorer"
         // "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; Zoom 3.6.0; wbx 1.0.0; rv:11.0) like Gecko"
      } else if (sUsrAg.indexOf("Edge") > -1) {
         sBrowser = "Microsoft Edge (Legacy)"
         // "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299"
      } else if (sUsrAg.indexOf("Edg") > -1) {
         sBrowser = "Microsoft Edge (Chromium)"
         // Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.64
      } else if (sUsrAg.indexOf("Chrome") > -1) {
         sBrowser = "Google Chrome or Chromium"
         // "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/66.0.3359.181 Chrome/66.0.3359.181 Safari/537.36"
      } else if (sUsrAg.indexOf("Safari") > -1) {
         sBrowser = "Apple Safari"
         // "Mozilla/5.0 (iPhone; CPU iPhone OS 11_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Mobile/15E148 Safari/604.1 980x1306"
      } else {
         sBrowser = "unknown"
      }

      alert(`You are using: ${sBrowser}`)



   </script>
</body>

在这里插入图片描述

6.2 location 对象

location 表示的是浏览器地址栏的信息

  • 可以直接将location的值修改为一个新的地址,这样会使得网页发生跳转
  • location.assign() 跳转到一个新的地址
  • location.replace() 跳转到一个新的地址(无法通过回退按钮回退)
  • location.reload() 刷新页面,可以传递一个true来强制清缓存刷新
  • location.href 获取当前地址
使用示例

console.log(location.href)

location = “https://www.lilichao.com”

location.assign(“https://www.lilichao.com”)

location.replace(“https://www.lilichao.com”)

location.reload(true)

6.3 history 对象

  • history.back() 回退按钮
  • history.forward() 前进按钮
  • history.go() 可以向前跳转也可以向后跳转
使用示例
 console.log(history.length)

//history.back()

//history.forward()

// 正数代表是向前,负数是后退 x 步
//history.go(-1)

7、定时器

通过定时器,可以使代码在指定时间后执行

  • 设置定时器的方式有两种:
    • setTimeout()
      • - 参数:
        1. 回调函数(要执行的代码)
        2. 间隔的时间(毫秒)
      • 关闭定时器
        • clearTimeout()
    • setInterval() (每间隔一段时间代码就会执行一次)
      • 参数:
        1. 回调函数(要执行的代码)
        2. 间隔的时间(毫秒)
      • 关闭定时器
        • clearInterval()

setTimeout

// 间隔2s中后执行
      const timer = setTimeout(() => {
         console.log('我是定时器代码==》')
      },2000)

      // 关闭定时器
      clearTimeout(timer);

setInterval

<body>

   <h1>0</h1>
   <button>停止</button>


   <script>
      /*
            通过定时器,可以使代码在指定时间后执行
               - 设置定时器的方式有两种:
                  setTimeout()
                        - 参数:
                           1. 回调函数(要执行的代码)
                           2. 间隔的时间(毫秒)
                        - 关闭定时器
                           clearTimeout()

                  setInterval() (每间隔一段时间代码就会执行一次)
                        - 参数:
                           1. 回调函数(要执行的代码)
                           2. 间隔的时间(毫秒)
                        - 关闭定时器
                           clearInterval()

      */

      // 间隔2s中后执行
      const timer = setTimeout(() => {
         console.log('我是定时器代码==》')
      },2000)

      // 关闭定时器
      clearTimeout(timer);


      const h1html = document.querySelector("h1");

      let num = 0;

      //间隔固定的时间一直执行
      const interTime = setInterval(() => {
         num ++;

         h1html.textContent = num;

      }, 50);

      //手动停止
      const btn = document.getElementsByTagName("button")[0]
      btn.onclick = function() {
          clearInterval(interTime);
      }




   </script>
</body>

在这里插入图片描述

8、事件循环

事件循环(event loop)

  • 函数在每次执行时,都会产生一个执行环境
    时产生的一切数据
    • 问题:函数的执行环境要存储到哪里呢?
    • 函数的执行环境存储到了一个叫做调用栈的地方
    • 栈,是一种数据结构,特点 后进先出

​ 调用栈(call stack)

  • 调用栈负责存储函数的执行环境
    境会作为一个栈帧
    插入到调用栈的栈顶,函数执行完毕其栈帧会自动从栈中弹出

使用示例

     function fn() {
          let a = 10;
          let b = 20;

          function fn2() {
             console.log("fn2")
          }

          fn2();

          console.log("fn~~")

        }


        fn();


        console.log("1111")

   </script>

在这里插入图片描述

在这里插入图片描述

9、事件循环-消息队列

事件循环(event loop)

  • 函数在每次执行时,都会产生一个执行环境
    数执行时产生的一切数据
    • 问题:函数的执行环境要存储到哪里呢?
    • 函数的执行环境存储到了一个叫做调用栈的地方
    • 栈,是一种数据结构,特点 后进先出
    • 队列,是一种数据结构,特点 先进先出

​ 调用栈(call stack)

  • 调用栈负责存储函数的执行环境
    执行环境会作为一个栈帧
    插入到调用栈的栈顶,函数执行完毕其栈帧会自动从栈中弹出

​ 消息队列

  • 消息队列负责存储将要执行的函数
    响应函数并不是直接就添加到调用栈中的
    因为调用栈中有可能会存在一些还没有执行完的代码
    • 事件触发后,JS引擎是将事件响应函数插入到消息队列中排队

代码演示

  function fn() {
         let a = 10
         let b = 20

         function fn2() {
            console.log("fn2")
         }

         fn2()

         console.log("fn~")
      }

      fn()

      console.log(1111);


      // 我们思考,当我们有一个函数没有调用的时候,那么他是应该放到哪里呢
      // 当我们执行这个函数的时候,是否立即就执行呢
      // 
      const btn1 = document.getElementById("btn1");
      const btn2 = document.getElementById("btn2");


      // 执行此函数,会有一个3s的时间等待操作
      btn1.onclick = function() {
         alert("btn1事件被触发了!!!");

         const begin = Date.now();
         const num = document.getElementById("num");
         while(Date.now() - begin < 3000) {
            // let temNum = num.textContent;
            // let timer;
            // if(temNum <= 0) {
            //    clearTimeout(timer);
            //    break;
            // }
            // num.textContent = temNum - 100
            
         }
      }

      btn2.onclick = function() {
         alert("我是btn2的操作")
      }




   </script>

在这里插入图片描述

在这里插入图片描述

10、定时器-消息队列

定时器的本质,就是在指定的时间后将函数添加到消息队列中

示例1

  /**
     *   定时器的本质,就是在指定的时间后将函数添加到消息队列中
     */
    console.time()
    setTimeout(() => {
      console.timeEnd();
      console.log("定时器执行了!!!")

    }, 3000);

    const begion = Date.now();
    while(Date.now() - begion < 6000) {}

在这里插入图片描述

setInterval()

setInterval() 每间隔一段时间就将函数添加到消息队列中,但是如果,函数执行较慢,那么就无法确保每次执行的间隔是一样的

    console.time('定时间隔执行');
    setInterval(() => {
      console.timeEnd("定时间隔执行");
      // 如果我自己的逻辑执行需要1s 那么,实际上下次就是执行时间 + 间隔的时间
      //console.log("定时器执行了~~~~")
      alert("执行!!!")
      console.time("定时间隔执行")
    }, 3000);

在这里插入图片描述

创建一个确保函数每次执行都有相同的间隔

console.time("固定间隔")
    setTimeout(function fn(){
      console.timeEnd("固定间隔");
      //console.log("我开始执行了 !")
      alert("---")

      // 在settimeout的回调函数的最后,再调用一次setTimeout函数
      console.time("固定间隔")
      setTimeout(fn,3000);
    }, 3000);

在这里插入图片描述

函数执行顺序

 // 函数执行顺序
    setTimeout(() => {
      console.log('0毫秒以后开始执行~~~')
    }, 0);

    //先执行,因为他是全局作用域,先进栈,setTimeout需要先进队列,所以慢一些
    console.log(22222)

网站公告

今日签到

点亮在社区的每一天
去签到