React中Styled Components实现嵌套HTML元素:单选组样式问题求助
&, > Hey there! Let's break down your problem step by step—first fixing the checked state issue, then clarifying those tricky selectors you're confused about.
Your Original Scenario
You're new to styled-components and trying to replicate this HTML segmented control:
<div class="segmentedControlContainer"> <input type="radio" name="segmentedControlExample" value="1" id="1" /> <label for="1"> One </label> <input type="radio" name="segmentedControlExample" value="2" id="2" /> <label for="2"> Two </label> <input type="radio" name="segmentedControlExample" value="3" id="3" /> <label for="3"> Three </label> </div>
You've got these styled components defined:
export const SegmentedControlWrapper = styled.div` display: flex; width: 100%; position: relative; z-index: 1; user-select: none; margin: 0 auto; font-size: 16px; border-radius: 3px; border: solid 1px #bcbcbc ` export const Input = styled.input` display: none; &:checked + label { color: #fff; background: #blah; } ` export const Label = styled.label` flex: 1; color: #blah; padding: 15px; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer; &:checked { } &:not(:last-child){ .... } `
And you're using them like this:
<SegmentedControlWrapper> <Input /> <Label /> </SegmentedControlWrapper>
You noticed the checked state isn't working, and you're confused about & and other selectors. Let's fix this!
1. Fixing the Checked State Issue
The main problems preventing your checked state from working are:
Missing Required Attributes
Your <Input /> and <Label /> components aren't linked properly. Radio inputs need a shared name to group them, and each input needs an id that matches the label's htmlFor prop. Without this, clicking the label won't toggle the input's checked state.
Invalid Selector & Color Values
&:checkedon the Label is useless—labels don't have a checked state; only inputs do. Delete that line.#blahisn't a valid color code. Replace it with real hex/rgb values (e.g.,#666for unselected text,#007bfffor selected background).
Corrected Component Usage
<SegmentedControlWrapper> <Input type="radio" name="segmentedControl" value="1" id="seg-1" defaultChecked /> <Label htmlFor="seg-1">One</Label> <Input type="radio" name="segmentedControl" value="2" id="seg-2" /> <Label htmlFor="seg-2">Two</Label> <Input type="radio" name="segmentedControl" value="3" id="seg-3" /> <Label htmlFor="seg-3">Three</Label> </SegmentedControlWrapper>
Updated Styled Components
export const Input = styled.input` display: none; /* & refers to the current Input component */ &:checked + label { color: #fff; background-color: #007bff; /* Valid blue color */ } ` export const Label = styled.label` flex: 1; color: #666; /* Unselected text color */ padding: 15px; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer; transition: background-color 0.2s ease; /* Smooth hover/checked transition */ /* Add right border to all except last label for segmented look */ &:not(:last-child) { border-right: 1px solid #bcbcbc; } `
2. Understanding &, >, and Other Selectors in Styled-Components
Styled-components uses Sass-like syntax for selectors—here's what those symbols mean:
&: The Current Component
This is a shortcut for referencing the component itself. For example:const Button = styled.button` background: #fff; /* & = button */ &:hover { background: #eee; } /* Target this button when it's inside a .container */ .container & { padding: 10px; } `In your Input component,
&:checked + labelmeans "when this input is checked, target the immediately following label".>: Direct Child Selector
Use this to target only direct children of the component:const Card = styled.div` /* Target only p tags that are direct children of the card */ > p { margin: 0; } `Other Useful Selectors
& + *: Target the next sibling element (like you did with&:checked + label)& ~ *: Target all siblings after the component&:not(selector): Exclude elements that match the selector (e.g.,&:not(:last-child))
3. Full Working Example
Putting it all together, here's a complete, runnable component:
import styled from 'styled-components'; export const SegmentedControlWrapper = styled.div` display: flex; width: 300px; /* Adjust width as needed */ position: relative; z-index: 1; user-select: none; margin: 20px auto; font-size: 16px; border-radius: 3px; border: solid 1px #bcbcbc; ` export const Input = styled.input` display: none; &:checked + label { color: #fff; background-color: #007bff; } ` export const Label = styled.label` flex: 1; color: #666; padding: 15px; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer; transition: all 0.2s ease; &:not(:last-child) { border-right: 1px solid #bcbcbc; } /* Optional hover effect */ &:hover { background-color: #f5f5f5; } ` function SegmentedControl() { return ( <SegmentedControlWrapper> <Input type="radio" name="segmentedControl" value="1" id="seg-1" defaultChecked /> <Label htmlFor="seg-1">One</Label> <Input type="radio" name="segmentedControl" value="2" id="seg-2" /> <Label htmlFor="seg-2">Two</Label> <Input type="radio" name="segmentedControl" value="3" id="seg-3" /> <Label htmlFor="seg-3">Three</Label> </SegmentedControlWrapper> ); } export default SegmentedControl;
Now your checked state should work perfectly, and you have a better grasp of how those selectors function in styled-components!
内容的提问来源于stack exchange,提问作者blackmamba24

