事件冒泡;事件捕获; 触发顺序,先捕获,后冒泡;取消冒泡(封装取消冒泡的函数);事件对象

本文详细解析了事件冒泡和事件捕获的概念,包括它们的触发顺序、如何取消冒泡,以及不同浏览器对事件对象的支持情况。通过具体代码示例,展示了这两种事件传播方式在实际开发中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

事件冒泡:

        结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,

        即同一事件,自子元素冒泡向父元素。(自底向上)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        div {
            width: 500px;
            height: 500px;
            background-color: red;
        }

        span {
            display: inline-block;
            width: 200px;
            height: 200px;
            background-color: yellow;
        }

        strong {
            display: inline-block;
            width: 100px;
            height: 100px;
            background-color: blue;
        }
    </style>
</head>

<body>
    <div>
        <span>
            <strong></strong>
        </span>
    </div>
</body>
<script>
    /*
      事件冒泡:
        结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,
        即同一事件,自子元素冒泡向父元素。(自底向上)
    */
    const div = document.getElementsByTagName('div')[0];
    const span = document.getElementsByTagName('span')[0];
    const strong = document.getElementsByTagName('strong')[0];
    div.addEventListener('click', () => {
        console.log('你触发到div标签了')
    }, false)
    span.addEventListener('click', () => {
        console.log('你触发到span标签了')
    }, false)
    strong.addEventListener('click', () => {
        console.log('你触发到strong标签了')
    }, false)
</script>

</html>

 事件捕获:

       结构上(非视觉上)嵌套关系的元素,会存在事件捕获的功能,

       即同一事件,自父元素捕获子元素。(自顶向下)

       IE没有捕获事件。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        div {
            width: 500px;
            height: 500px;
            background-color: red;
        }

        span {
            display: inline-block;
            width: 200px;
            height: 200px;
            background-color: yellow;
        }

        strong {
            display: inline-block;
            width: 100px;
            height: 100px;
            background-color: blue;
        }
    </style>
</head>

<body>
    <div>
        <span>
            <strong></strong>
        </span>
    </div>
</body>
<script>
    /*
     事件捕获:
       结构上(非视觉上)嵌套关系的元素,会存在事件捕获的功能,
       即同一事件,自父元素捕获子元素。(自顶向下)
       IE没有捕获事件。
   */
    const div = document.getElementsByTagName('div')[0];
    const span = document.getElementsByTagName('span')[0];
    const strong = document.getElementsByTagName('strong')[0];
    div.addEventListener('click', () => {
        console.log('你触发到div标签了')
    }, true)
    span.addEventListener('click', () => {
        console.log('你触发到span标签了')
    }, true)
    strong.addEventListener('click', () => {
        console.log('你触发到strong标签了')
    }, true)



</script>

</html>

触发顺序,先捕获,后冒泡

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        div {
            width: 500px;
            height: 500px;
            background-color: red;
        }

        span {
            display: inline-block;
            width: 200px;
            height: 200px;
            background-color: yellow;
        }

        strong {
            display: inline-block;
            width: 100px;
            height: 100px;
            background-color: blue;
        }
    </style>
</head>
<body>
<div>
        <span>
            <strong></strong>
        </span>
</div>
</body>
<script>
    /*
    触发顺序,先捕获,后冒泡
     */
    const div = document.getElementsByTagName('div')[0];
    const span = document.getElementsByTagName('span')[0];
    const strong = document.getElementsByTagName('strong')[0];

    div.addEventListener('click', () => {
        console.log('你触发到div标签了——捕获')
    }, true);
    span.addEventListener('click', () => {
        console.log('你触发到span标签了——捕获')
    }, true);
    strong.addEventListener('click', () => {
        console.log('你触发到strong标签了——捕获')
    }, true);

    div.addEventListener('click', () => {
        console.log('你触发到div标签了——冒泡')
    }, false);
    span.addEventListener('click', () => {
        console.log('你触发到span标签了——冒泡')
    }, false);
    strong.addEventListener('click', () => {
        console.log('你触发到strong标签了——冒泡')
    }, false);


</script>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        div {
            width: 500px;
            height: 500px;
            background-color: red;
        }

        span {
            display: inline-block;
            width: 200px;
            height: 200px;
            background-color: yellow;
        }

        strong {
            display: inline-block;
            width: 100px;
            height: 100px;
            background-color: blue;
        }
    </style>
</head>
<body>
<div>
        <span>
            <strong></strong>
        </span>
</div>
</body>
<script>
    /*
    触发顺序,先捕获,后冒泡
     */
    const div = document.getElementsByTagName('div')[0];
    const span = document.getElementsByTagName('span')[0];
    const strong = document.getElementsByTagName('strong')[0];

    div.addEventListener('click', () => {
        console.log('你触发到div标签了——冒泡')
    }, false);
    span.addEventListener('click', () => {
        console.log('你触发到span标签了——冒泡')
    }, false);
    strong.addEventListener('click', () => {
        console.log('你触发到strong标签了——冒泡')
    }, false);

    div.addEventListener('click', () => {
        console.log('你触发到div标签了——捕获')
    }, true);
    span.addEventListener('click', () => {
        console.log('你触发到span标签了——捕获')
    }, true);
    strong.addEventListener('click', () => {
        console.log('你触发到strong标签了——捕获')
    }, true);

</script>
</html>

取消冒泡:
    W3C标准event.stopPropagation();但不支持IE9以下版本。
    IE独有event.cancelBubble=true;但是谷歌浏览器现在也支持了。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        div {
            width: 500px;
            height: 500px;
            background-color: red;
        }
    </style>
</head>
<body>
<div></div>
</body>
<script>
    /*
    取消冒泡:
        W3C标准event.stopPropagation();但不支持IE9以下版本。
        IE独有event.cancelBubble=true;但是谷歌浏览器现在也支持了。
     */
    document.onclick = function () {
        alert("触发到文档里面的点击事件了")
    };
    var div = document.getElementsByTagName('div')[0];
    div.onclick = function (e) {
        // e.stopPropagation();
        e.cancelBubble = true;
        this.style.backgroundColor = 'blue';
    };

    /*
    封装取消冒泡的函数stopBubble(event)
     */
    function stopBubble(event) {
        if (event.stopPropagation) {
            event.stopPropagation()
        } else {
            event.cancelBubble = true
        }
    }
</script>
</html>

事件对象

event||window.event用于IE;
事件源对象:
    event.target 火狐只有这个
    event.srcElement IE只有这个
    这俩chrome都有
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
</ul>
</body>
<script type="text/javascript">
    /*
    事件对象:
        event||window.event用于IE;
        事件源对象:
            event.target 火狐只有这个
            event.srcElement IE只有这个
            这俩chrome都有
     */
    var ul = document.getElementsByTagName('ul')[0];
    //事件源对象
    ul.onclick = function (event) {
        var event = event || window.event;
        var target = event.target || event.srcElement;
        console.log(target.innerText);
    }
</script>
</html>

 

<think>根据用户问题,需要在Vue3和TypeScript项目中使用ECharts,并解决点击图例(legend)时不触发冒泡事件的问题。冒泡事件指的是,当点击图例时,可能会触发父容器的事件(如点击事件)。我们通常希望点击图例时只执行图例的相关操作(如显示/隐藏系列),而不触发父元素的点击事件。解决方案思路:1.在ECharts中,图例的点击事件触发图例的选中状态变化,同时也会触发ECharts的`legendselectchanged`事件。2.我们可以通过监听ECharts的`legendselectchanged`事件,并在事件处理函数中阻止事件冒泡。但是,这个事件是ECharts自定义事件,不是原生的DOM事件,所以不会冒泡到DOM树中。3.实际上,用户的问题可能是:当点击图例时,会触发父容器的原生DOM事件(如click事件)。这是因为图例的点击事件本身也是DOM事件,会向上冒泡。因此,我们需要在ECharts图例的DOM事件上阻止冒泡。但是,ECharts并没有直接提供在内部DOM事件上添加阻止冒泡的方法。替代方案:我们可以通过给ECharts实例添加`zr`事件(zrender事件)来捕获原始事件,然后判断事件目标是否是图例元素,如果是则阻止事件冒泡。具体步骤:1.在初始化ECharts实例后,获取其zrender实例(通过`echartsInstance.getZr()`)。2.监听zrender实例的`click`事件(或其他需要阻止的事件)。3.在事件处理函数中,判断事件目标(target)是否是图例元素(图例元素通常具有特定的属性或类型,可以通过`target.type`判断,图例元素类型为`'legend'`或通过`target.is('legend')`判断?注意:实际中图例元素可能不是直接标记为legend,而是通过组件的图形元素类型判断)。4.实际上,ECharts的图例元素在zrender中并没有一个特定的类型标识。我们可以通过事件参数中的`target`的组件信息来判断:`target.__ecComponentInfo`中可能包含组件信息,但这不是官方API。另一种更直接的方法:由于ECharts的图例点击事件触发`legendselectchanged`事件,而同时也会产生一个原生的click事件。我们可以通过给ECharts容器绑定一个原生的click事件,然后判断事件目标是否在图例的DOM元素内(通过类名等),如果是则阻止冒泡。但是这种方法依赖于ECharts内部生成的DOM结构,而ECharts的DOM结构可能会随版本变化,不够稳定。推荐方法:在ECharts实例的`legendselectchanged`事件中,我们无法阻止原生事件冒泡。因此,我们需要在ECharts容器上监听原生click事件,然后判断事件目标是否在图例元素上(通过类名),如果是,则调用`event.stopPropagation()`。步骤:1.在ECharts容器上添加一个原生click事件监听器。2.在事件处理函数中,判断事件目标(event.target)是否具有图例元素的类(例如,ECharts图例元素通常有`'echarts-legend'`或`'echarts-legend-item'`类,具体需要查看生成的DOM结构)。3.如果目标元素是图例元素,则调用`event.stopPropagation()`。注意:由于ECharts渲染的图例元素类名可能会变化,我们可以通过查看当前版本的ECharts生成的图例元素的类名来确定。例如,ECharts5.x版本中,图例项通常有类名`'echarts-legend-item'`,图例标记有类名`'echarts-legend-icon'`,图例文本有类名`'echarts-legend-text'`。因此,我们可以检查事件目标或其父元素是否包含这些类名。但是,这种方法需要操作DOM,并且依赖于ECharts的内部实现,可能存在版本兼容问题。另一种思路:在ECharts的`legendselectchanged`事件中,我们无法阻止原生事件冒泡,但我们可以通过事件委托在父容器上处理原生事件,并判断事件发生的时间是否与图例点击事件接近(或者通过标志位)来决定是否阻止冒泡。这种方法不够优雅。更可靠的方法(官方API):在ECharts中,我们可以通过设置`legend`的`selectedMode`为`false`来禁止图例的点击切换功能,然后自己实现图例点击事件。但这需要完全自定义图例行为,比较复杂。经过分析,ECharts并没有提供直接阻止图例点击事件冒泡的API。因此,我们采用在容器上监听原生点击事件,然后根据类名判断并阻止冒泡的方法。具体代码步骤(Vue3+TypeScript):1.在模板中,给ECharts容器添加一个ref,并添加一个原生点击事件(注意:这里我们需要在事件处理函数中阻止冒泡,但不要阻止默认行为)。2.在事件处理函数中,判断事件目标(event.target)是否包含图例的类名(例如'echarts-legend-item')。3.如果包含,则调用`event.stopPropagation()`。但是,注意:图例元素可能包含多个部分(如图标、文本),所以我们需要检查事件目标或其父元素是否具有图例项的类名。代码示例:模板部分:```html<divref="chartRef"@click="handleChartClick"></div>```在setup中:```tsimport{onMounted,ref}from'vue';import*asechartsfrom'echarts';constchartRef=ref<HTMLElement|null>(null);letchartInstance:echarts.ECharts|null=null;//点击事件处理函数consthandleChartClick=(event:MouseEvent)=>{//判断事件目标是否为图例元素consttarget=event.targetasHTMLElement;//检查事件目标或其父元素是否具有图例项的类名if(isLegendElement(target)){event.stopPropagation();}};//判断是否为图例元素的辅助函数functionisLegendElement(element:HTMLElement):boolean{//检查当前元素或其父元素是否包含图例的类名while(element&&element!==chartRef.value){if(element.classList?.contains('echarts-legend-item')||element.classList?.contains('echarts-legend-icon')||element.classList?.contains('echarts-legend-text')){returntrue;}element=element.parentElementasHTMLElement;}returnfalse;}onMounted(()=>{if(chartRef.value){chartInstance=echarts.init(chartRef.value);//设置图表选项chartInstance.setOption({...});//你的图表配置}});```注意:这种方法依赖于ECharts生成的DOM元素的类名,如果ECharts版本更新导致类名变化,可能需要调整。另外,如果用户使用的是ECharts的svg渲染器,那么DOM结构会有所不同,但类名通常是相同的。另一种方案:在ECharts实例上监听`legendselectchanged`事件,然后设置一个标志位,在父容器的点击事件处理函数中检查这个标志位,如果是因为图例点击导致的,则阻止冒泡。但是,由于事件顺序问题(原生事件于ECharts事件?),可能不可行。因此,我们采用上述DOM类名判断的方法。最后,用户可能需要根据自己的ECharts版本调整类名。可以通过在浏览器中查看图例元素的类名来确认。相关问题:1.如何自定义ECharts图例的样式?2.在Vue3中使用ECharts有哪些注意事项?3.如何优化ECharts在Vue项目中的性能?</think>在 Vue3 和 TypeScript 中使用 ECharts 时,防止点击图例触发冒泡事件,可通过以下方案解决: ### 解决方案 #### 1. **阻止事件传播(推荐)** 在 ECharts 实例上监听图例点击事件,通过 `event.stopPropagation()` 阻止事件冒泡: ```typescript import * as echarts from 'echarts'; import { onMounted, ref } from 'vue'; const chartRef = ref<HTMLDivElement | null>(null); onMounted(() => { if (!chartRef.value) return; const chart = echarts.init(chartRef.value); const option: echarts.EChartsOption = { legend: { data: ['系列1', '系列2'], selectedMode: true // 允许点击图例 }, series: [{ /* 系列配置 */ }] }; chart.setOption(option); // 监听图例点击事件并阻止冒泡 chart.on('legendselectchanged', (event) => { event.event?.event.stopPropagation(); // 关键:阻止冒泡 }); }); ``` ```html <template> <div @click="handleParentClick"> <!-- 父容器有点击事件 --> <div ref="chartRef" style="width: 600px; height: 400px"></div> </div> </template> ``` #### 2. **自定义图例点击逻辑** 通过 `legend.selectedMode: false` 禁用默认行为,手动实现图例切换: ```typescript chart.setOption({ legend: { data: ['系列1', '系列2'], selectedMode: false // 禁用默认点击 } }); // 手动实现图例点击 chart.on('click', (params) => { if (params.componentType === 'legend') { // 自定义切换逻辑 const newOption = { ... }; chart.setOption(newOption); event.event?.stopPropagation(); // 阻止冒泡 } }); ``` #### 3. **全局事件拦截** 在父容器拦截特定类名的点击事件(依赖 ECharts 内部类名): ```typescript const handleParentClick = (event: MouseEvent) => { const target = event.target as HTMLElement; // 检查是否点击了图例元素(类名参考 ECharts 渲染结果) if (target.closest('.echarts-legend-item')) { event.stopPropagation(); // 阻止冒泡 } }; ``` ```html <template> <div @click="handleParentClick"> <!-- 父容器事件 --> <div ref="chartRef"></div> </div> </template> ``` ### 关键说明 1. **事件对象层级** ECharts 事件对象结构为:`{ event: MouseEvent, ... }`,需通过 `event.event.stopPropagation()` 访问原生事件[^1]。 2. **TypeScript 类型** 使用 `echarts.EChartsOption` 和 `echarts.EChartsEvent` 确保类型安全。 3. **与 Vue 事件修饰符对比** Vue 的 `.stop` 修饰符(如 `@click.stop`)仅适用于模板中的 DOM 元素,无法直接用于 ECharts 的 Canvas 事件。 ### 验证效果 - ✅ 点击图例时:触发图例切换,但不会触发父容器的 `handleParentClick` - ✅ 点击图表区域:正常触发父容器事件 ### 相关问题 1. 如何自定义 ECharts 图例的样式和交互行为? 2. 在 Vue3 中如何优雅地封装可复用的 ECharts 组件? 3. ECharts 在 Canvas 和 SVG 渲染模式下的事件处理有何差异? 4. 如何优化 Vue3 + ECharts 项目的性能以避免内存泄漏? > 引用说明: > [^1]: ECharts 事件对象结构参考官方文档,原生事件需通过 `event.event` 访问。 > [^2]: Vue 事件修饰符(如 `.stop`)仅适用于 DOM 元素,不适用于 Canvas 事件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值