js学习笔记:事件——事件类型_gigi就是我的博客-程序员宅基地

技术标签: javascript  

js学习笔记:事件——事件类型
web浏览器中可能发生的事件有很多类型。DOM3级事件规定了以下几类事件:

  • UI事件:当用户与页面上的元素交互时触发。
  • 焦点事件:当元素获得或失去焦点时触发
  • 鼠标事件:当用户通过鼠标在页面上执行操作时触发
  • 滚轮事件:当使用鼠标滚轮时触发
  • 文本事件:当在文档中输入文本时触发
  • 键盘事件:当用户通过键盘在页面上执行操作时触发
  • 変动事件:当底层DOM结构发生变化时触发

    除了这几类事件之外,HTML5也定义了一组事件,有些浏览器还会在DOM和BOM中实现其他专有事件。

UI事件

UI事件指的是那些不一定与用户操作有关的事件。现有的UI事件如下:

  • load:当页面完全加载后在window上面触发;当所有框架都加载完毕时在框架集上面触发;当图像加载完毕时在< img>上面触发;或者当嵌入的内容加载完毕时在< object>元素上面触发。
  • unload:当页面完全卸载后在window上面触发;当所有框架卸载后在框架集上面触发;或者当嵌入的内容且在完毕后在< object>元素上触发。
  • abort:在用户停止下载过程时,如果嵌入的内容没有加载完,则在< object>元素上触发。
  • error:当发生javascript错误时在window上触发,当无法加载图像时在< img>元素上触发,当无法加载嵌入内容时在< object>内容上触发
  • select:当用户选择文本框(< input>或< textarea>)中的一或多个字符时触发。
  • resize:当窗口或框架的大小变化时在window或框架上面触发。
  • scroll:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。

多数这些事件都与window对象或表单控件相关。

load事件

不冒泡
不能取消默认事件

Javascript中最常用的一个事件就是load。当页面完全加载后(包括所有图像、Javascript文件、CSS文件等外部资源),就会触发window上面的load事件。
有两种定义onload事件处理程序的方法:

  • 通过javascript指定
  • HTML级事件处理程序,即为body元素添加一个onload属性。

图像上面也可以触发load事件,无论是在DOM元素中的图像元素还是HTML中的图像元素。因此可以在HTML中为任何图像指定onload事件处理程序:

<img src="smile.gif" onload="alert('image loaded')">

当然,同样的功能也可以用javascript完成。

在创建新的< img>元素时,可以为其指定一个事件处理程序,以便图像加载完毕后给出提示。此时,最重要的是要在指定src属性之前先指定事件。

EventUtil.addHandler(window,"load",function(){
    var image = document.createElement("img");
    EventUtil.addHandler(image,"load",function(event){
        event = EventUtil.getEvent(event);
        alert(EventUtil.getTarget(event).src);
    });
    document.body.appendChild(image);
    image.src = "smile.gif";
})

上面例子中使用的EventUtil是之前定义的跨浏览器的定义事件处理程序和获取事件对象的接口。

这个例子中,首先为window指定了onload事件处理程序。原因在于,我们想向DOM中添加一个新元素,所以必须确定页面已经加载完毕——如果在页面加载前操作document.body会导致错误。
然后,创建了一个新的图像元素,并设置了其onload事件处理程序。
最后将这个图像添加到页面中,还设置了它的src属性。这里有一点要额外注意,新图像元素不一定要从添加到文档后才开始下载,只要设置了src属性就会开始下载。

还有一些元素也以非标准的方式支持load事件:
< script>元素也会触发load事件,以便开发人员确定动态加载的javascript文件是否加载完毕。
与图像不同,只有在设置了< script>元素的src属性并将该元素添加到文档后,才会开始下载Javascript文件。换句话说,对于< script>元素而言,指定src属性和指定事件处理程序的先后顺序就不重要了。

EventUtil.addHandler(window,"load",function(){
    
    var script= document.createElement("script");
    EventUtil.addHandler(script,"load",function(event){
    
        alert("loaded");
    });
    script.src = "example.js";
    document.body.appendChild(script);
})

IE和Opera还支持< link>元素上的load事件,以便开发人员确定样式表是否加载完毕。
与< script>节点类似,在未指定href属性并将< link>元素添加到文档之前也不会开始下载样式表。

unload事件

不冒泡
不能取消默认事件

与load事件对应的是unload事件,这个事件在文档被完全卸载后触发。只要用户从一个页面切换到另一个页面,就会发生unload事件。而利用这个事件最多的情况就是清除引用,以避免内存泄露。
与load事件类似,也有两种指定onunload事件处理程序的方式:

  • 通过js添加
  • 为< body>元素添加onunload特性。

此时event.target = document。
无论使用哪种方式,都要小心编写onunload事件处理程序中的代码。既然unload事件是在一切都被卸载之后才触发,那么在页面加载后存在的那些对象,此时就不一定存在了。此时,操作DOM节点或者元素的样式就会导致错误。

unload事件会在beforeunload事件之后被触发,而beforeunload事件是可以取消默认行为的。

resize事件

不冒泡
不能取消默认事件

当浏览器窗口被调整到一个新的高度或宽度时,就会触发resize事件。这个事件在window上面触发,因此可以通过Javascript或者< body>元素中的onresize特性来指定事件处理程序。

EventUtil.addHandler(window,"resize",function(event){
    
    alert("resize");
});

此时event.target = document。

关于何时会触发resize事件,不同的浏览器有不同的机制:

  • Firefox:只会在用户停止调整窗口大小时才会触发resize事件。
  • 其他浏览器:只要浏览器窗口变化了1像素时就触发,然后随着变化不断重复触发。

由于存在这个差别,应该注意不要在这个事件的处理程序中加入大计算量的代码,因为这些代码有可能被频繁执行,从而导致浏览器反应明显变慢。

浏览器窗口最小化或最大化时也会触发resize事件。

针对resize事件需要做防抖动或者节流处理:

  • 使用setTimeout实现节流
var throttle = function(func,delay){
    
    var timer = null;
    return function(){
    
        if(!timer){
            timer = setTimeout(function(){
    
                func();
                timer = null;
            },delay);
        }
    }
}

window.addEventListener("resize",throttle(handler,1000));
function handler(){
    
    console.log(Date.now());
}

这样可以保证至少每一秒才触发一次。

也可以使用requestAnimationFrame来实现,使其在每一个渲染帧刷新一次。

var throttle = function(func,delay){
    
    var running = false;
    return function(){
    
        if(!running){
            running = true;
            requestAnimationFrame(function(){
    
                func();
                running= false;
            });
        }
    }
}

scroll事件

element的scroll事件不冒泡,但是document的defaultView的scroll事件冒泡
不能取消默认行为

虽然scroll事件是在window对象上发生的,但它实际表示的则是页面中相应元素的变化。

  • 在混杂模式下,可以通过< body>元素的scrollLeft和scrollTop来监控这一变化。
  • 在标准模式下,除了Safari之外的所有浏览器都会通过< html>元素来反映这一变化。

与resize事件类似,scroll事件也会在文档被滚动期间重复被触发,所以有必要尽量保持事件处理程序的代码简单。

移动端兼容性:在IOS系统中,视图滚动过程中scroll事件不会被触发,在滚动结束后scroll才会触发,因此移动端很少使用scroll事件。

焦点事件

都不能取消默认事件

焦点事件会在页面元素获得或失去焦点时触发。利用这些事件并与document.hasFocus()方法及document.activeElement属性配合,可以知晓用户在页面上的行踪。
有以下6个焦点事件:

  • blur:在元素失去焦点时触发,这个事件不会冒泡
  • focus:在元素获得焦点时触发,这个事件不会冒泡
  • focusin:在元素获得焦点时触发,与HTML事件focus等价,但它冒泡
  • focusout:在元素失去焦点时触发,这个事件是HTML事件blur的通用版本,冒泡

这一类事件中最主要的两个是focus和blur,他们都是js早起就得到所有浏览器支持的事件,但它们不冒泡,因此才会有focusin和focusout被纳入标准。

当焦点从页面的一个元素移动到另一个元素,会依次触发下列事件:

  • focusout:在失去焦点的元素上触发
  • focusin:在获得焦点的元素上触发
  • blur:在失去焦点的元素上触发
  • focus:在获得焦点的元素上触发

其中,focusout和blur的事件目标是失去焦点的元素;focus和focusin的事件目标是获得焦点的元素。

鼠标与滚轮事件

鼠标事件是web开发中最常用的一类事件。DOM3级事件中定义了9个鼠标事件:

  • click:在用户单击主鼠标按钮或按下回车键时触发。这意味着onclick事件处理程序即可以通过键盘也可以通过鼠标执行。
  • dbclick:在用户双击主鼠标按钮时触发。
  • mousedown:用户按下了任意鼠标按钮时触发,不能通过键盘触发这个事件。
  • mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发这个事件不冒泡,而且在光标移动到后代元素上不会触发。
  • mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。
  • mousemove:当鼠标指针在元素内部移动时重复地触发。不能通过键盘触发这个事件。
  • mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。不能通过键盘触发这个事件。
  • mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发,也可能是这个元素的子元素。不能通过键盘触发这个事件。
  • mouseup:在用户释放鼠标按钮时触发。不能通过键盘触发这个事件。

页面上所有元素都支持鼠标事件。除了mouseenter和mouseleave,所有鼠标事件都会冒泡,也可以被取消。

只有在同一个元素上相继触发mousedown和mouseup事件,才会触发click事件;
如果mousedown或mouseup中的一个被取消,就不会触发click事件。

类似的,只有触发两次click事件,才会触发一次dbclick事件。
如果有代码阻止了连续两次触发click事件,那么就不会触发dbclick事件了。

这4个事件的触发顺序始终如下:

  • mousedown
  • mouseup
  • click
  • mousedown
  • mouseup
  • click
  • dbclick

显然,click和dbclick都会依赖与其他先行事件的触发;而mousedown和mouseup则不受其他事件影响。

另外注意mouseleave和mouseout的区别,之前写过一篇:mouseleave 与 mouseout 的不同

鼠标事件中还有一类滚轮事件mousewheel,这个事件跟踪鼠标滚轮。

客户区坐标位置

鼠标事件都是在浏览器视口中的特定位置上发生的。这个位置信息保存在事件对象的clientX和clientY属性中。
所有浏览器都支持这两个属性,它们的值表示事件发生时鼠标指针在视口中的水平和垂直坐标。
这里写图片描述

页面坐标位置

通过客户区坐标能知道鼠标是在视口中什么位置发生的,而页面坐标通过事件对象的pageX和pageY属性,能告诉你事件是在页面中的什么位置发生的。
换句话说,pageX和pageY表示鼠标光标在页面中的位置,因此坐标是从页面本身而非视口的左边和顶边计算的。

在页面没有滚动的情况下,pageX和pageY的值与clientX和clientY的值相等。

屏幕坐标位置

鼠标事件发生时,不仅会有相对于浏览器窗口的位置,还有一个相对于整个电脑屏幕的位置。
通过screenX和screenY属性就可以确定鼠标事件发生时鼠标指针相对于整个屏幕的坐标信息。

修改键

虽然鼠标事件主要是使用鼠标来触发的,但在按下鼠标时键盘上的某些键的状态也可以影响到所要采取的操作。
这些修改键就是shift、ctrl、alt、meta。他们经常被用来修改鼠标事件的行为。
DOM为此规定了事件对象的4个属性,表示这些修改键的状态:shiftKey、ctrlKey、altKey、metaKey。这些属性中包含的都是布尔值,如果相应的键被按下了,则值为true,否则值为false。当某个鼠标事件发生时,通过检测这几个属性就可以确定用户是否同时按下了其中的键。

相关元素

发生mouseover和mouseout事件时,还会涉及更多的元素。
这两个事件都会涉及把鼠标指针从一个元素的边界之内移动到另一个元素的边界之内。

  • 对mouseover事件而言,事件的主目标是获得光标的元素,而相关元素就是那个失去光标的元素。
  • 对mouseout事件而言,事件的主目标是失去光标的元素,而相关元素就是那个获得光标的元素。

来看下面这个例子:

<body>
    <div id="myDiv"></div>
</body>

如果鼠标指针一开始位于这个< div>元素上,然后移除这个元素,那么就会在div元素上触发mouseout事件,相关元素就是< body>元素。与此同时,< body>元素上面会触发mouseover事件,而相关元素变成了< div>。

DOM通过event对象的relatedTarget属性提供了相关元素的信息。这个属性只对于mouseover、mouseout、mouseenter、mouseleave事件才包含值;对于其他事件,这个值为null。
IE8及之前版本不支持relatedTarget属性,但提供了保存着同样信息的不同属性:

  • mouseover事件触发时,fromElement属性中保存着相关元素
  • mouseout事件触发时,toElement属性中保存着相关元素

鼠标按钮

只有在主鼠标按钮被单击时才会触发click事件,因此检测按钮的信息并不是必要的。
但对于mousedown和mouseup事件来说,则在其event对象存在一个button属性,表示按下或释放的按钮。
DOM的buttom属性可能有如下3个值:

  • 0表示主鼠标按钮(鼠标左键)
  • 1表示中间的鼠标按钮(滚轮)
  • 2表示次鼠标按钮(鼠标右键)

更多的事件信息

DOM2级事件规范在event对象中还提供了detail属性,用于给出有关事件的更多信息。
对于鼠标事件来说,detail中包含了一个数值,表示在给定位置上发生过多少次单击。
在同一个元素上相继地发生一次mousedown和mouseup事件算一次单击。
detail属性从1开始计数,每次单击发生后都会递增。如果鼠标在mousedown和mouseup之间移动了位置,则detail会被重置为0.

鼠标滚轮事件

当用户通过鼠标滚轮与页面交互,在垂直方向上滚动页面时,就会触发wheel事件。
这个事件可以在任何元素上面触发,最终会冒泡到document或window对象。
与wheel事件对应的event对象除了包含鼠标事件的所有标准信息外,还包含几个属性用来获取各个方向上滚动的距离:

  • deltaX
  • deltaY
  • deltaZ

将mousewheel事件处理程序指定给页面中的任何元素或document对象,即可处理鼠标滚轮的交互操作。

EventUtil.addHandler(document,"mousewheel",function(event){
    event = EventUtil.getEvent(event);
    alert(event.deltaY);
});

这个例子会在发生wheel事件时显示deltaY的值。

触摸设备

  • 不支持dbclick事件
  • 轻击可单击元素会触发mousemove事件
  • mousemove事件也会触发mouseover和mouseout事件
  • 两个手指放在屏幕上且页面随手指移动而滚动时会触发mousewheel和scroll事件

无障碍性问题

可以通过键盘上的回车键触发click事件,但其他鼠标事件却无法通过键盘来触发。为此,不建议使用click之外的鼠标事件来展示功能或引发代码执行,因为这样会给盲人或视障用户造成极大不便。

键盘与文本事件

  • keydown:当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件。
  • keypress:当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件。
    按下ESC键也会触发这个事件。
  • keyup:当用户释放键盘上的键时触发。

虽然所有元素都支持以上3个事件,但只有在用户通过文本框输入文本时才最常用到。

只有一个文本事件:textInput。这个事件是对keypress的补充,用意是在将文本显示给用户之前更容易拦截文本。在文本插入文本框之前会触发textInput事件。

用户按了一下键盘上的字符键时:

  • 首先会触发keydown事件
  • 然后是keypress事件
  • 最后会触发keyup事件。

其中,keydown和keypress都是在文本框发生变化之前被触发的;keyup则是在文本框已经发生变化之后被触发的。

如果用户按下一个字符键不放,就会重复触发keydown和keypress事件,直到用户松开该键为止。

如果用户按下的是一个非字符键,那么首先会触发keydown事件,然后就是keyup事件。如果用户按住这个非字符键不放,就会重复触发keydown事件,直到用户松开该键为止,此时会触发keyup事件。

键码

在发生keydown和keyup事件时,event对象的keyCode属性(或者key属性)中会包含一个代码,与键盘上的一个特定的键对应。
对于数字字母字符键,keyCode属性的值与ASCII码中对应小写字母或数字的编码相同。
DOM和IE的event对象都支持keyCode属性。

字符编码

发生keypress事件意味着按下的键会影响屏幕中文本的显示。在所有浏览器中,按下能够查汝或删除字符的键都会触发keypress事件。

大多数浏览器的event对象都支持一个charCode属性(或者char属性),这个属性只有在发生keypress事件时才有值,而且这个值是按下的那个键所代表字符的ASCII编码。

在取得了字符编码之后,就可以使用String.fromCharCode()将其转换成实际的字符、

textInput事件

根据规范,当用户在可编辑区域中输入字符时,就会触发这个事件。这个用于替代keypress的textInput事件的行为稍有不同。

  • 任何可以获得焦点的元素都可以触发keypress事件;但只有可编辑区域才能触发textInput事件。
  • textInput事件只会在用户按下能够输入实际字符的键时才会被触发;而keypress事件则是在按下那些能够影响文本显示的键时也会触发(比如退格键)。

由于textInput事件主要考虑的是字符,因此它的event对象中还包含了一个data属性,这个属性的值就是用户输入的字符(而非字符编码)

拖拽事件

事件流程

一个完整的drag and drop流程通常包含以下几个步骤:

  • 设置可拖拽目标:设置属性draggable=true,实现元素可拖拽
  • 监听 dragstart事件并设置拖拽数据
  • 设置允许的拖放效果,如copy、link、move
  • 设置拖放目标,默认情况下浏览器阻止所有的拖放操作,因此要监听dragenter或者dragover事件并取消其默认行为,使得元素可拖放。
  • 监听 drop事件,并执行相应操作

拖拽事件

  • dragstart:拖拽开始时在被拖拽元素上触发此事件,监听器需要设置拖拽所需数据。
    从操作系统拖拽文件到浏览器时不触发此事件.

  • dragenter:拖拽鼠标进入元素时在该元素上触发,用于给拖放元素设置视觉反馈,如高亮

  • dragover:拖拽时鼠标在目标元素上移动时触发.监听器通过阻止浏览器默认行为设置元素为可拖放元素.
  • dragleave:拖拽时鼠标移出目标元素时在目标元素上触发.此时监听器可以取消掉前面设置的视觉效果.
  • drag:拖拽期间在被拖拽元素上连续触发
  • drop:鼠标在拖放目标上释放时,在拖放目标上触发.此时监听器需要收集数据并且执行所需操作.
    如果是从操作系统拖放文件到浏览器,需要取消浏览器默认行为.
  • dragend:鼠标在拖放目标上释放时,在拖拽元素上触发.将元素从浏览器拖放到操作系统时不会触发此事件.

事件对象

拖拽事件对象为DragEvent,此接口有一个dataTransfer属性,此属性值为一个对象,该对象包含了拖拽事件的状态、拖动事件的类型,拖动的数据等。

  • dropEffect:拖拽交互类型,可能的值:

    • copy: 复制到新的位置
    • move: 移动到新的位置.
    • link: 建立一个源位置到新位置的链接.
    • none: 禁止放置(禁止任何操作).
  • effectAllowed:指定允许的交互类型,可以取值:copy,move,link,copyLink,copyMove,limkMove, all, none

  • files:包含File对象的FileList对象,从操作系统向浏览器拖放文件时有用
  • setData(format, data):设置拖拽数据。format为数据格式,如text,text/html
  • getData(format):获取数据
  • clearData
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/crystal6918/article/details/52934837

智能推荐

GC策略_weixin_33985507的博客-程序员宅基地

2019独角兽企业重金招聘Python工程师标准&gt;&gt;&gt; ...

java动态规划算法_南风知易✓✓✓的博客-程序员宅基地

java 动态规划算法递归算法的时间复杂度=递归的次数递归函数本身的时间复杂度*

典型电路的收集与记录__Jason^_^的博客-程序员宅基地

5V to 3V3通过AMS1117-3.3,将电压降至3V3,钽电容是必须的。蜂鸣器电路通过ss8050的NPN三极管实现对蜂鸣器的控制LED电路通过限流电阻对LED的电流和亮度进行控制。但每种颜色所配合的电阻还需实验,达到LED能亮同时又不晃眼睛的效果。...

Spinner的两种调用数据放式_faith_boys的博客-程序员宅基地

ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, mLmtdReptStr);adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);mSpinner.setAd

ROS arbotix踩坑:‘rospkg‘、except UnicodeDecodeError, exc、dynamic module does not define init function_#君君#的博客-程序员宅基地_ros 安装rospkg

在执行roslaunch mbot_description arbotix_mbot_with_camera_xacro.launch文件时,即包含arbotix模块的程序时,报错No module named ‘rospkg’ 。报错No module named 'rospkg'经过对大家文章的查看,发现基本集中在把环境配置为python3.或者安装rospkg。但是仍没有解决。查看opt/ros/melodic/lib/python2.7/dist-packages,发现根本没有ro.

面试题:实现数组扁平化_weixin_34056162的博客-程序员宅基地

什么是数组扁平化数组扁平化是指将一个多维数组变为一维数组reduce 方法实现reduce 本身就是一个迭代循环器,通常用于累加,所以根据这一特点有以下:const arr1 = [1,[4,6],[8,3,[19,38]]]function flatten(arr) { return arr.reduce((result, item)=&gt; { return re...

随便推点

nginx x-sendfile下载_anjile1234567890的博客-程序员宅基地

    // 利用nginx直接下载文件,提高效率    public function download_apk_efficient($internal_file_path,$file_name){        // And redirect user to internal location        header(&quot;Content-Type: application/vn...

聊聊身份认证的那些事(开篇)_罗斯839的博客-程序员宅基地_cas流程

开篇身份认证,对于一个安全的应用来说,是第一道门槛,它为后续所有的安全措施提供了“身份”这样一个关键信息。通常每个公司会有好几个外部应用,如果每一个应用使用独立的账号体系,管理起来会非常复杂,用户也需要保存好几个账号密码,严重影响使用体验。而且公司内部的各个管理或开发系统,比如各种服务器、confluence、jira、gitlab等也都使用单独的账号,一个开发人员要记住各种纷繁杂乱的账号和密码,如果不提前保存下来,根本不可能能靠记忆完成输入。这时,如何设计一个简单易用的身份认证体系就显得尤为重要

Vscode的使用小技巧_weixin_30399821的博客-程序员宅基地

命令行启动code如果你的系统是Linux系统(我使用的是Ubuntu 16.04)这样就可以直接使用 code + filename来编辑文件(就像vi + filename)如果你的系统是MacOS 就需要在vscode里面按 command + shift + p 之后输入 shell 基本上在第一提示里面就会显示安装code,如图所示转载于:https://www.cnblo...

SpringMVC -> 转发(forward)和重定向(redirect)_欧皇小德子的博客-程序员宅基地_springmvc转发的关键字是redirect

默认写法就是转发:return的字符串将对应的前端页面转发到要求的url上配置了视图解析器的viewsresolver @RequestMapping("/hello/{a}/{b}") public String hello(@PathVariable int a, @PathVariable int b, Model model) { model.addAttribute("haha", "结果:" + (a + b)); return "hello"; }没有视图解析器

ros kinetic 自带opencv3 与 opencv 2 的兼容问题_原野寻踪的博客-程序员宅基地

问题分析自己写的系统必须基于opencv 2.x,而ros kinetic自带了opencv3的版本。于是在编译时报错:/usr/bin/ld: CMakeFiles/xxxx.cpp.o: undefined reference to symbol '_ZN2cv6String10deallocateEv'/opt/ros/kinetic/lib/x86_64-linux-gnu/l...

Latex002 | 详细教程:LaTeX 编译器哪个好?——如何在 Visual Studio Code 中全流程编写 LaTeX(上篇)_爱学习的Allan的博客-程序员宅基地_latex编译器

你是否在编写 LaTeX 过程中遇到了编译器“不给力”,无法自动补全、缩进等问题?本文比较了流行的 LaTeX 的编译器,并简要分析了其优势与不足,最终给出解决方案。