当前位置: 首页 > news >正文

js:事件流

事件流

事件流是指事件完整执行过程中的流动路径

一个事件流需要经过两个阶段:捕获阶段,冒泡阶段

捕获阶段是在dom树里获取目标元素的过程,从大到小

冒泡阶段是获取以后回到开始,从小到大,像冒泡一样

实际开发中大部分只用到了冒泡阶段

事件捕获

从Dom的根元素开始去执行对应的事件(从外到里)

事件捕获需要写对应代码才能看到效果
dom.addEventListener('事件类型',function(){},true)

true是捕获,默认是false

false是冒泡阶段,如果你只使用 L0 事件监听,那么事件监听器只会在冒泡阶段触发,不会在捕获阶段触发,因为ie等浏览器不支持捕获,或者说没有冒泡和捕获这个概念

1、事件覆盖情况:

L0(传统on<事件类型>注册):对于同一个对象,如果后面注册了相同类型的事件,它会覆盖前面注册的事件。例如,如果一个元素上先后注册了两个onclick事件,那么只有后面注册的那个事件会被触发。
L2(事件监听注册addEventListener):使用这种方法注册事件时,后面注册的同类型事件不会覆盖前面注册的事件。这意味着,可以在同一个元素上注册多个相同类型的事件,并且它们都会被触发。
2、事件解绑方式:

L0:直接使用null赋值就可以实现事件的解绑,例如对象.onclick = null。
L2:解绑事件需要使用removeEventListener方法,并传入相应的事件类型、事件处理函数以及捕获或冒泡阶段(如果绑定时指定了的话)。需要注意的是,如果使用匿名函数作为事件处理函数,那么将无法解绑该事件,因为无法再次引用到相同的匿名函数。
3、执行阶段:

L0:注册的事件都是在冒泡阶段执行的。
L2:注册事件时可以通过第三个参数来指定事件是在捕获阶段还是冒泡阶段执行。如果不指定第三个参数或传入false,则事件默认在冒泡阶段执行;如果传入true,则事件在捕获阶段执行。

事件冒泡

事件冒泡:当一个元素的事件被触发时,同意的事件将会在该元素的所有祖先元素依次触发

dom.addEventListener('事件类型',function(){})

L2事件监听的第三个参数是false

阻止冒泡

冒泡会把事件传递到父元素,有时候我们需要将事件的影响限制到当前元素里,就需要阻止事件冒泡

阻止事件冒泡首先要拿到事件对象:

这个方法不光可以阻断冒泡,可以阻断其他时间流动传播,就是说也可以阻断捕获阶段的事件传播

在哪里截断就在哪里加这句方法,记住此方法要调用事件对象e

阻止默认行为

有些网页元素有默认行为,比如右键弹出菜单栏这种,如果想阻止默认事件,就需要加上

解绑事件

之前我们只学了监听事件,有时候监听结束后不需要监听了,就需要解绑事件

在L0旧语法里是这么解绑的

L2里使用addEventListener()添加监听,就要使用removeEventListener()来解绑

removeEventListener(事件类型,事件处理函数,[获取捕获或冒泡阶段])

因为要添加事件处理函数作为参数,匿名函数没有名字,所以不能被解绑

加了中括号的参数表示可写可不写

fn不能加小括号,函数回调不用加小括号

鼠标经过事件的区别

在只有一级,没有父子级的时候,二者没什么区别

mouseover和mouseout在父子级都存在的时候会有冒泡效果,自己的效果会冒泡到父级上

mouseenter和mouseleae则没有这个问题,推荐使用这种用法

事件委托

事件相对于快递,给很多元素绑定事件的时候,在以前我们是才能for循环的方式,给每个元素绑定事件,就相当于每个快递1对1的送给每个人;但是有了驿站,就可以把快递托给驿站,就不需要一个一个的注册事件,这叫事件委托,此时的驿站和个人是一种父子级的关系

事件委托是一种事件注册的小技巧

原理:当我们想要给很多个子元素注册一样的事件的时候,例如点击小圆点切换图片(轮播图),就需要单独给每个小圆点添加监听;但是给父元素添加监听的时候,我们触发子元素的时候,会冒泡到父元素,然后触发父元素绑定的事件。

所以事件委托的原理就是事件冒泡

这样可以提高性能,减少注册次数

写一个看看:

父元素里的子元素在监听到事件以后会执行父元素绑定的函数:

 const box = document.querySelector('div')box.addEventListener('click', function (e) {//alert('点上了')console.log(e.target);})

点击不同的span,e.target的属性值也不一样

根据事件对象的特性改变span的背景颜色:

 <script>const box = document.querySelector('div')box.addEventListener('click', function (e) {//alert('点上了')console.log(e.target);e.target.style.backgroundColor='purple'})</script>

但是这样有一个问题:父级绑定了以后所有的子级触发事件后都会执行父元素绑定的函数,有时候并不是所有的子级都需要触发,类名不同或者标签不同的元素不需要这个事件,怎么做到分离父级和  一部分子级?

事实证明人类对事件对象的开发程度不足1%:

展开这个箭头:

可以发现这些目标是有nodeName的,我们可以靠nodeName来区分,只对span添加委托:

 const box = document.querySelector('div')box.addEventListener('click', function (e) {//alert('点上了')//console.log(e);if(e.target.nodeName==='SPAN')e.target.style.backgroundColor='purple'})

使用事件委托来重做一下之前的小圆点切换图片功能:

一个需要注意的点:之前我们是用i来访问选择的li,现在没有i了,怎么知道选中的是哪个li?

使用自定义属性

定义:

<div data-id="0"></div>

获取:

   const div=document.querySelector('div')console.log(div.dataset.id)

像这样,来访问li

但是!孩子们,注意一个问题,此时你获得的data-id是字符串,不能之间当数字使,取过来会当字符串使用

js:

  //事件委托写法const ul = document.querySelector('ul')ul.addEventListener('click', function (e) {//console.log(e.target.dataset.id);// 隐式转换,转换为数字型const id =+e.target.dataset.idtoggle(id)clearInterval(timeId)i = idtimeId = setInterval(function () {next.click()}, 1000)})

html:

  <ul class="slider-indicator"><li class="active" data-id="0"></li><li data-id="1"></li><li data-id="2"></li><li data-id="3"></li></ul>

其它事件

除了之前学的鼠标事件、键盘事件、焦点事件、文本事件等,还有其他事件

页面加载事件

在打开一个网页的时候,会有许多外部资源被加载,比如图片、外联CSS、JavaScript等,我们需要等这些元素加载完在打开网页;或者在body前面写的js没办法加载dom树,所以我们需要等待(load)

事件名:load

window.addEventListener('load',function(){})

load事件的意思是等加载完毕,再去执行回调函数

但是现在没什么人这么写,因为如果要等待dom树加载完为什么要写body前面呢?直接写在body后面就不用操心这个问题了

有时候网络出了问题或者网页加载比较慢的时候,网页里会先加载出来html元素,css和js等还没有渲染出来。这种只加载出html骨架就触发的事件叫DOMContentLoaded

相比load事件,DOMContentLoaded事件触发会比load快,因为不用等样式表、图片等外部资源加载好,用户体验更好

document.addEventListener('DOMContentLoaded',function(){})

元素滚动事件

页面内有些元素是根据滚动在不同地方的时候出现的,这个关于网页滚动的事件就叫scroll(滚动事件)

首先要确定的是谁在滚动?window

   window.addEventListener('scroll', function () {console.log('页面滚动了');})

每滚动1像素就会检测到:

介绍页面的新属性

srcollLeft:左侧被卷去的距离

srcollTop:顶部被卷去的距离

出现的数字有两个特点:不带单位、可读写(简称可访问可赋值)

可以通过页面滚动的距离来设置元素的显示:

   const elevator = document.querySelector('.xianshi')window.addEventListener('scroll', function () {//console.log('页面滚动了');//console.log(document.documentElement.scrollTop)let top = document.documentElement.scrollTopif (top >= 300) {elevator.style.opacity = 1}})

说明数据可读可写

页面尺寸事件

随着页面的放大和缩小触发事件

通过clientWidth和clientHeight来获取盒子的宽度:

但是clientWidth和clientHeight是不包含padding和border的

而offsetWidth和offsetHeight则可以包含padding和border,可以获取宽高

获取尺寸的数据为数值,可以方便计算

前提是盒子为可视化盒子,如果盒子被隐藏起来了,获取的数值为0

offsetLeft和offsetTop可以获取元素距离定位父级元素(如果最近一级的父级元素不带定位,就往上找一级,直到找到带定位的父级元素,测量和该元素的距离为offsetLeft的值)的左、上距离,是只读属性做不了计算

demo

解决问题:初次没有active类的报错问题

解决办法:先获取类,分情况讨论:有active这个类/没有这个类

如果当前对象没有这个类,就为他添加此类/如果有这个类就去除

html{scroll-behavior: smooth;/* 滑动流畅 */
}

在点击事件之后,电梯字体的颜色也要改变,来告诉用户当前处于哪个模块

所以只要一滚动,就要移除当前的active,在滚动之后再加上

本来我觉得单独获取与for循环获取还是后者更方便,但是品客老师说这样不方便后期的修改,降低的代码的可用性

//电梯函数模块(function () {const elevator = document.querySelector('.xtx-elevator')window.addEventListener('scroll', function () {const n = document.documentElement.scrollTop// if (n > 300) {//   elevator.style.opacity = 1// } else {//   elevator.style.opacity = 0// }elevator.style.opacity = n >= 300 ? 1 : 0})const backTop = document.querySelector('#backTop')backTop.addEventListener('click', function () {document.documentElement.scrollTop = 0//window.scrollTo(0, 0)将页面滚动到指定坐标})})();(function () {//模块变色和点击跳转效果const list = document.querySelector('.xtx-elevator-list')list.addEventListener('click', function (e) {if (e.target.tagName === 'A' && e.target.dataset.name) {const old = document.querySelector('.xtx-elevator-list .active')// if (old) old.classList.remove('active')// e.target.classList.add('active')//console.log(document.querySelector(`.xtx_goods_${e.target.dataset.name}`).offsetTop)const top = document.querySelector(`.xtx_goods_${e.target.dataset.name}`).offsetTopdocument.documentElement.scrollTop = top}})window.addEventListener('scroll', function () {const old = document.querySelector('.xtx-elevator-list .active')if (old) old.classList.remove('active')// 获取所有 <li> 元素const news = document.querySelector('.xtx_goods_new')const popular = document.querySelector('.xtx_goods_popular')const brand = document.querySelector('.xtx_goods_brand')const topic = document.querySelector('.xtx_goods_topic')const n=document.documentElement.scrollTopif(n>=news.offsetTop&&n<popular.offsetTop){document.querySelector('[data-name=new]').classList.add('active')}else if(n>=popular.offsetTop&&n<brand.offsetTop){document.querySelector('[data-name=popular]').classList.add('active')}else if(n>=brand.offsetTop&&n<topic.offsetTop){document.querySelector('[data-name=brand]').classList.add('active')}else if(n>=topic.offsetTop){document.querySelector('[data-name=topic]').classList.add('active')}})})();

就在此时此刻,我的外卖被偷了

我永远痛恨偷外卖的4全家

http://www.lryc.cn/news/517142.html

相关文章:

  • Linux对比Windows
  • Excel 技巧03 - 如何对齐小数位数? (★)如何去掉小数点?如何不四舍五入去掉小数点?
  • Vue3国际化多语言的切换
  • 使用XAML语言仿写BiliBil登录界面
  • 机器学习和深度学习
  • Word表格批量提取数据到Excel,Word导出到Excel,我爱excel
  • SpringSecurity抛出异常但AccessDeniedHandler不生效
  • 高清绘画素材3600多张动漫线稿线描上色练习参考插画原画
  • EXCEL技巧
  • python制作翻译软件
  • ollama+FastAPI部署后端大模型调用接口
  • BERT:深度双向Transformer的预训练用于语言理解
  • 【AI-23】深度学习框架中的神经网络3
  • 网站运营数据pv、uv、ip
  • 高阶知识库搭建实战五、(向量数据库Milvus安装)
  • 【TR369】RTL8197FH-VG+RTL8812F增加TR369 command节点
  • FPGA实现UART对应的电路和单片机内部配合寄存器实现的电路到底有何区别?
  • 数据库模型全解析:从文档存储到搜索引擎
  • 【Java基础】Java异常捕捉,throws/throw、finally、try、catch关键字的含义与运用
  • Android Studio 安装配置(个人笔记)
  • 计算机网络——数据链路层-介质访问控制
  • pytest日志显示
  • 【信息系统项目管理师】第15章:项目风险管理过程详解
  • Diffusers 使用 LoRA
  • 云安全博客阅读(二)
  • SpringCloud系列教程:微服务的未来(六)docker教程快速入门、常用命令
  • Vue 快速入门:开启前端新征程
  • UVM:uvm_component methods configure
  • LLM 训练中存储哪些矩阵:权重矩阵,梯度矩阵,优化器状态
  • 大模型思维链推理的进展、前沿和未来分析