如何始终在有焦点的 Material-UI Button 组件上应用 focusVisible 样式?
Posted
技术标签:
【中文标题】如何始终在有焦点的 Material-UI Button 组件上应用 focusVisible 样式?【英文标题】:How to always apply focusVisible styling on a focused Material-UI Button component? 【发布时间】:2021-03-29 09:27:17 【问题描述】:对于 Material-UI Button 组件,我希望“focus”样式看起来与“focusVisible”样式相同。这意味着如果按钮以编程方式或使用鼠标聚焦,我希望它具有相同的涟漪效果,就像按钮通过 Tab 键聚焦一样。
我发现的一种解决方法是在元素获得焦点之前调用dispatchEvent(new window.Event("keydown"))
,导致键盘成为最后使用的输入类型。这将使按钮看起来像我想要的那样,直到 onMouseLeave 事件(来自 MUI
我已经想出了如何像这样改变组件的焦点样式:
import React from "react"
import withStyles from "@material-ui/core/styles"
import Button from "@material-ui/core/Button"
const styles =
root:
'&:focus':
border: "3px solid #000000"
const CustomButtonRaw = React.forwardRef((props, ref) =>
const classes, ...rest = props
return <Button classes=root: classes.root ...rest ref=ref/>
const CustomButton = withStyles(styles, name: "CustomButton" )(CustomButtonRaw)
export default CustomButton
所以,当按钮处于“焦点”状态时,我可以对按钮应用一些样式。 (例如,我应用了边框)。但我错过了如何应用样式。我尝试将 className 'Mui-visibleFocus' 放在 Button 上,但这似乎没有效果。如果 Button 处于 visibleFocus 状态,是否有某种方法可以获得将应用的样式?
【问题讨论】:
我也遇到了同样的问题,可惜还是不行 Think Blew 发布了一个 Answer 说“尝试了这个并想出了一个使用 Buttonbase https://codesandbox.io/s/modal-dialog-button-autofocus-y9hfz 的操作引用的解决方案” 【参考方案1】:我们可以创建对material-ui Button
组件的action
的引用,并使用useLayoutEffect
内的动作引用来实现涟漪效果
import React, createRef, useLayoutEffect from "react";
import Button from "@material-ui/core/Button";
function FocusButton(props)
const handleClose = props;
const actionRef = createRef();
useLayoutEffect(() =>
if (actionRef.current)
actionRef.current.focusVisible();
, []);
return (
<Button action=actionRef onClick=handleClose>
Ok
</Button>
);
上面的FocusButton
可以用来替代Button
或者干脆添加引用,在触发方法中调用focusVisible()
例如:
const buttonRef = createRef();
const handleButton2Click = () =>
buttonRef.current.focusVisible();
;
.
.
.
.
<Button action=buttonRef variant="outlined">
Button 1
</Button>
<Button variant="outlined" color="primary" onClick=handleButton2Click>
Button 2
</Button>
你可以在这个link找到演示
【讨论】:
【参考方案2】:ButtonBase
(其中Button
delegates to)有一个action prop,它提供set the button's focus-visible state的功能。
ButtonBase
为此利用了useImperativeHandle hook。要利用它,您将 ref 传递给 action
属性,然后您可以稍后调用 actionRef.current.focusVisible()
。
然而,这本身是不够的,因为有几个鼠标和触摸事件ButtonBase listens to 来启动/停止波纹。如果你使用disableTouchRipple
属性,它是prevents ButtonBase from trying to start/stop the ripple based on those events。
不幸的是,disableTouchRipple
阻止了按钮上的点击和触摸动画。这些可以通过显式添加您控制的另一个TouchRipple
元素来恢复。下面的示例显示了将 onMouseDown
和 onMouseUp
处理为概念验证,但理想的解决方案将处理 ButtonBase
处理的所有不同事件。
这是一个工作示例:
import React from "react";
import Button from "@material-ui/core/Button";
import TouchRipple from "@material-ui/core/ButtonBase/TouchRipple";
const FocusRippleButton = React.forwardRef(function FocusRippleButton(
onFocus, onMouseDown, onMouseUp, children, ...other ,
ref
)
const actionRef = React.useRef();
const rippleRef = React.useRef();
const handleFocus = (event) =>
actionRef.current.focusVisible();
if (onFocus)
onFocus(event);
;
const handleMouseUp = (event) =>
rippleRef.current.stop(event);
if (onMouseUp)
onMouseUp(event);
;
const handleMouseDown = (event) =>
rippleRef.current.start(event);
if (onMouseDown)
onMouseDown(event);
;
return (
<Button
ref=ref
action=actionRef
disableTouchRipple
onFocus=handleFocus
onMouseDown=handleMouseDown
onMouseUp=handleMouseUp
...other
>
children
<TouchRipple ref=rippleRef />
</Button>
);
);
export default function App()
return (
<div className="App">
<FocusRippleButton variant="contained" color="primary">
Button 1
</FocusRippleButton>
<br />
<br />
<FocusRippleButton
variant="contained"
color="primary"
onFocus=() => console.log("Some extra onFocus functionality")
>
Button 2
</FocusRippleButton>
</div>
);
【讨论】:
嗨,Ryan,这非常好,而且符合我的要求。但是使用这种应用“disableTouchRipple”道具的解决方案存在一个问题:现在按钮不再具有单击动画。您是否知道我可以从 ButtonBase 访问“rippleRef”以便我自己触发涟漪?或者是否有一种解决方法可以让点击动画再次工作? 你会得到一个点击动画,因为当你点击时焦点波纹立即开始;虽然我意识到它不一样——点击一个已经有焦点的按钮不会提供任何反馈。在没有disableTouchRipple
的情况下,我找不到让焦点涟漪对点击起作用的方法(我尝试了几种方法)。
Material-UI 没有公开任何访问rippleRef
的方式。如果您尝试通过TouchRippleProps
传递它,那么它会破坏ButtonBase
,因为它无法访问rippleRef
。如果不更改 Material-UI,我看不到任何方法。
看来你是对的。我一直在尝试找到一种访问rippleRef
的方法,但我认为没有一种方法......这很不幸。现在我想知道,是否可以使用单独的<TouchRipple/>
元素自己创建动画?
@user3044553 是的,可以为点击动画添加一个单独的<TouchRipple>
元素。我已经更新了我的答案,包括一个过于简单化(不处理 ButtonBase
处理的所有事件)版本。以上是关于如何始终在有焦点的 Material-UI Button 组件上应用 focusVisible 样式?的主要内容,如果未能解决你的问题,请参考以下文章
如何在错误和焦点上更改 Material-UI TextField 底部和标签颜色
使用 React Material-UI 自动完成始终显示默认选项
带有 Material-UI 的 s-s-r 上的 @emotion/cache 始终为空