ListItemButton自定义子组件时首次点击onClick不触发问题
问题原因及解决方法
问题分析
出现两次点击才触发ListItemButton点击事件的核心原因,是自定义HighlightText组件的根容器(StyledHighlightText)干扰了事件冒泡,或是浏览器将第一次点击优先识别为文本选择操作,而非触发父组件的点击事件。
对比两种场景的DOM结构:
- 使用原生
Typography时,DOM层级是ListItemButton→Typography(文本元素),点击事件可直接冒泡到父组件。 - 使用
HighlightText(带高亮场景)时,DOM层级变为ListItemButton→StyledHighlightText(自定义容器) → 多个Typography,自定义容器可能拦截了事件传递,或是浏览器的文本选择行为抢占了第一次点击的响应优先级。
修复方案
方案1:让自定义容器不拦截点击事件
修改StyledHighlightText的样式,添加pointer-events: none,让点击事件直接穿透到父级的ListItemButton:
const StyledHighlightText = styled.div` pointer-events: none; /* 保留原有其他样式 */ `;
该方案适用于HighlightText仅做展示、无需自身处理点击事件的场景。
方案2:统一DOM结构,复用Typography作为根容器
将HighlightText的根容器换成Typography,保持和原生场景一致的DOM层级:
const HighlightText = ({ text }: { text: string }) => { const splitText = text.split('}'); const hasHighlights = splitText.length > 1; if (!hasHighlights) { return <Typography variant="body1">{text}</Typography>; } return ( <Typography variant="body1" component="div"> {splitText[0].replace('{', '')} <StyledHighlightTextBold variant="body1"> {splitText[1]} </StyledHighlightTextBold> </Typography> ); };
通过component="div"让Typography渲染为div容器,既保持样式统一,又避免额外容器干扰事件传递。
方案3:禁用文本选择
如果是文本选择抢占了点击事件,可给StyledHighlightText添加禁用文本选择的样式:
const StyledHighlightText = styled.div` user-select: none; /* 保留原有其他样式 */ `;
这样第一次点击会直接触发父组件的点击事件,而非选中文本。
内容的提问来源于stack exchange,提问作者Kim Vu
相关产品推荐
相关产品推荐

