鼠标事件汇总

事件名 触发时机 备注
click 点击鼠标左键
mouseover 鼠标经过
mouseout 鼠标离开
focus 得到焦点 不会冒泡
blur 失去焦点 不会冒泡
contextmenu 点击鼠标右键
selectstart 开始选中一段文字
mousedown 按下鼠标左键
mouseup 松开鼠标
mousemove 移动鼠标 每移动1px就会触发
dblclick 双击鼠标左键

1. 注册事件

1551165252019.png


2. 事件处理

DOM2 Events 为事件处理程序的赋值和移除定义了两个方法:addEventListener()removeEventListener()
这两个方法暴露在所有 DOM 节点上,它们接收 3 个参数:事件名、事件处理函数和一个布尔值,true 表示在捕获阶段调用事件处理程序,false(默认值)表示在冒泡阶段调用事件处理程序。

2.1 事件监听 addEventListener()

  1. eventTarget.addEventListener(type,listener,useCapture)
  2. //例子,给按钮创建事件
  3. let btn = document.getElementById("myBtn");
  4. btn.addEventListener("click", function(){
  5. console.log(this.id);
  6. }, false);
  • addEventListener()的三个参数分别为:
    1. - type:事件类型,例如clickmouseover注意这里事件名不带on,要加`''`j将事件类型包裹
    2. - listener: 事件处理函数,事件发生时设置的函数
    3. - userCapture:在事件流里面,true代表在捕获阶段处理程序、false表示冒泡阶段处理程序(默认为false

attacheEvent()事件监听(IE678支持)

1551165781836.png
eventTarget.attachEvent()方法将指定的监听器注册到 eventTarget(目标对象) 上,当该对象触发指定的事件时,指定的回调函数就会被执行。
1551165843912.png

事件监听兼容性解决方案

封装一个函数,函数中判断浏览器的类型:
1551166023885.png

2.2 事件删除 removeEventListener()

1551166185410.png

  • 通过addEventListener()添加的事件处理程序只能使用 removeEventListener()并传入与添加时同样的参数来移除。(即传统注册方式removeEventListener()都能够进行删除)
  • 这意味着使用 addEventListener()添加的匿名函数无法移除,
  • 如下面的例子所示:

    1. let btn = document.getElementById("myBtn");
    2. btn.addEventListener("click", function(){
    3. console.log(this.id);
    4. }, false);
    5. // 其他代码
    6. btn.removeEventListener("click", function() { // 没有效果!!!
    7. console.log(this.id);
    8. }, false);

    这个例子通过addEventListener()添加了一个匿名函数作为事件处理程序。然后,又以看起来相同的参数调用了 removeEventListener()。但实际上,第二个参数与传给 addEventListener()的完全不是一回事。传给removeEventListener()的事件处理函数必须与传给 addEventListener()的是同一个,
    如下面的例子所示:

    1. let btn = document.getElementById("myBtn");
    2. let handler = function() {
    3. console.log(this.id);
    4. };
    5. btn.addEventListener("click", handler, false);
    6. // 其他代码
    7. btn.removeEventListener("click", handler, false); // 有效果!
  • 这个例子有效,因为调用 addEventListener()和 removeEventListener()时传入的是同一个函数。

  • 大多数情况下,事件处理程序会被添加到事件流的冒泡阶段,主要原因是跨浏览器兼容性好。
  • 把事件处理程序注册到捕获阶段通常用于在事件到达其指定目标之前拦截事件。
  • 如果不需要拦截,则不要使用事件捕获

3. 事件流

DOM2 Events 规范规定事件流分为 3 个阶段:事件捕获、到达目标和事件冒泡。事件捕获最先发生,为提前拦截事件提供了可能。然后,实际的目标元素接收到事件。最后一个阶段是冒泡,最迟要在这个阶段响应事件。仍以前面那个简单的 HTML 为例,点击

元素会以如图 所示的顺序触发事件。
事件流.png
1551166581552.png

  • 在 DOM 事件流中,实际的目标(
    元素)在捕获阶段不会接收到事件。
  • 这是因为捕获阶段从document 到再到就结束了。下一阶段,即会在
    元素上触发事件的“到达目标”阶段,通常在事件处理时被认为是冒泡阶段的一部分(稍后讨论)。
  • 然后,冒泡阶段开始,事件反向传播至文档。
  • 大多数支持 DOM 事件流的浏览器实现了一个小小的拓展。
  • 虽然 DOM2 Events 规范明确捕获阶段不命中事件目标,但现代浏览器都会在捕获阶段在事件目标上触发事件。
  • 最终结果是在事件目标上有两个机会来处理事件。

1551169042295.png
注意 所有现代浏览器都支持 DOM 事件流,只有 IE8 及更早版本不支持。


4. 事件对象

  • DOM 中发生事件时,所有相关信息都会被收集并存储在一个名为 event 的对象中。
  • 这个对象包含了一些基本信息,比如导致事件的元素、发生的事件类型,以及可能与特定事件相关的任何其他数据。
  • 例如,鼠标操作导致的事件会生成鼠标位置信息,而键盘操作导致的事件会生成与被按下的键有关的信息。
  • 所有浏览器都支持这个 event 对象,尽管支持方式不同。

注意 event 对象只在事件处理程序执行期间存在,一旦执行完毕,就会被销毁。

在 DOM 合规的浏览器中,event 对象是传给事件处理程序的唯一参数。不管以哪种方式(DOM0或 DOM2)指定事件处理程序,都会传入这个 event 对象。下面的例子展示了在两种方式下都可以使用事件对象:

  1. let btn = document.getElementById("myBtn");
  2. btn.onclick = function(event) {
  3. console.log(event.type); // "click"
  4. };
  5. btn.addEventListener("click", (event) => {
  6. console.log(event.type); // "click"
  7. }, false);

这个例子中的两个事件处理程序都会在控制台打出 event.type 属性包含的事件类型。这个属性中始终包含被触发事件的类型,如”click”(与传给 addEventListener()和 removeEventListener()方法的事件名一致)。
在通过 HTML 属性指定的事件处理程序中,同样可以使用变量 event 引用事件对象。

  1. <input type="button" value="Click Me" onclick="console.log(event.type)">

1551169931778.png


在事件处理程序内部,this 对象始终等于 currentTarget 的值,而 target 只包含事件的实际目标。如果事件处理程序直接添加在了意图的目标,则 this、currentTarget 和 target 的值是一样的。
下面的例子展示了这两个属性都等于 this 的情形:

  1. let btn = document.getElementById("myBtn");
  2. btn.onclick = function(event) {
  3. console.log(event.currentTarget === this); // true
  4. console.log(event.target === this); // true
  5. };

上面的代码检测了 currentTarget 和 target 的值是否等于 this。因为 click 事件的目标是按钮,所以这 3 个值是相等的。如果这个事件处理程序是添加到按钮的父节点(如 document.body)上,那么它们的值就不一样了。比如下面的例子在 document.body 上添加了单击处理程序:

  1. document.body.onclick = function(event) {
  2. console.log(event.currentTarget === document.body); // true
  3. console.log(this === document.body); // true
  4. console.log(event.target === document.getElementById("myBtn")); // true
  5. };
  • 这种情况下点击按钮,this 和 currentTarget 都等于 document.body,这是因为它是注册事件处理程序的元素。
  • 而 target 属性等于按钮本身,这是因为那才是 click 事件真正的目标。
  • 由于按钮本身并没有注册事件处理程序,因此 click 事件冒泡到 document.body,从而触发了在它上面注册的* 处理程序。

e.target 和 this 的区别

  • this 是事件绑定的元素(绑定这个事件处理函数的元素) 。
  • e.target 是事件触发的元素。

常情况下terget 和 this是一致的,
但有一种情况不同,那就是在事件冒泡时(父子元素有相同事件,单击子元素,父元素的事件处理函数也会被触发执行),

  • 这时候this指向的是父元素,因为它是绑定事件的元素对象,
  • 而target指向的是子元素,因为他是触发事件的那个具体元素对象。

5. 阻止默认行为

preventDefault()方法用于阻止特定事件的默认动作。比如,链接的默认行为就是在被单击时导航到 href 属性指定的 URL。如果想阻止这个导航行为,可以在 onclick 事件处理程序中取消,如下面的例子所示:

  1. let link = document.getElementById("myLink");
  2. link.onclick = function(event) {
  3. event.preventDefault();
  4. };

6. 阻止事件冒泡

stopPropagation()方法用于立即阻止事件流在 DOM 结构中传播,取消后续的事件捕获或冒泡。例如,直接添加到按钮的事件处理程序中调用 stopPropagation(),可以阻止 document.body 上注册的事件处理程序执行。

  1. let btn = document.getElementById("myBtn");
  2. btn.onclick = function(event) {
  3. console.log("Clicked");
  4. event.stopPropagation();
  5. };
  6. document.body.onclick = function(event) {
  7. console.log("Body clicked");
  8. };
  • 如果这个例子中不调用stopPropagation(),那么点击按钮就会打印两条消息。
  • 但这里由于click事件不会传播到 document.body,因此 onclick 事件处理程序永远不会执行。

1551171467194.png


7. 事件委托

当有多数相同的事件时可以将此事件委托给父元素,事件委托利用事件冒泡,可以只使用一个事件处理程序来管理一种类型的事件。例如,click 事件冒泡到 document。这意味着可以为整个页面指定一个 onclick 事件处理程序,而不用为每个可点击元素分别指定事件处理程序。

  1. <ul>
  2. <li>事件1</li>
  3. <li>事件2</li>
  4. <li>事件3</li>
  5. <li>事件4</li>
  6. <li>事件5</li>
  7. </ul>
  8. <script>
  9. var ul = document.querySelector('ul') //获取父元素
  10. ul.addEventListener('click',function(e){ //给父元素设置事件
  11. alert('事件发生')
  12. // ul.children.style.backgroundColor=''
  13. for (let i = 0; i < ul.children.length; i++) { //设置排它算法
  14. ul.children[i].style.backgroundColor=''
  15. }
  16. e.target.style.backgroundColor='#0ff' //设置事件行为
  17. })
  18. </script>

只要可行,就应该考虑只给 document 添加一个事件处理程序,通过它处理页面中所有某种类型的事件。相对于之前的技术,事件委托具有如下优点

  • document 对象随时可用,任何时候都可以给它添加事件处理程序(不用等待 DOMContentLoaded或 load 事件)。这意味着只要页面渲染出可点击的元素,就可以无延迟地起作用。
  • 节省花在设置页面事件处理程序上的时间。只指定一个事件处理程序既可以节省 DOM 引用,也可以节省时间。
  • 减少整个页面所需的内存,提升整体性能

常用鼠标事件


8. 禁止选中文字和禁止右键菜单

1551172755484.png

  1. <body>
  2. <script>
  3. // 1. contextmenu 我们可以禁用右键菜单
  4. document.addEventListener('contextmenu', function(e) {
  5. e.preventDefault();
  6. })
  7. // 2. 禁止选中文字 selectstart
  8. document.addEventListener('selectstart', function(e) {
  9. e.preventDefault();
  10. })
  11. </script>
  12. </body>

9. 鼠标事件

鼠标事件是 Web 开发中最常用的一组事件,这是因鼠标是用户的主要定位设备。DOM3 Events 定义了 9 种鼠标事件。

  • click :用户单击鼠标左键或者按键盘回车键时触发。这主要基于无障碍的考虑,让键盘和鼠标都能触发onlick事件处理程序
  • dbclick :在用户双击鼠标主键(通常是左键)时触发。这个事件不是在 DOM2 Events 中定义 的,但得到了很好的支持,DOM3 Events 将其进行了标准化。
  • mousedown : 在用户按下任意鼠标键时触发。这个事件不能通过键盘触发。
  • mouseenter : 在用户把鼠标光标从元素外部移到元素内部时触发。这个事件不冒泡,也不会在 光标经过后代元素时触发。mouseenter 事件不是在 DOM2 Events 中定义的,而是 DOM3 Events 中新增的事件。
  • mouseleave :在用户把鼠标光标从元素内部移到元素外部时触发。这个事件不冒泡,也不会在 光标经过后代元素时触发。mouseleave 事件不是在 DOM2 Events 中定义的,而是 DOM3 Events 中新增的事件。
  • mousemove:在鼠标光标在元素上移动时反复触发。这个事件不能通过键盘触发。
  • mouseout :在用户把鼠标光标从一个元素移到另一个元素上时触发。移到的元素可以是原始元 素的外部元素,也可以是原始元素的子元素。这个事件不能通过键盘触发
  • mouseover:在用户巴鼠标光标从元素外部移到元素内部时触发。这个事件不能通过键盘触发
  • mouseup:在用户释放鼠标键时触发。这个事件不能通过键盘触发

mouseenter 和mouseover的区别

  • 当鼠标移动到元素上时就会触发mouseenter 事件
  • 类似 mouseover,它们两者之间的差别是
    • mouseover 鼠标经过自身盒子会触发,经过子盒子还会触发。
    • mouseenter 只会经过自身盒子触发
  • 之所以这样,就是因为mouseenter不会冒泡
  • 跟mouseenter搭配鼠标离开 mouseleave 同样不会冒泡

事件名 触发时机 备注
click 点击鼠标左键
mouseover 鼠标经过
mouseout 鼠标离开
focus 得到焦点 不会冒泡
blur 失去焦点 不会冒泡
contextmenu 点击鼠标右键
selectstart 开始选中一段文字
mousedown 按下鼠标左键
mouseup 松开鼠标
mousemove 移动鼠标 每移动1px就会触发
dblclick 双击鼠标左键

9. 1鼠标事件对象

1551173103741.png

9.1 获取鼠标页面坐标

  1. <script>
  2. // 鼠标事件对象 MouseEvent
  3. document.addEventListener('click', function(e) {
  4. // 1. client 鼠标在可视区的x和y坐标
  5. console.log(e.clientX);
  6. console.log(e.clientY);
  7. console.log('---------------------');
  8. // 2. page 鼠标在页面文档的x和y坐标
  9. console.log(e.pageX);
  10. console.log(e.pageY);
  11. console.log('---------------------');
  12. // 3. screen 鼠标在电脑屏幕的x和y坐标
  13. console.log(e.screenX);
  14. console.log(e.screenY);
  15. })
  16. </script>

10. 键盘事件

键盘事件是通过用户操作键盘时触发。
键盘事件包含 3 个事件:

键盘事件 触发条件
keydown 用户按下键盘上某个键时触发,而且持续按住会重复触发。
keypress 用户按下键盘上某个键并产生字符时触发,而且持续按住会重复触发。Esc 键也会触发这个事件。
keyup 用户释放键盘上某个键时触发。

注意:

  • 在addEventListener中使用时不需要加on,
  • keypress不能识别功能键,但会区分大小写
  1. <script>
  2. // 常用的键盘事件
  3. //1. keyup 按键弹起的时候触发
  4. document.addEventListener('keyup', function() {
  5. console.log('我弹起了');
  6. })
  7. //3. keypress 按键按下的时候触发 不能识别功能键 比如 ctrl shift 左右箭头啊
  8. document.addEventListener('keypress', function() {
  9. console.log('我按下了press');
  10. })
  11. //2. keydown 按键按下的时候触发 能识别功能键 比如 ctrl shift 左右箭头啊
  12. document.addEventListener('keydown', function() {
  13. console.log('我按下了down');
  14. })
  15. // 4. 三个事件的执行顺序 keydown -- keypress -- keyup
  16. </script>

DOM3 Events 废弃了 keypress 事件,而推荐 textInput 事件

  • 输入事件只有一个,即 textInput
  • 这个事件是对 keypress 事件的扩展,用于在文本显示给用户之前更方便地截获文本输入。
  • textInput 会在文本被插入到文本框之前触发。

总结:

  • 当用户按下键盘上的某个字符键时,首先会触发 keydown 事件,然后触发 keypress 事件,最后触发 keyup 事件。
  • 注意,这里 keydown 和 keypress 事件会在文本框出现变化之前触发,而 keyup事件会在文本框出现变化之后触发。如果一个字符键被按住不放,keydown 和 keypress 就会重复触发,直到这个键被释放。
  • 对于非字符键,在键盘上按一下这个键,会先触发 keydown 事件,然后触发 keyup 事件。如果按住某个非字符键不放,则会重复触发 keydown 事件,直到这个键被释放,此时会触发 keyup 事件。

10.1 键盘事件对象

  • 对于 keydown 和 keyup 事件,event 对象的 keyCode 属性中会保存一个键码,对应键盘上特定的一个键。
  • 对于字母和数字键,keyCode 的值与小写字母和数字的 ASCII 编码一致。比如数字 7 键的keyCode 为 55,而字母 A 键的 keyCode 为 65,而且跟是否按了 Shift 键无关。
  • DOM 和 IE 的 event 对象都支持 keyCode 属性。
  1. //如何使用keyCode
  2. let textbox = document.getElementById("myText");
  3. textbox.addEventListener("keyup", (event) => {
  4. console.log(event.keyCode);
  5. });

这个例子在 keyup 事件触发时直接显示出 event 对象的 keyCode 属性值。下表给出了键盘上所有非字符键的键码。

10.2 textInput 事件

  • DOM3 Events 规范增加了一个名为 textInput 的事件,其在字符被输入到可编辑区域时触发。
  • 作为对 keypress 的替代,textInput 事件的行为有些不一样。
  • 一个区别是 keypress 会在任何可以获得焦点的元素上触发,而 textInput 只在可编辑区域上触发。
  • 另一个区别是 textInput 只在有新字符被插入时才会触发,而 keypress 对任何可能影响文本的键都会触发(包括退格键)。
  • 因为 textInput 事件主要关注字符,所以在 event 对象上提供了一个 data 属性,包含要插入的字符(不是字符编码)。
  • data 的值始终是要被插入的字符,因此如果在按 S 键时没有按 Shift 键,data的值就是”s”,但在按 S 键时同时按 Shift 键,data 的值则是”S”。
    1. let textbox = document.getElementById("myText");
    2. textbox.addEventListener("textInput", (event) => {
    3. console.log(event.data);
    4. });
    5. //这个例子会实时把输入文本框的文本通过日志打印出来。