赞
踩
Web页面经常和用户进行交互,需要捕捉交互的过程:
比如用户点击了某个按钮、用户在输入框里面输入了某个文本、用户鼠标经过了某个位置;
浏览器需要搭建一条JavaScript代码和事件之间的桥梁;
当某个事件发生时,让JavaScript可以相应(执行某个函数),所以需要针对事件编写处理程序(handler);
如何进行事件监听?
事件监听方式一:在script中直接监听(很少使用);
事件监听方式二:DOM属性,通过元素的on来监听事件;
事件监听方式三:通过EventTarget中的addEventListener
来监听【推荐使用】
<body> <!-- 事件监听方式一:直接在html中编写JavaScript代码(了解) --> <button onclick="console.log('按钮1发生了点击~');">按钮1</button> <button class="btn2">按钮2</button> <button class="btn3">按钮3</button> <script> //事件监听方式二 // 1.获取元素对象 var btn2El = document.querySelector(".btn2") var btn3El = document.querySelector(".btn3") // 2.onclick属性 // function handleClick01() { // console.log("按钮2发生了点击~") // } // function handleClick02() { // console.log("按钮2的第二个处理函数") // } // btn2El.onclick = handleClick01 // btn2El.onclick = handleClick02 // 3.事件监听方式三: addEventListener(推荐) btn3El.addEventListener("click", function() { console.log("第一个btn3的事件监听~") }) btn3El.addEventListener("click", function() { console.log("第二个btn3的事件监听~") }) btn3El.addEventListener("click", function() { console.log("第三个btn3的事件监听~") }) </script> </body>
鼠标事件:
click —— 当鼠标点击一个元素时(触摸屏设备会在点击时生成)。
mouseover / mouseout —— 当鼠标指针移入/离开一个元素时。
mousedown / mouseup —— 当在元素上按下/释放鼠标按钮时。
mousemove —— 当鼠标移动时。
键盘事件: keydown 和 keyup —— 当按下和松开一个按键时。
表单(form)元素事件:
submit —— 当访问者提交了一个 form
时。
focus —— 当访问者聚焦于一个元素时,例如聚焦于一个 input
。
文档加载Document 事件: DOMContentLoaded —— 当 HTML 的加载和处理均完成,DOM 被完全构建完成时。
CSS 事件: transitionend —— 当一个 CSS 动画完成时。
产生事件流?
一个问题:当在浏览器上对着一个元素点击时,点击的不仅仅是这个元素本身;
这是因为HTML元素是存在父子元素叠加层级的;
比如一个span元素是放在div元素上的,div元素是放在body元素上的,body元素是放在html元素上的;
默认情况下事件是从最内层的span向外依次传递的顺序,这个顺序称之为事件冒泡(Event Bubble)
另外一种监听事件流的方式就是从外层到内层(body -> span),这个顺序称之为事件捕获(Event Capture);
为什么会产生两种不同的处理流?
事件捕获和冒泡的过程:
(1)捕获阶段(Capturing phase): 事件(从 Window)向下走近元素。
(2)目标阶段(Target phase): 事件到达目标元素。
(3) 冒泡阶段(Bubbling phase): 事件从元素上开始冒泡。
通过event对象来获取当前的阶段: eventPhase。
开发中通常会使用事件冒泡,所以事件捕获了解即可
<style> .box { display: flex; justify-content: center; align-items: center; width: 200px; height: 200px; background-color: orange; } .box span { width: 100px; height: 100px; background-color: red; } </style> <body> <div class="box"> <span></span> </div> <script> // 1.获取元素 var spanEl = document.querySelector("span") var divEl = document.querySelector("div") var bodyEl = document.body // 2.绑定点击事件 // spanEl.onclick = function() { // console.log("span元素发生了点击~") // } // divEl.onclick = function() { // console.log("div元素发生了点击~") // } // bodyEl.onclick = function() { // console.log("body元素发生了点击~") // } // 默认情况下是事件冒泡 spanEl.addEventListener("click", function() { console.log("span元素发生了点击~冒泡") }) divEl.addEventListener("click", function() { console.log("div元素发生了点击~冒泡") }) bodyEl.addEventListener("click", function() { console.log("body元素发生了点击~冒泡") }) // 设置希望监听事件捕获的过程 spanEl.addEventListener("click", function() { console.log("span元素发生了点击~捕获") }, true) divEl.addEventListener("click", function() { console.log("div元素发生了点击~捕获") }, true) bodyEl.addEventListener("click", function() { console.log("body元素发生了点击~捕获") }, true) </script> </body>
当一个事件发生时,会有这个事件相关的很多信息:
比如事件的类型是什么,点击的是哪一个元素,点击的位置是哪里等等相关的信息;
这些信息会被封装到一个Event对象中,这个对象由浏览器创建,称之为event对象;
该对象提供了想要的一些属性,以及可以通过该对象进行某些操作;
如何获取这个event对象呢?
event对象会在传入的事件处理(event handler)函数回调时,被系统传入;
在回调函数中拿到这个event对象;
type:事件的类型;
target
:当前事件发生的元素;
currentTarget
:当前处理事件的元素;
eventPhase:事件所处的阶段;
offsetX、offsetY:事件发生在元素内的位置;
clientX、clientY:事件发生在客户端内的位置;
pageX、pageY:事件发生在客户端相对于document的位置;
screenX、screenY:事件发生相对于屏幕的位置;
<style> .box { display: flex; width: 200px; height: 200px; background-color: orange; } span { width: 100px; height: 100px; background-color: #f00; } </style> <body> <div class="box"> <span class="btn"> <button>按钮</button> </span> </div> <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br> <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br> <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br> <script> var divEl = document.querySelector("div") var btnEl = document.querySelector(".btn") // btnEl.onclick = function() { // console.log("按钮发生了点击~") // } divEl.onclick = function(event) { // 1.偶尔会使用 console.log("事件类型:", event.type) //click console.log("事件阶段:", event.eventPhase) //3 // 2.比较少使用 console.log("事件元素中位置", event.offsetX, event.offsetY) //12 7 console.log("事件客户端中位置", event.clientX, event.clientY) //21 17 console.log("事件页面中位置", event.pageX, event.pageY) // 21 7 console.log("事件在屏幕中位置", event.screenX, event.screenY) // 903 225 // 3.target/currentTarget console.log(event.target) //<button>按钮</button> console.log(event.currentTarget) // <div class="box"> <span class="btn"> <button>按钮</button> </span> </div> console.log(event.currentTarget === event.target) //false } </script> </body>
preventDefault:取消事件的默认行为;
stopPropagation:阻止事件的进一步传递(冒泡或者捕获都可以阻止);
<style> .box { display: flex; width: 200px; height: 200px; background-color: orange; } .box span { width: 100px; height: 100px; background-color: #f00; } </style> <body> <a href="http://www.baidu.com">百度一下</a> <div class="box"> <span> <button>按钮</button> </span> </div> <script> // 1.阻止默认行为 // var aEl = document.querySelector("a") // aEl.onclick = function(event) { // console.log("a元素发生了点击~") // event.preventDefault() // } // 2.阻止事件进一步传递 var btnEl = document.querySelector("button") var spanEl = document.querySelector("span") var divEl = document.querySelector("div") divEl.addEventListener("click", function(event) { console.log("div的事件捕获监听~") // event.stopPropagation() }, true) spanEl.addEventListener("click", function() { console.log("span的事件捕获监听~") }, true) btnEl.addEventListener("click", function(event) { console.log("button的事件捕获监听~") // event.stopPropagation() }, true) divEl.addEventListener("click", function() { console.log("div的事件冒泡监听~") }) spanEl.addEventListener("click", function(event) { console.log("span的事件冒泡监听~") event.stopPropagation() }) btnEl.addEventListener("click", function() { console.log("button的事件冒泡监听~") }) </script> </body>
函数中,通过this来获取当前的发生元素,在浏览器内部,调用event handler是绑定到当前的target上的
<body> <div> <button>按钮</button> </div> <script> var btnEl = document.querySelector("button") var divEl = document.querySelector("div") divEl.onclick = function(event) { console.log(this) //<div> <button>按钮</button> </div> console.log(event.currentTarget) //<div> <button>按钮</button> </div> console.log(divEl) //<div> <button>按钮</button> </div> console.log(this === divEl) //true } // divEl.addEventListener("click", function() { // console.log(this) // }) </script> </body>
所有的节点、元素都继承自EventTarget,事实上Window也继承自EventTarget。
EventTarget是一个DOM接口,主要用于添加、删除、派发Event事件;
EventTarget常见的方法:
addEventListener:注册某个事件类型以及事件处理函数;
removeEventListener:移除某个事件类型以及事件处理函数;
dispatchEvent:派发某个事件类型到EventTarget上;
<body> <button>按钮</button> <script> var btnEl = document.querySelector("button") // 1.将监听函数移除的过程 // var foo = function() { // console.log("监听到按钮的点击") // } // btnEl.addEventListener("click", foo) // // 需求: 过5s钟后, 将这个事件监听移除掉 // setTimeout(function() { // btnEl.removeEventListener("click", foo) // }, 5000) // 下面这种做法是无法移除的 btnEl.addEventListener("click", function() { console.log("btn监听的处理函数~") }) //因为 function不明确 setTimeout(function() { btnEl.removeEventListener("click", function() {}) }, 5000) // eventtarget就可以实现类似于事件总线的效果,但是不推荐使用 //事件实现效果 window.addEventListener("code", function() { console.log("监听到code的呼唤~") }) //5秒钟后派发一个事件 setTimeout(function() { window.dispatchEvent(new Event("code")) }, 5000) </script> </body>
事件冒泡在某种情况下可以帮助实现强大的事件处理模式 –事件委托模式(也是一种设计模式)
事件委托模式?
当子元素被点击时,父元素可以通过冒泡可以监听到子元素的点击;
并且通过event.target获取到当前监听的元素;
案例:一个ul中存放多个li,点击某一个li会变成红色
方案一:监听每一个li的点击,并且做出相应;
方案二:在ul中监听点击,并且通过event.target拿到对应的li进行处理; 因为这种方案并不需要遍历后给每一个li上添加事件监听,所以它更加高效;
<style> .active { color: red; font-size: 20px; background-color: orange; } </style> <body> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> <li>10</li> </ul> <script> // 1.每一个li都监听自己的点击, 并且有自己的处理函数【性能一般】 // var liEls = document.querySelectorAll("li") // for (var liEl of liEls) { // // 监听点击 // liEl.onclick = function(event) { // event.currentTarget.classList.add("active") // } // } // 2.统一在ul中监听【性能较好】 // var ulEl = document.querySelector("ul") // ulEl.onclick = function(event) { // console.log("点击了某一个li", event.target) // event.target.classList.add("active") // } // 3.新需求: 点击li变成active, 其他的取消active var ulEl = document.querySelector("ul") var activeLiEl = null ulEl.onclick = function(event) { // 1.第一种思想:for循环遍历所有元素,将之前的active移除掉 // for (var i = 0; i < ulEl.children.length; i++) { // var liEl = ulEl.children[i] // if (liEl.classList.contains("active")) { // liEl.classList.remove("active") // } // } // 1. 第二种思想:用querySector遍历找到active的li, 移除掉active // var activeLiEl = ulEl.querySelector(".active") // if (activeLiEl) { // activeLiEl.classList.remove("active") // } // 1.第三种思想:拿出一个变量记录cative的li,不用遍历 if (activeLiEl) { activeLiEl.classList.remove("active") } // 2.给点击的元素添加active event.target.classList.add("active") // 3.记录最新的active对应的li activeLiEl = event.target } </script> </body>
事件委托的标记:某些事件委托可能需要对具体的子组件进行区分,这个时候我们可以使用data-
*对其进行标记,比如多个按钮的点击,区分点击了哪一个按钮
<body> <div class="box"> <button data-action="search">搜索~</button> <button data-action="new">新建~</button> <button data-action="remove">移除~</button> <button>1111</button> </div> <script> var boxEl = document.querySelector(".box") boxEl.onclick = function(event) { var btnEl = event.target var action = btnEl.dataset.action switch (action) { case "remove": console.log("点击了移除按钮") break case "new": console.log("点击了新建按钮") break case "search": console.log("点击了搜索按钮") break default: console.log("点击了其他") } } </script> </body>
(不仅仅是鼠标设备,也包括模拟鼠标的设备,比如手机、平板电脑)
mouseover和mouseenter的区别
mouseenter
和mouseleave
不支持冒泡
进入子元素依然属于在该元素内,没有任何反应
mouseover
和mouseout
支持冒泡【应用广泛】
进入元素的子元素时
先调用父元素的mouseout
再调用子元素的mouseover
因为支持冒泡,所以会将mouseover传递到父元素中;
<style> .box { display: flex; justify-content: center; align-items: center; width: 200px; height: 200px; background-color: orange; } .box span { width: 100px; height: 100px; background-color: red; } </style> <body> <div class="box"> <span></span> </div> <script> var boxEl = document.querySelector(".box") var spanEl = document.querySelector("span") // 1.第一组 鼠标进入子元素,不会冒泡,即不会触发子元素事件 boxEl.onmouseenter = function() { console.log("box onmouseenter") } boxEl.onmouseleave = function() { console.log("box onmouseleave") } spanEl.onmouseenter = function() { console.log("span onmouseenter") } spanEl.onmouseleave = function() { console.log("span onmouseleave") } // 第二组 鼠标进入子元素,冒泡,触发子元素事件 // boxEl.onmouseover = function() { // console.log("box onmouseover") // } // boxEl.onmouseout = function() { // console.log("box onmouseout") // } </script> </body>
<style> .box { background-color: orange; } .box button { flex: 1; height: 50px; } </style> </head> <body> <div class="box"> <button>删除</button> <button>新增</button> <button>搜索</button> </div> <script> // 方案一: 监听的本身就是button元素 // var btnEls = document.querySelectorAll("button") // for (var i = 0; i < btnEls.length; i++) { // btnEls[i].onmouseover = function(event) { // console.log(event.target.textContent) // } // } // 方案二: 事件委托 var boxEl = document.querySelector(".box") //不能用 mouseenter 因为不会单独触发子元素事件 boxEl.onmouseover = function(event) { console.log(event.target.textContent) } </script>
事件的执行顺序是 onkeydown、onkeypress、onkeyup
down事件先发生;
press发生在文本被输入;
up发生在文本输入完成;
通过key和code来区分按下的键:
code:“按键代码”(“KeyA”,“ArrowLeft” 等),特定于键盘上按键的物理位置。
key:字符(“A”,“a” 等),对于非字符(non-character)的按键,通常具有与 code 相同的值。)
<body> <input type="text"> <button>搜索</button> <script> var inputEl = document.querySelector("input") var btnEl = document.querySelector("button") // inputEl.onkeydown = function() { // console.log("onkeydown") // } // inputEl.onkeypress = function() { // console.log("onkeypress") // } // inputEl.onkeyup = function(event) { // console.log(event.key, event.code) // } // 1.搜索功能 btnEl.onclick = function() { console.log("进行搜索功能", inputEl.value) //点击按钮 } inputEl.onkeyup = function(event) { if (event.code === "Enter") { console.log("进行搜索功能", inputEl.value) //敲击回车键 } } // 2.按下键盘s键的时候, 搜索自动获取焦点 document.onkeyup = function(event) { if (event.code === "KeyS") { inputEl.focus() } } </script> </body>
大致了解,等到开发用到去查文档
<body> <form action="/abc"> <input type="text"> <textarea name="" id="" cols="30" rows="10"></textarea> <button type="reset">重置</button> <button type="submit">提交</button> </form> <script> var inputEl = document.querySelector("input") // 1.获取焦点和失去焦点 // inputEl.onfocus = function() { // console.log("input获取到了焦点") // } // inputEl.onblur = function() { // console.log("input失去到了焦点") // } // 2.内容发生改变/输入内容 // 输入的过程: input // 内容确定发生改变(离开): change // inputEl.oninput = function() { // console.log("input事件正在输入内容", inputEl.value) // } // inputEl.onchange = function() { // console.log("change事件内容发生改变", inputEl.value) // } // 3.监听重置和提交 var formEl = document.querySelector("form") formEl.onreset = function(event) { console.log("发生了重置事件") event.preventDefault() } formEl.onsubmit = function(event) { console.log("发生了提交事件") // axios库提交 跳转到新页面 event.preventDefault() } </script> </body>
DOMContentLoaded
:浏览器已完全加载 HTML,并构建了 DOM 树,但像 和样式表之类的外部资源可能尚未加载完成。
load
:浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等。
<body> <script> // 注册事件监听 window.addEventListener("DOMContentLoaded", function() { // 1.这里可以操作box, box已经加载完毕 // var boxEl = document.querySelector(".box") // boxEl.style.backgroundColor = "orange" console.log("HTML内容加载完毕") // 2.获取img对应的图片的宽度和高度 var imgEl = document.querySelector("img") console.log("图片的宽度和高度:", imgEl.offsetWidth, imgEl.offsetHeight) }) window.onload = function() { console.log("文档中所有资源都加载完毕") // var imgEl = document.querySelector("img") // console.log("图片的宽度和高度:", imgEl.offsetWidth, imgEl.offsetHeight) } window.onresize = function() { console.log("创建大小发生改变时") } </script> <div class="box"> <p>哈哈哈啊</p> </div> <a href="#">百度一下</a> <img src="../images/test.jpg" alt=""> </body>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。