React中内嵌input的label标签为何会触发两次onClick事件?
为何内部包含input的label标签会使onClick事件执行两次?
这事儿是HTML原生行为加事件冒泡搞出来的:
- 当你点击label里的radio按钮时,浏览器先触发input的点击事件,接着因为label和input的关联特性,浏览器会自动触发一次label的点击事件,用来实现“点label也能选中控件”的功能。
- 同时,input的点击事件会向上冒泡到它的父元素label,又触发了一次label的onClick回调。
- 再加上React 18的StrictMode在开发环境下会重复执行事件回调来检测问题,这就让两次触发的现象更显眼,但就算去掉StrictMode,原生行为导致的两次触发依然存在。
解决办法
有几种方式可以搞定:
- 阻止事件冒泡:给input加onClick事件,阻止事件向上传递,这样就不会因为冒泡再触发一次label的回调:
function App() { return ( <label className="label" onClick={()=> console.log("label onClick")}> <input className="radio" type="radio" onClick={(e) => e.stopPropagation()} /> </label> ); }
- 分开label和input:不用嵌套结构,改用label的
htmlFor属性和input的id关联,这样点击label时只会触发一次回调:
function App() { return ( <> <label className="label" htmlFor="radio-btn" onClick={()=> console.log("label onClick")} ></label> <input id="radio-btn" className="radio" type="radio" /> </> ); }
- 换绑定位置:如果没必要在label上绑定事件,直接把逻辑绑在input的onClick上就行。
原代码示例
JS代码
const { StrictMode } = React; const { createRoot } = ReactDOM; function App() { return ( <label className="label" onClick={()=> console.log("label onClick")}> <input className="radio" type="radio" /> </label> ); } const root = createRoot(document.getElementById("root")); root.render(<StrictMode><App /></StrictMode>);
CSS代码
.label { display: block; background-color: red; width: 300px; height: 100px; position: relative; } .radio { width: 30px; height: 30px; }
HTML代码
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script> <div id="root"></div>
内容的提问来源于stack exchange,提问作者m_jdm35
相关产品推荐
相关产品推荐

