J M*_*Mei 2 css sass radio-button reactjs
我一直在网上寻找创建自定义复选框和单选按钮的方法。我已设法创建一个复选框,但单选框遇到的一个问题是单击它不会激活或触发我输入的 onChange 调用。我目前有这个:
const customButtons = props => {
const [isChecked, setChecked] = React.useState(false);
const toggleCheck = e => {
setChecked(e.target.checked || !isChecked)
}
return (
<>
<span className={container}>
<input type={props.type} checked={isChecked} onChange={e => toggleCheck(e)} id={props.id} />
<span></span>
</span>
</>
)
}
Run Code Online (Sandbox Code Playgroud)
我已经使用 css 使跨度覆盖单选按钮并制作了原始单选按钮display: none;,但是当我单击跨度圆圈时,它不会触发单击。我向 span 添加了一个 onClick:<span onClick={toggleCheck}>但这会导致单击两次时单选按钮被取消选中。在保持原始行为的同时实现自定义单选按钮的更好方法是什么?
如果这很重要的话,我也在使用 scss。
您的方法适用于两者radio,checkboxes如果输入未设置为display: none,当然就像正常输入一样。但是,如果将它们设置为 display: none,则实际上是在 UI 事件中隐藏它们,因此它们根本不会触发任何点击。
\n\n\nTLDR:更好的方法是,
\nopacity: 0在输入上设置 ,使用标签来htmlFor触发更改。然后将标签伪元素设置为看起来像收音机。
这是实时代码沙箱的链接
\n\n由于您没有提供样式,因此很难判断您如何直观地布置自定义输入。用我的方法,
\n\n大多数 UIradios在只需要选择一个选项和checkboxes多项选择时使用。也就是说,可以轻松地将状态从各个无线电选项提升到父无线电组组件,然后传递无线电状态,同时让复选框控制其各自的状态,因为它们是相互独立构建的。
另一个观察结果是,您的收音机缺少name属性(Reason why you were seeing multiple clicks with just fewer or no change at all),导致它们彼此脱节。要将它们放在一个组中,它们需要共享一个共同的name属性,这样您只需针对每个无线电的选项值。
一旦选择了所有没有公共组(无名称属性)的单选选项,您就无法在 UI 上取消选择它们,因此它们不会触发任何进一步的 onChange 事件。因此,如果选项不是强制性的,建议添加重置选项以清除这些选项。
以下是每个无线电输入组件的代码。
\n\nconst RadioInput = ({ name, label, value, isChecked, handleChange }) => {\n const handleRadioChange = e => {\n const { id } = e.currentTarget;\n handleChange(id); // Send back id to radio group for comparison\n };\n\n return (\n <div>\n {/* Target this input: opacity 0 */}\n <input\n type="radio"\n className="custom-radio"\n name={name}\n id={value} // htlmlFor targets this id.\n checked={isChecked}\n onChange={handleRadioChange}\n />\n <label htmlFor={value}>\n <span>{label}</span>\n </label>\n </div>\n );\n};\nRun Code Online (Sandbox Code Playgroud)\n\nlabel请注意,通常在编写自定义输入来覆盖本机输入时,如果您定位该元素并利用其forakahtmlFor属性来选择输入,则会更容易。从之前的努力来看,很难用自定义元素取悦所有屏幕阅读器,尤其是当您覆盖的本机input设置为不显示时。
在我看来,最好将其绝对定位,将其不透明度设置为零,然后让标签触发其变化。
\n\n此处链接到沙盒
\n\n组件的完整代码
\n\n应用程序.js
\n\n\n\nimport React, { useState } from "react";\nimport "./styles.scss";\n\n/*\nLet Checkbox the controls its own state.\nStyling \'custom-radio\', but only make the borders square in .scss file.\n*/\nconst CheckboxInput = ({ name, label }) => {\n const [isChecked, setIsChecked] = useState(false);\n\n const toggleCheck = e => {\n setIsChecked(() => !isChecked);\n };\n\n return (\n <div>\n <input\n type="checkbox"\n className="custom-radio"\n name={name}\n id={name}\n checked={isChecked}\n onChange={toggleCheck}\n />\n <label htmlFor={name}>\n <span>{label}</span>\n </label>\n </div>\n );\n};\n\n/*\nThe custom radio input, uses the same styles like the checkbox, and relies on the \nradio group parent for its state.\n*/\nconst RadioInput = ({ name, label, value, isChecked, handleChange }) => {\n const handleRadioChange = e => {\n const { id } = e.currentTarget;\n handleChange(id);\n };\n\n return (\n <div>\n <input\n type="radio"\n className="custom-radio"\n name={name}\n id={value}\n checked={isChecked}\n onChange={handleRadioChange}\n />\n <label htmlFor={value}>\n <span>{label}</span>\n </label>\n </div>\n );\n};\n\n/*\nThis is what control the radio options. Each radio input has the same name attribute\nthat way you can have multiple groups on the form.\n*/\nconst RadioGropupInput = () => {\n const [selectedInput, setSelectedInput] = useState("");\n\n const handleChange = inputValue => {\n setSelectedInput(inputValue);\n };\n\n return (\n <>\n <div>\n {/*\n You could map these values instead from an array of options\n And an option to clear the selections if they are not mandatory.\n PS: Add aria attributes for accessibility\n */}\n <RadioInput\n name="option"\n value="option-1"\n label="First Choice"\n isChecked={selectedInput === "option-1"}\n handleChange={handleChange}\n />\n <RadioInput\n name="option"\n value="option-2"\n label="Second Choice"\n isChecked={selectedInput === "option-2"}\n handleChange={handleChange}\n />\n <RadioInput\n name="option"\n value="option-3"\n label="Third Choice"\n isChecked={selectedInput === "option-3"}\n handleChange={handleChange}\n />\n </div>\n </>\n );\n};\n\nexport default () => (\n <div className="App">\n <RadioGropupInput />\n <hr />\n <CheckboxInput name="remember-me" label="Remember Me" />\n <CheckboxInput name="subscribe" label="Subscribe" />\n </div>\n);\n\nRun Code Online (Sandbox Code Playgroud)\n\n风格
\n\n\n\n.custom-radio {\n /* Hide the input element and target the next label that comes after it in the DOM */\n position: absolute;\n display: inline-block;\n opacity: 0;\n\n & + label {\n cursor: pointer;\n display: inline-block;\n position: relative;\n white-space: nowrap;\n line-height: 1rem;\n margin: 0 0 1.5rem 0;\n padding: 0 0 0 1rem;\n transition: all 0.5s ease-in-out;\n\n span {\n margin-left: 0.5rem;\n }\n\n /* Styles these pseudo elements to look like radio inputs. */\n &::before,\n &::after {\n content: \'\';\n position: absolute;\n color: #f5f5f5;\n text-align: center;\n border-radius: 0;\n top: 0;\n left: 0;\n width: 1rem;\n height: 1rem;\n transition: all 0.5s ease-in-out;\n }\n\n &::before {\n text-rendering: auto;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n line-height: 1rem;\n border-radius: 0;\n background-color: #ffffff;\n color: #ffffff;\n box-shadow: inset 0 0 0 1px #666565, inset 0 0 0 1rem #ffffff,\n inset 0 0 0 1rem #6b0707;\n }\n\n &:hover,\n &:focus,\n &:active {\n color: red;\n font-weight: bolder;\n transition: all 0.3s ease;\n outline: none;\n\n &::before {\n color: #ffffff;\n animation-duration: 0.5s;\n animation-name: changeSizeAnim;\n animation-iteration-count: infinite;\n animation-direction: alternate;\n box-shadow: inset 0 0 0 1px #6b0707, inset 0 0 0 16px #ffffff,\n inset 0 0 0 16px #6b0707;\n }\n }\n }\n\n &:focus,\n &:hover,\n &:checked {\n & + label {\n color: #220000 !important;\n }\n\n & + label::before {\n animation-duration: 0.3s;\n animation-name: selectCheckboxAnim;\n animation-iteration-count: 1;\n animation-direction: alternate;\n border: solid 1px rgba(255, 0, 0, 0.5);\n box-shadow: inset 0 0 0 1px #bc88d4, inset 0 0 0 0 #ffffff,\n inset 0 0 1px 2px #6d1717;\n }\n }\n\n &:checked {\n & + label::before {\n content: \'\xe2\x9c\x94\'; /* Swap out this emoji checkmark with maybe an icon font of base svg*/\n background-color: #d43333;\n color: #ffffff;\n border: solid 1px rgba(202, 50, 230, 0.5);\n box-shadow: inset 0 0 0 1px #bc88d4, inset 0 0 0 0 #ffffff,\n inset 0 0 0 16px #d43333;\n }\n }\n\n & + label {\n &::before {\n border-radius: 50%;\n }\n }\n\n &[type=checkbox] {\n & + label {\n &::before {\n /* Remove the border radius on the checkboxes for a square effect */\n border-radius: 0;\n }\n }\n }\n\n\n @keyframes changeSizeAnim {\n from {\n box-shadow: 0 0 0 0 #d43333,\n inset 0 0 0 1px #d43333,\n inset 0 0 0 16px #FFFFFF,\n inset 0 0 0 16px #d43333;\n }\n\n to {\n box-shadow: 0 0 0 1px #d43333,\n inset 0 0 0 1px #d43333,\n inset 0 0 0 16px #FFFFFF,\n inset 0 0 0 16px #d43333;\n }\n }\n\n /* Add some animations like a boss, cause why would you hustle to build\n a custom component when you can\'t touch this!\n */\n @keyframes selectCheckboxAnim {\n 0% {\n box-shadow: 0 0 0 0 #bc88d4,\n inset 0 0 0 2px #FFFFFF,\n inset 0 0 0 3px #d43333,\n inset 0 0 0 16px #FFFFFF,\n inset 0 0 0 16px #d43333;\n }\n\n 100% {\n box-shadow: 0 0 20px 8px #eeddee,\n inset 0 0 0 0 white,\n inset 0 0 0 1px #bc88d4,\n inset 0 0 0 0 #FFFFFF,\n inset 0 0 0 16px #d43333;\n }\n }\n}\n\n/* Styles used to test out and reproduce out your approach */\n.container.control-experiment {\n background: #fee;\n\n\n span,\n input {\n display: flex;\n\n border: solid 1px red;\n width: 2rem;\n height: 2rem;\n line-height: 2rem;\n display: inline-block;\n }\n\n input { \n position: absolute;\n margin: 0;\n padding: 0;\n }\n\n input[type=\'radio\'] {\n // display: none; /* Uncommenting this out makes all your inputs unsable.*/\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n我重复一遍以强调,不要忘记添加自定义输入的 aria 属性。\n您可以再次测试实时沙箱
\n