JS事件向下穿透,给父元素绑定事件,点击到子元素时返回的target信息为子元素(想要父元素的信息)

本文介绍了一种解决JS事件穿透问题的方法,通过使用currentTarget属性,可以准确地获取到绑定事件的元素信息,而非触发事件的元素。这在处理复杂的DOM结构时尤其有用。

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

    这个问题答案很简单,但困扰了我两年,先上代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JS事件穿透</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>
<body>
    <div id="outDiv" data-info="out" style="border: 1px solid black;padding: 10px;">
        Out
        <div id="innerDIv"  data-info="inner" style="border: 1px solid red;padding: 5px;">
            Inner
        </div>
    </div>
</body>
<script>
    $("#outDiv").click(function (e) {
        console.log(e.target.dataset.info)
    })
</script>
</html>

效果图:

    在这段代码中,我想要实现的效果是点击Out名字的div,获取到data-info为out的信息,点击Out里面的子元素的时候,也返回我绑定事件的Out的Div的data-info的信息,但现在问题出在,当我点击Inner的div的时候,返回的是Inner这个div上的datainfo的信息,效果如图:

    我很苦恼,因为父元素里面可能会有很多子元素,一一处理起来很那,我最开始的做法是把父元素内所有的子元素都加上data-info属性,且值都和父元素一致,但这实在是一种蠢方法。网上常有的阻止事件穿透与事件冒泡,也与我的需求和场景不符。后来我打印e的时候,柳岸花明了:

    原来,e是有一个属性,叫做currentTarget,它返回的,永远是你绑定事件的元素,而不是当前触发事件的元素。

至此问题圆满解决,我想要的,就是currentTarget。

    PS:之前懒惰,在每个子元素里都添加data-info信息,现在想想实在太傻叉了。任何问题一定有解决方案,逃避是解决不了问题的,哈哈

<think>我们正在处理一个关于uvui框架中uv-textarea组件的问题:当在uv-textarea的父元素上添加点击事件点击uv-textarea本身无法触发父元素点击事件。我们需要解决这个问题。 可能的原因: 1. 事件冒泡被阻止:uv-textarea内部可能阻止了事件的冒泡,导致点击textarea事件没有传递到父元素。 2. uv-textarea组件本身是一个输入控件,它可能会捕获事件并停止冒泡,以避免与父元素事件冲突。 解决方案思路: 1. 我们可以尝试在uv-textarea上添加一个点击事件,并在该事件中手动触发父元素事件。 2. 或者,我们可以使用事件委托,但注意uv-textarea可能阻止了事件冒泡。 具体步骤: 方案一:在uv-textarea的点击事件中手动触发父元素点击事件 - 给父元素设置一个id或ref,以便在uv-textarea的事件处理函数中能够访问到父元素。 - 在uv-textarea的点击事件处理函数中,模拟一个事件并触发父元素点击事件。 方案二:使用事件委托,但需要确保事件能冒泡到父元素。如果uv-textarea内部阻止了冒泡,那么事件委托将不起作用,因此我们需要检查uv-textarea的源码或文档,看它是否阻止了事件冒泡。如果没有,那么事件委托应该可以工作。 由于我们无法确定uv-textarea内部是否阻止了冒泡,我们可以先尝试方案一。 示例代码(使用Vue语法): 假设我们的模板如下: ```html <div @click="handleParentClick"> <uv-textarea @click="handleTextareaClick"></uv-textarea> </div> ``` 在方法中: ```javascript methods: { handleParentClick() { console.log('父元素点击'); }, handleTextareaClick(e) { // 在这里手动触发父元素点击事件 // 因为点击textarea,我们希望父元素点击事件也被触发 // 注意:这里我们直接调用父元素事件处理函数,或者我们可以触发一个事件 // 由于事件冒泡已经被uv-textarea阻止,所以我们不能通过冒泡触发父元素事件,因此手动触发 // 方法1:直接调用父元素事件处理函数(如果逻辑简单且没有依赖事件对象) // this.handleParentClick(); // 方法2:创建一个事件并派发到父元素上(如果需要事件对象) // 注意:由于父元素事件监听在div上,我们可以获取父元素并触发其事件 const parent = e.currentTarget.parentNode; // 获取父元素 // 创建一个新的鼠标点击事件 const event = new MouseEvent('click', { bubbles: true, // 设置冒泡 cancelable: true, view: window }); parent.dispatchEvent(event); } } ``` 但是,请注意,这样可能会触发两次父元素事件:一次是uv-textarea触发父元素事件,另一次是用户点击父元素其他区域正常触发。因为我们只在点击textarea手动触发父元素事件,而点击父元素其他区域会正常触发。所以我们需要考虑是否会导致重复触发。 实际上,在点击textarea,我们手动触发了父元素点击事件,而点击父元素的其他区域(非textarea)则正常触发。所以不会重复触发。 另一种更简洁的方法:在uv-textarea的点击事件中,停止阻止事件冒泡(如果它阻止了的话),然后让事件自然冒泡到父元素。但是,这需要我们知道uv-textarea内部是否调用了`e.stopPropagation()`。如果它调用了,我们可以在自己的事件处理函数中先停止它,然后重新触发冒泡?但通常我们无法修改组件内部的行为。 因此,我们采用方案一:在uv-textarea的点击事件中手动触发父元素点击事件。 但是,如果uv-textarea组件内部已经阻止了事件冒泡,那么我们在它上面绑定点击事件仍然会触发(因为是在它自身上绑定的),然后我们在自己的事件处理函数中手动触发父元素事件。 所以,我们按照方案一实现。 注意:在Vue中,我们也可以使用$emit来触发父组件的事件,但这里uv-textarea是子组件,我们是在它的父组件上监听点击事件。所以,我们也可以考虑在uv-textarea上触发一个事件,然后在父组件中监听这个事件,并同触发父组件的点击事件。但是这样会使得逻辑分散。 因此,我们直接在uv-textarea的点击事件处理函数中触发父元素点击事件(通过调用父元素事件处理函数或派发事件)。 但是,如果父元素点击事件处理函数依赖于事件对象(比如event.target),那么我们在手动触发传递的事件对象可能不是原始的。因此,我们需要根据实际需求来处理。 如果父元素点击事件处理函数不依赖事件对象,我们可以直接调用它。如果依赖,我们可以创建一个新的事件对象,或者将原始事件对象传递给父元素事件处理函数。 另一种做法:在父元素事件处理函数中,我们根据事件对象来判断事件源,如果是uv-textarea,则忽略(因为我们已经手动处理了)?这样可能会复杂化。 因此,我们建议在父元素点击事件处理函数中不要过度依赖事件对象,或者我们可以将事件处理逻辑抽象成一个函数,然后在需要的地方调用。 例如: ```javascript methods: { handleParentClick(e) { // 如果事件是手动触发的,我们可能没有原始事件对象,所以这里我们考虑两种情况 // 我们可以将事件处理逻辑独立出来 this.doParentClickLogic(); }, handleTextareaClick(e) { this.doParentClickLogic(); }, doParentClickLogic() { // 实际的逻辑 console.log('执行父元素点击逻辑'); } } ``` 这样,无论从哪里触发,我们都调用同一个函数。 总结:根据具体情况选择方法。 如果问题仍然存在,请检查uv-textarea组件是否在内部阻止了点击事件的冒泡,并尝试在handleTextareaClick中调用e.stopPropagation()?但通常不需要,因为uv-textarea已经阻止了冒泡,所以事件不会传到父元素,我们才需要手动触发。 因此,我们采用在uv-textarea的点击事件中调用父元素事件处理逻辑的方法。 步骤: 1. 将父元素点击事件的处理逻辑提取到一个独立的方法中(例如doParentClick)。 2. 在父元素的@click事件中调用这个方法。 3. 在uv-textarea的@click事件中也调用这个方法。 这样,点击父元素其他区域,会触发父元素点击事件,从而调用doParentClick;点击uv-textarea,会触发handleTextareaClick,然后调用doParentClick。 示例代码: ```html <template> <div @click="handleParentClick"> <!-- 其他内容 --> <uv-textarea @click="handleTextareaClick"></uv-textarea> </div> </template> <script> export default { methods: { handleParentClick() { this.doParentClick(); }, handleTextareaClick() { this.doParentClick(); }, doParentClick() { // 这里写原本父元素点击事件需要执行的逻辑 console.log('执行父元素点击事件逻辑'); } } } </script> ``` 这样,无论点击父元素还是uv-textarea,都会执行相同的逻辑。 注意:如果父元素点击事件处理函数中需要访问事件对象(例如获取点击位置等),那么我们需要在调用doParentClick传递事件对象。但是,在handleTextareaClick中,我们可以传递原始事件对象,而在handleParentClick中也会传递事件对象。但是,在handleTextareaClick中,我们触发的是uv-textarea的点击事件事件对象与点击父元素事件对象不同。因此,如果逻辑依赖于事件对象(比如鼠标位置等),那么这种方法就不适用了。 在这种情况下,我们可以考虑在handleTextareaClick中创建一个模拟的事件对象,或者重新设计逻辑,使其不依赖事件对象。 如果逻辑不依赖事件对象,那么这种方法是最简单的。 如果依赖事件对象,并且父元素点击事件处理函数需要知道事件目标(event.target)来判断点击的是哪个元素,那么我们可以这样修改: 在handleParentClick中,我们传递事件对象给doParentClick,而在handleTextareaClick中,我们传递一个模拟的事件对象,其中target设置为父元素(或者uv-textarea的父元素,即当前div),或者我们也可以传递uv-textarea元素,具体根据逻辑来定。 但是,这样可能会使逻辑变得复杂。因此,我们建议,如果可能,尽量避免在父元素点击事件处理函数中依赖事件对象。如果必须依赖,那么可能需要重新考虑设计。 另一种思路:我们可以不在父元素上监听点击事件,而是在父元素内部的每个子元素上监听点击事件(除了uv-textarea),然后对于uv-textarea,我们单独处理。但是这样可能不够通用。 综上所述,根据实际需求选择合适的方法。如果逻辑简单,不依赖事件对象,那么提取公共方法是最简单的。如果依赖事件对象,可能需要更复杂的处理。 因此,我们提供两种解决方案: 方案A(推荐,逻辑不依赖事件对象): - 提取公共方法,在父元素点击事件和uv-textarea的点击事件中都调用这个公共方法。 方案B(逻辑依赖事件对象): - 在uv-textarea的点击事件中,手动触发父元素点击事件,并传递一个模拟的事件对象(但模拟事件对象可能无法完全复制原始事件的所有属性,因此可能不完美)。 - 或者,我们可以修改父元素事件处理函数,使其能够处理两种不同来源的事件(一种是原生事件,一种是手动触发的事件)。这可能需要额外的标志位。 考虑到复杂性和兼容性,我们优先推荐方案A。 如果方案A不满足需求(即必须使用事件对象),那么我们可以尝试在uv-textarea的点击事件中阻止组件内部的事件冒泡阻止(如果可能的话),或者联系uvui官方询问是否有办法允许事件冒泡。 最后,如果以上方法都不可行,我们可以考虑在父元素上监听事件使用捕获阶段(因为捕获阶段在冒泡之前),但uv-textarea阻止冒泡不会影响捕获。所以,我们可以将父元素事件监听改为在捕获阶段触发。 例如: ```html <div @click.capture="handleParentClick"> <uv-textarea></uv-textarea> </div> ``` 这样,在事件捕获阶段,父元素就会先触发事件,然后才传递到uv-textarea。但是,这样会导致父元素事件子元素之前触发,而且无论点击哪个子元素父元素都会触发。这可能会带来其他问题,比如我们可能不希望点击任何子元素都触发父元素事件。 因此,我们需要在父元素事件处理函数中判断事件目标,如果事件目标不是我们想要的(比如不是uv-textarea,而是其他子元素),那么就不执行逻辑?这又回到了依赖事件对象的问题。 所以,这种方法需要根据具体需求调整。 综上所述,我们提供多种方案,开发者可以根据实际需求选择。 由于问题描述中并没有具体说明父元素点击事件的逻辑是否依赖事件对象,我们给出一个通用的方案(方案A)作为首选。 因此,回答如下:</think>### 解决 uv-textarea 父元素点击事件未触发问题 在 uvui 框架中,当给 `uv-textarea` 的父元素添加点击事件点击文本区域无法触发父元素事件,这是因为 **事件冒泡被阻止**。以下是原因分析和解决方案: #### 问题原因 1. **组件内部事件处理** uv-textarea 组件内部可能调用了 `event.stopPropagation()` 阻止事件冒泡,导致点击事件无法传递到父元素。 2. **输入框默认行为** 浏览器中 `<textarea>` 元素会独占点击事件,阻止事件向父级传播,这是浏览器的默认行为。 --- ### 解决方案 #### 方法 1:通过 `@click.native` 强制触发(推荐) 为 `uv-textarea` 添加原生点击事件监听,手动触发父元素事件: ```html <template> <div @click="handleParentClick"> <!-- 添加 .native 修饰符监听原生事件 --> <uv-textarea @click.native="handleTextareaClick" placeholder="点击我" /> </div> </template> <script> export default { methods: { handleParentClick() { console.log("父元素点击"); }, handleTextareaClick(e) { // 手动触发父元素事件 this.handleParentClick(); // 如果需要原始事件对象(可选) // const newEvent = new Event('click', { bubbles: true }); // e.currentTarget.parentNode.dispatchEvent(newEvent); } } } </script> ``` #### 方法 2:使用 CSS 穿透指针事件 通过 CSS 允许事件穿透父元素(需注意输入框可用性): ```css /* 在父元素样式表中添加 */ .uv-textarea__wrapper { pointer-events: none; /* 禁用自身事件 */ } .uv-textarea__wrapper textarea { pointer-events: auto; /* 启用输入框事件 */ } ``` #### 方法 3:事件委托到更高层级 将事件监听提升到更外层容器: ```html <template> <div class="outer-container" @click="handleContainerClick"> <div> <!-- uv-textarea 的父元素 --> <uv-textarea placeholder="点击我"/> </div> </div> </template> <script> export default { methods: { handleContainerClick(e) { // 通过事件目标判断点击来源 if (e.target.closest('.uv-textarea')) { console.log("实际点击区域是 uv-textarea"); } } } } </script> ``` --- ### 关键注意事项 1. **事件修饰符** 使用 `@click.native` 而非 `@click`,确保监听原生 DOM 事件而非组件自定义事件。 2. **组件封装影响** 如果 uv-textarea 内部嵌套了多层 DOM,可能需要通过 `$el` 获取根元素: ```javascript handleTextareaClick(e) { e.currentTarget.$el.parentNode.click(); // 触发父元素 } ``` 3. **兼容性测试** 不同浏览器对事件传播的处理存在差异,需在目标平台(微信/H5)中验证方案有效性。 > **提示**:若问题持续存在,建议检查 uvui 版本更新日志,部分 UI 库会在后续版本优化事件传递机制[^1]。 ---
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值