DOM事件

认识Dom事件

DOM中的事件->行为,行为背后对应着执行某个(某些)待执行函数。

事件的基本逻辑

  1. 给元素的某个行为添加上对应的函数 (称为事件处理函数)。
  2. 等到用户对该元素实施该行为。
  3. 浏览器会检测到,在元素上发生了该行为,就会马上触发该行为绑定的函数。
  4. 函数执行,那么会产生相应的效果。

常用的事件绑定方法

const h1 = document.querySelector(".en")

function abc(){

}

h1.addEventListener('click',abc)

事件处理函数中的this: 非箭头函数中是指向绑定元素本身的,可以通过this 来访问元素。如果你需要在事件处理函数中使用this,就用function关键字的函数。

重要的事件对象 

什么是事件对象?

事件处理函数中系统自动传入的第一个参数。

事件(行为)发生后,所有相关的信息都会进入到事件对象中,从而让人们进行使用。我们可以通过事件对象来获取到这些信息,从而实现各种想要的效果。

要想获取事件对象,只需要在事件处理函数中定义一个形参,通过第一个形参就能获取这个事件对象,这个形参命名可以自定义 一般来说命名为e 或者event。

body.addEventListener('click', (e) => {
  console.log("我是body")
})

编程说明:一般来说,事件对象中有非常多的属性和一些方法不同的属性,会在不同的类型上通过继承来实现,可以根据事件对象类型的继承关系,去相应的父类上找到属性和方法的说明。

事件对象的常用属性(重要)

事件对象的常用属性

事件流机制

 1.当途中按钮被点击,click事件触发,新的事件对象就会被创建。但不会被创建在实际点击的地方,而是创建再document的根目录下——也就是树的最顶端。

2.该事件会在所谓的捕获阶段沿着整棵树传播,一路直达目标元素目标元素就是实际触发事件的元素。(浏览器自然知道这个目标元素该是谁)。

3.我们就可以选择再该元素上放置事件处理函数来处理事件。

4.然后事件在到达目标元素后立即就会沿着树一路返回,也就是所谓的冒泡阶段。

5.如果说父元素也监听了目标元素的相同的事件类型,该父元素的该事件处理函数也会执行。

6.如果你想要阻止父元素的行为,就可以在目标元素上使用e.stopPropagation()来阻止冒泡。

注意:在CSS中,元素被覆盖(overlapped)指的是一个元素位于另一个元素的上方,覆盖了后者的部分或全部内容。在这种情况下,鼠标事件会被覆盖的元素捕获,而不会传递到被覆盖的元素上。具体来说,覆盖的元素会接收鼠标事件,例如mouseovermouseoutclick等,而被覆盖的元素不会接收这些事件。这是因为浏览器的事件模型是基于捕获和冒泡的机制来处理事件的,当一个元素被覆盖时,覆盖的元素会捕获鼠标事件,而不会传递到被覆盖的元素上。 

event中target与currentTarget的区别

  • target属性:指向事件最初发生的元素,即引发事件的目标元素。无论事件处理函数绑定在哪个元素上,target属性始终指向同一个元素,即最初触发事件的那个元素,也就是目标元素对象。
  • currentTarget属性:指向当前正在处理事件的元素,即事件处理函数绑定的元素。随着事件在DOM树中传播(无论是捕获阶段还是冒泡阶段),每个处理函数中的currentTarget会变化,以反映当前正在执行处理函数的元素。

结合DOM树非常好理解。

实现交互的基本方法

实时交互性变化

  • 直接修改style属性:适用于需要根据用户实时输入(如鼠标位置)动态改变元素样式的情况。这种方法可以实现即时的视觉反馈。

过渡效果

  • CSS transition:适合简单的从一个状态到另一个状态的变化,例如颜色、尺寸、透明度等。transition需要事件触发(如点击或悬停),并且只能在两个状态之间进行平滑过渡。
  • CSS animation:适合更复杂的动画,可以定义多个中间状态,通过@keyframes实现。它不需要事件触发,可以自动运行,并支持循环和方向控制。

复杂动画

  • JavaScript Element.animate()方法:适用于需要更复杂控制和交互的动画,尤其是需要在动画过程中动态调整属性时。这种方法提供了更高的灵活性和控制力。
  • 使用库(如Lottie):对于非常复杂的动画,通常使用动画软件(如After Effects)制作,然后通过工具导出为可嵌入网页的格式。这种方法适合于需要高质量和复杂效果的场景。

常见变化类型

选择具体方法时,需要考虑以下因素:

  • 动画复杂度:简单过渡用transition,复杂动画用animation或JavaScript。
  • 性能影响:CSS过渡和动画通常比JavaScript更高效,因为它们可以由浏览器优化。
  • 交互需求:实时交互建议直接修改样式,非实时交互可以使用过渡或动画。

动画交互常用的属性与方法 

动画交互的常用属性与方法

常见事件

常见事件

事件补充

事件委托机制

为什么需要事件委托机制?

如上图DOM树所示,如果我们有1000个按钮的话,如果给每个按钮都加上事件处理函数,这会严重影响网站的性能。所以我们就可以使用事件委托机制。

实现机制

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DOM 树事件委托</title>
    <style>
        .btn { padding: 10px; margin: 5px; display: inline-block; background-color: #ccc; cursor: pointer; }
    </style>
</head>
<body>
    <div id="root">
        <header class="topbar">
            <div class="options">
                <button class="btn">Button 1</button>
                <button class="btn">Button 2</button>
                <button class="btn">Button 3</button>
            </div>
        </header>
    </div>

    <script>
        // 获取父元素
        const root = document.getElementById('root');

        // 为父元素添加点击事件监听器
        root.addEventListener('click', function(event) {
            // 检查点击的目标是否是按钮
            if (event.target && event.target.matches('.btn')) {
                alert('Clicked: ' + event.target.textContent);
            }
        });
    </script>

</body>
</html>

 编程提醒:如果你发现事件处理时出现了一些奇怪现象,那他有可能和事件冒泡有关。

节流与防抖

防抖

“你先一直执行,等你执行完,再过一段时间执行”,

例如,一个搜索输入框,等输入停止后,再触发搜索

function debounce(fn, delay = 200) {
    let timer = 0;
    return function(...args) {
        if (timer)  clearTimeout(timer);
        timer = setTimeout(() => {
            fn.apply(this, args);
            timer = 0;
        }, delay)
    }
}

 立即执行版本:

function debounce(fn, delay = 200, immediate = true) {
    let timer = 0;

    return function(...args) {
        if (timer)  clearTimeout(timer);
        
        if (immediate && !timer) {
            fn.apply(this, args);
        }

        timer = setTimeout(() => {
            fn.apply(this, args);
            timer = 0;
        }, delay)
    }
}

例子: 

<input type="text" id="input1" placeholder="只能输入数字">
<div id="message"></div>

<script>
function debounce(fn, delay = 200) {
    let timer = 0;
    return function() {
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
            // 关键点:通过 apply 传递 this 和 arguments
            fn.apply(this, arguments);
            timer = 0;
        }, delay);
    };
}

const input1 = document.getElementById('input1');
const message = document.getElementById('message');

input1.addEventListener('keydown', debounce(function(event) {
    // 1. 使用 this 获取输入框的值
    const value = this.value;
    
    // 2. 使用 event.key 获取按下的键
    const key = event.key;

    // 显示按键信息
    message.textContent = `最后一次按下的键:${key},当前输入内容:${value}`;

    // 3. 验证输入:如果不是数字,阻止默认行为
    if (!/[0-9]/.test(key)) {
        event.preventDefault(); // 阻止输入非数字字符
        message.style.color = "red";
    } else {
        message.style.color = "green";
    }
}));
</script>

为什么需要 this 和 event

1. this 的作用

  • 在事件监听器中,this 默认指向触发事件的元素(即输入框 input1)。

  • 我们需要通过 this.value 获取输入框的当前值,而不是直接通过 input1.value。这样做的好处是:

    • 代码更通用:如果同一个防抖函数应用到多个输入框,this 会自动指向各自的输入框。

    • 避免闭包问题:如果直接在防抖回调中写 input1.value,当多个输入框共用同一个防抖函数时,可能会指向错误的元素。

2. event 的作用

  •  .key(按下的按键)、event.preventDefault()(阻止默认行为)。

  • 我们需要通过 event.key 判断用户按下的键是否是数字,如果不是,则调用 event.preventDefault() 阻止输入。

如果不用 fn.apply(this, arguments) 会怎样?

假设防抖函数中直接调用 fn(),而不是 fn.apply(this, arguments)

问题 1:this 指向错误

  • fn 中的 this 会变成全局对象(浏览器中是 window),而不是输入框元素。

  • 导致 this.value 变成 window.value(即 undefined),无法获取输入框的值。

问题 2:event 参数丢失

  • fn 无法接收到 event 对象,导致 event.key 和 event.preventDefault() 失效。

  • 用户按下非数字键时,无法阻止输入,也无法显示按下的键信息。

图示:

节流 

“别急,一个一个来,按时间节奏来,插队者无效”。

例如,drag或者scroll期间触发某个回调,要设置一个时间间隔。

function throttle(fn, delay = 200) {
    let timer = 0;
    return function(...args) {
        if (timer)  return;
        timer = setTimeout(() => {
            fn.apply(this, args);
            timer = 0;
        }, delay)
    }
}

立即执行如下:

function throttle(fn, delay = 200, immediate = true) {
    let timer = 0;
    return function(...args) {
        if (timer)  return;
        
        if (immediate && !timer) {
            fn.apply(this, args);
        }

        timer = setTimeout(() => {
            fn.apply(this, args);
            timer = 0;
        }, delay)
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值