我一直在网上寻找创建自定义复选框和单选按钮的方法。我已经设法创建了一个复选框,但是单选框存在的一个问题是单击它不会激活或触发输入中的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>
</>
)
}
我已经使用css来获得覆盖单选按钮的范围,并制作了原来的单选按钮display: none;
,但是当我单击范围圆圈时,它不会触发单击。我在范围中添加了onClick:<span onClick={toggleCheck}>
,但这会导致单击两次时取消选中单选按钮。在维持原始行为的同时实现自定义单选按钮的更好方法是什么?
如果这很重要,我也在使用scss。
答案 0 :(得分:1)
我尝试了您的示例,并使用了toggleCheck登录,并且它被单选和复选框触发。
CustomButtons组件
import React from "react";
const CustomButtons = props => {
const [isChecked, setChecked] = React.useState(false);
const toggleCheck = e => {
console.log(e);
setChecked(e.target.checked || !isChecked)
};
return (
<>
<span>
<input type={props.type} checked={isChecked} onChange={e => toggleCheck(e)} id={props.id}/>
<span>{props.text}</span>
</span>
</>
)
};
export default CustomButtons
如何在App.js中使用自定义按钮
<CustomButtons type={"radio"} text={"One"}/>
<CustomButtons type={"radio"} text={"Two"}/>
<CustomButtons type={"checkbox"} text={"One"}/>
<CustomButtons type={"checkbox"} text={"Two"}/>
答案 1 :(得分:1)
如果输入未设置为radio
,则您的方法同时适用于checkboxes
和display: none
,就像正常输入一样。但是,如果将它们设置为显示:无,则实际上是在UI事件中隐藏它们,因此它们根本不会触发任何点击。
TLDR :更好的方法是,在输入上设置
opacity: 0
,使用带有htmlFor
的标签来触发更改。然后设置标签伪元素的样式,使其看起来像收音机。
此处是指向Live Code Sandbox的链接
由于您未提供样式,因此很难说出您在视觉上如何布置自定义输入。用我的方法,
大多数UI仅在选择一个选项时需要使用radios
,而在选择多个选项时则使用checkboxes
。也就是说,将状态从单个无线电选项提升到父无线电组组件,然后传递无线电状态变得容易,同时让复选框控制它们各自的状态,因为它们彼此独立。 p>
另一个观察结果是您的收音机缺少name
属性(Reason why you were seeing multiple clicks with just fewer or no change at all
),使它们彼此不相交。要将它们放置在一个组中,它们需要共享一个公用的name
属性,这样,您只需将每个无线电的选项值作为目标即可。
一旦选择了所有没有公共组(无名称属性)的单选选项,就无法在UI上取消选择它们,因此它们不会触发任何进一步的onChange事件。因此,如果不是必需的,建议添加一个重置选项以清除这些选项。
这是每个无线电输入组件的代码。
const RadioInput = ({ name, label, value, isChecked, handleChange }) => {
const handleRadioChange = e => {
const { id } = e.currentTarget;
handleChange(id); // Send back id to radio group for comparison
};
return (
<div>
{/* Target this input: opacity 0 */}
<input
type="radio"
className="custom-radio"
name={name}
id={value} // htlmlFor targets this id.
checked={isChecked}
onChange={handleRadioChange}
/>
<label htmlFor={value}>
<span>{label}</span>
</label>
</div>
);
};
请参见,通常在编写自定义输入以覆盖本机输入时,如果将label
元素作为目标并利用其for
的{{1}}属性来选择输入会更容易。从以前的工作来看,很难用自定义元素来吸引所有屏幕阅读器,尤其是当您覆盖的本地htmlFor
设置为不显示时。
我认为,最好绝对定位它,将其不透明度设置为零,并让标签触发它的变化。
链接到Sandbox here
组件的完整代码
App.js
input
样式
import React, { useState } from "react";
import "./styles.scss";
/*
Let Checkbox the controls its own state.
Styling 'custom-radio', but only make the borders square in .scss file.
*/
const CheckboxInput = ({ name, label }) => {
const [isChecked, setIsChecked] = useState(false);
const toggleCheck = e => {
setIsChecked(() => !isChecked);
};
return (
<div>
<input
type="checkbox"
className="custom-radio"
name={name}
id={name}
checked={isChecked}
onChange={toggleCheck}
/>
<label htmlFor={name}>
<span>{label}</span>
</label>
</div>
);
};
/*
The custom radio input, uses the same styles like the checkbox, and relies on the
radio group parent for its state.
*/
const RadioInput = ({ name, label, value, isChecked, handleChange }) => {
const handleRadioChange = e => {
const { id } = e.currentTarget;
handleChange(id);
};
return (
<div>
<input
type="radio"
className="custom-radio"
name={name}
id={value}
checked={isChecked}
onChange={handleRadioChange}
/>
<label htmlFor={value}>
<span>{label}</span>
</label>
</div>
);
};
/*
This is what control the radio options. Each radio input has the same name attribute
that way you can have multiple groups on the form.
*/
const RadioGropupInput = () => {
const [selectedInput, setSelectedInput] = useState("");
const handleChange = inputValue => {
setSelectedInput(inputValue);
};
return (
<>
<div>
{/*
You could map these values instead from an array of options
And an option to clear the selections if they are not mandatory.
PS: Add aria attributes for accessibility
*/}
<RadioInput
name="option"
value="option-1"
label="First Choice"
isChecked={selectedInput === "option-1"}
handleChange={handleChange}
/>
<RadioInput
name="option"
value="option-2"
label="Second Choice"
isChecked={selectedInput === "option-2"}
handleChange={handleChange}
/>
<RadioInput
name="option"
value="option-3"
label="Third Choice"
isChecked={selectedInput === "option-3"}
handleChange={handleChange}
/>
</div>
</>
);
};
export default () => (
<div className="App">
<RadioGropupInput />
<hr />
<CheckboxInput name="remember-me" label="Remember Me" />
<CheckboxInput name="subscribe" label="Subscribe" />
</div>
);
我再次强调,请不要忘记为自定义输入添加aria属性。 同样,您可以测试live Sandbox