js红书 【7 事件 动画】

katsu 发布于 2025-02-17 133 次阅读


17 事件

事件流 事件处理程序 不同类型事件

JavaScript 与 HTML 的交互是通过事件实现的,事件代表文档或浏览器窗口中某个有意义的时刻。 可以使用仅在事件发生时执行的监听器(也叫处理程􏰀)订阅事件。

换句话说页面上发生了什么事情,对应执行什么操作

事件流

页面哪个部分拥有特定的事件呢? 这个事件归谁管啊?

事件流描述了页面接收事件的顺􏰀。

IE 和 Netspace 不一样

IE 事件冒泡 从下到上

由于旧版本浏览器不支持,因此实际当中几乎不会使用事件捕获。通常建议使用事件冒泡,特殊情况下可以使用事件捕获。

Netspace 事件捕获 从上到下

事件处理程序

事件意味着用户或浏览器执行的某种动作。比如,单击(click)、加载(load)、鼠标悬停 (mouseover)。为响应事件而调用的函数被称为事件处理程序(或事件监听器)。事件处理程􏰀的名字 以"on"开头,因此 click 事件的处理程􏰀叫作 onclick,而 load 事件的处理程􏰀叫作 onload。有

html 事件处理

作为事件处理程􏰀执行的代码可以访问全局作用域中的 一切。

    <form method="post">
      <input type="text" name="username" value="">
      <input type="button" value="Echo Username"

             onclick="console.log(username.value)">

</form>

点击这个例子中的按钮会显示出文本框中包含的文本。注意,事件处理程􏰀中的代码直接引用了 username。

捕获错误
<input type="button" value="Click Me" onclick="try{showMessage();}catch(ex) {}">

用 js 事件处理

DOM2

DOM2 Events 为事件处理程􏰀的赋值和移除定义了两个方法:addEventListener()remove- EventListener()

通过 addEventListener()添加的事件处理程􏰀只能使用 removeEventListener()并传入与添 加时同样的参数来移除。这意味着使用 addEventListener()添加的匿名函数无法移除

    let btn = document.getElementById("myBtn");

    let handler = function() {
      console.log(this.id);

    };
    btn.addEventListener("click", handler, false);

// 其他代码

btn.removeEventListener("click", handler, false); // 有效果!

跨浏览器事件处理

为啥?因为 IE 9 之前他妈的不兼容 现在好像兼容了

检测是否兼容 DOM2 方式

事件对象

所有浏览器都支持这个 event 对象,尽管支持方式不同。

let btn = document.getElementById("myBtn");
btn.onclick = function(event) {

  console.log("Clicked");

  event.stopPropagation();

};

document.body.onclick = function(event) {
  console.log("Body clicked");

};

如果这个例子中不调用 stopPropagation(),那么点击按钮就会打印两条消息。但这里由于 click 事件不会传播到 document.body,因此 onclick 事件处理程􏰀永远不会执行。

事件类型

事件监听器的注册过程是同步的,但其回调函数的执行是异步的。也就是说,当你用 addEventListener 注册一个事件时,这个注册动作是立刻完成的;而当事件真正发生时,回调函数会被放入任务队列中,在当前执行栈清空后才执行,这就是异步执行的行为。

DOM3 events

  •   用户界面事件(UIEvent):涉及与 BOM 交互的通用浏览器事件。

  •   焦点事件(FocusEvent):在元素获得和失去焦点时触发。

  •   鼠标事件(MouseEvent):使用鼠标在页面上执行某些操作时触发。

  •   滚轮事件(WheelEvent):使用鼠标滚轮(或类似设备)时触发。

  •   输入事件(InputEvent):向文档中输入文本时触发。

  •   键盘事件(KeyboardEvent):使用键盘在页面上执行某些操作时触发。

  •   合成事件(CompositionEvent):在使用某种 IME(Input Method Editor,输入法编辑器)输入 27

    字符时触发。

UIEvent

常见的 UIEvent 类型通常包括:

  • load/unload:页面或资源加载完成与卸载时触发的事件
  • resize:当窗口或视图尺寸变化时触发
  • scroll:页面滚动时触发
  • select:文本或表单元素中的内容被选中时触发

img 加 load 事件要写在 src 前面

FocusEvent

在 FocusEvent 中,最常用的事件是:

  • focus:当元素获得焦点时触发。
  • blur:当元素失去焦点时触发。

此外,还有:

  • focusinfocusout:这两个事件与 focus/blur 类似,但它们是冒泡的(即事件会从目标元素向上冒泡),在某些场景下会更适用。

MouseEvent & WheelEvent

在鼠标交互中,最常用的 MouseEvent 事件包括:

  • click:单击事件,当鼠标按下并释放时触发。
  • dblclick:双击事件,当快速连续点击两次时触发。
  • mousedown:鼠标按下时触发。
  • mouseup:鼠标释放时触发。
  • mousemove:鼠标移动时持续触发。
  • mouseenter / mouseleave:当鼠标进入或离开元素时触发(不冒泡)。
  • mouseover / mouseout:当鼠标悬停于元素上或离开时触发(会冒泡)。 存在相关元素

虽然没有专门命名为“右键事件”,但当用户点击鼠标右键时,会触发一个叫做 contextmenu 的事件。你可以监听这个事件,并调用 preventDefault() 来禁用默认的右键菜单。

而对于 WheelEvent,最常用的就是:

  • wheel:当鼠标滚轮滚动或触控板进行滚动操作时触发。

    触摸屏

  •   单指点触屏幕上的可点击元素会触发 mousemove 事件。如果操作会导致内容变化,则不会再触 发其他事件。如果屏幕上没有变化,则会相继触发 mousedown、mouseup 和 click 事件。点 触不可点击的元素不会触发事件。可点击元素是指点击时有默认动作的元素(如链接)或指定

    了 onclick 事件处理程􏰀的元素。

  •   mousemove 事件也会触发 mouseover 和 mouseout 事件。

  •   双指点触屏幕并滑动导致页面滚动时会触发 mousewheel 和 scroll 事件。

专为触摸设备的触摸事件

touchstart 等

KeyboardEvent & InputEvent

KeyboardEvent 常用事件

  • keydown
    当键盘上的任意键被按下时触发。

    • 特点:按键按下时立即触发;如果持续按住,会重复触发该事件。
    • 常用场景:捕获用户按键、快捷键监听等。
  • keyup
    当键盘上的按键被释放时触发。

    • 特点:只触发一次,标识按键释放的时刻。
    • 常用场景:检测用户输入结束、校验输入状态等。
document.addEventListener('keydown', function (e) {
  // 检查是否同时按下了 Ctrl 键和 's' 键
  if (e.ctrlKey && e.key.toLowerCase() === 's') {
    e.preventDefault(); // 阻止默认保存网页的行为
    console.log("触发保存操作!");
    // 在这里添加保存数据的逻辑
  }
});

InputEvent

  • textInput
    当元素的值发生变化时触发,无论是通过用户输入、剪切、粘贴或其他操作。

    • 特点:即时响应输入变化,适用于实时验证、联想搜索等需求。
    • 常用场景:动态表单验证、实时搜索提示、同步显示用户输入等。
  • compositionstart / compositionupdate / compositionend
    这组事件用于处理输入法(IME)的文本组合过程。

    • compositionstart:开始输入复合字符时触发。
    • compositionupdate:在输入过程中持续触发,反映当前组合状态。
    • compositionend:完成复合字符输入时触发。
    • 常用场景:多语言输入、需要对输入过程进行特殊处理的场合。

内存与性能

每个函数都是对象,都占用内存空间,对象越多,性能越差。其次,

为指定事件处理程序所需访问 DOM 的次数会先期造成整个页面交互的延迟。

事件太多-事件委托

过多事件处理程􏰀”的解决方案是使用事件委托

事件委托利用事件冒泡,可以只使用一个事件 处理程􏰀来管理一种类型的事件。

这意味着可以为整个页面指定 一个 onclick 事件处理程􏰀,而不用为每个可点击元素分别指定事件处理程􏰀。

使用事件委托,只要给所有元素共同的祖先节点添加一个事件处理程􏰀, 就可以解决问题。

let list = document.getElementById("myLinks");

list.addEventListener("click", (event) => {
  let target = event.target;

  switch(target.id) {
    case "doSomething":

      document.title = "I changed the document's title";
      break;

    case "goSomewhere":
      location.href = "http:// www.wrox.com";
      break;

    case "sayHi":
      console.log("hi");
      break;

} });
只要可行,就应该考虑只给 document 添加一个事件处理程序

click、mousedown、mouseup、keydown 和 keypress。比较适合

不用的及时删除-删除事件处理程序

把事件处理程􏰀指定给元素后,在浏览器代码和负责页面交互的 JavaScript 代码之间就建立了联系。这种联系建立得越多,页面性能就越差。

及时删除不用

很多 Web 应用性能不佳都是由于无用的事件处理程序长驻内存导致的。

出现垃圾事件的情况:

  1. 删除带有事件处理程序的元素。

  2. 最常见的还是使用 innerHTML 整体替换页面的 某一部分。这时候,被 innerHTML 删除的元素上如果有事件处理程􏰀,就不会被垃圾收集程􏰀正常清 理。

  3. 另一个可能导致内存中残留引用的问题是页面卸载。

    <div id="myDiv">
      <input type="button" value="Click Me" id="myBtn">

    </div>
    <script type="text/javascript">

      let btn = document.getElementById("myBtn");
      btn.onclick = function() {

// 执行操作

document.getElementById("myDiv").innerHTML = "Processing...";

// 不好! };

</script>

烂!因为事件没删

  <div id="myDiv">
      <input type="button" value="Click Me" id="myBtn">

    </div>
    <script type="text/javascript">

      let btn = document.getElementById("myBtn");
      btn.onclick = function() {

// 执行操作

btn.onclick = null; // 删除事件处理程序

document.getElementById("myDiv").innerHTML = "Processing..."; };

</script>
一般来说,最好在 onunload 事件处理程􏰀中趁页面尚未卸载先删除所有事件处理程序

onload 事件处理程􏰀中做了什么,最好在 onunload 事件处理程􏰀中恢复。

模拟事件

可以通过 JavaScript 在任何时候触发任意事件,而这些事件会被当成浏览器创建 的事件

DOM 事件模拟

任何时候,都可以使用 document.createEvent()方法创建一个 event 对象。

鼠标事件和键盘事件是浏览器中最常见的模拟对象。

创建 event-传入必要参数-触发事件

    let btn = document.getElementById("myBtn");

// 创建 event 对象  
let event = document.createEvent("MouseEvents");

// 初始化 event 对象  
event.initMouseEvent("click", true, true, document.defaultView,

                         0, 0, 0, 0, 0, false, false, false, false, 0, null);

// 触发事件 
btn.dispatchEvent(event);

18 动画与 canvas

<canvas>是 HTML5 最受欢迎的新特性。这个元素会占据一块页面区域,让 JavaScript 可以动态在上面绘制图片

18.1 使用 requestAnimationFrame

般计算机显示器的屏 幕刷新率都是 60Hz,基本上意味着每秒需要重绘 60 次。

因此,实现平滑动画最佳的重绘间隔为 1000 毫秒/60,大约 17 毫秒。以这个速度重绘可以实现最平 滑的动画,因为这已经是浏览器的极限了。

知道何时绘制下一帧是创造平滑动画的关键。(每秒内的每次刷新时绘制)

所有浏览器都支持这个方法不带前缀的版本,即 requestAnimationFrame()。接收一个动画操作的参数

与 setTimeout()类似,requestAnimationFrame()也返回一个请求 ID,可以用于通过另一个 方法 cancelAnimationFrame()来取消重绘任务。下面的例子展示了刚把一个任务加入队列又立即将 其取消:

let requestID = window.requestAnimationFrame(() => {
  console.log('Repaint!');
}); 

window.cancelAnimationFrame(requestID);

所谓钩子(hook),就是浏览器在执行下一次重 绘之前的一个点。每次调用 requestAnimationFrame()都会在队列上推入一个回调函数,队列的长度没有限制。在频繁执 行影响页面外观的代码时(比如滚动事件监听器),可以利用这个回调队列进行节流。

如果想把事件处理程􏰀的调用限制在每次重绘前发生,那么可以像这样下面把它封装到 request- AnimationFrame()调用中:

    let enabled = true;

    function expensiveOperation() {
      console.log('Invoked at', Date.now());

}

    window.addEventListener('scroll', () => {

if (enabled) {  
enabled = false; window.requestAnimationFrame(expensiveOperation); window.setTimeout(() => enabled = true, 50);

}

});

18.2 基本画布功能

创建<canvas>元素时至少要设置其 width 和 height 属性

创建对象-取得绘图上下文-绘图-可以导出

getContext toDataURL

18.3 2d 绘图上下文

填充和描边:
fillStyle 和 strokeStyle。

fillRect()、strokeRect()和 clearRect()

绘制路径:beginPath()

绘制文本:fillText() 和 strokeText()。

变换

图像,阴影,渐变

18.4 WebGL 3D

以后再说