React onBlur回调中使用document.activeElement返回body解决方案

Posted ruoyxue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React onBlur回调中使用document.activeElement返回body解决方案相关的知识,希望对你有一定的参考价值。

React在onBlur函数中调用document.activeElement总是返回body,得不到新的聚焦元素怎么办?

最开始想实现一个功能,点击img图标后给出购物下拉框CartDropdown,当img及CartDropdown失去焦点时隐藏CartDropdown。

最开始的核心代码如下:

export default function Cart() 
	const [isCartOpen, setIsCartOpen] = useState(false)
	
	function clickHandler() 
		setIsCartOpen(!isCartOpen)
	

	function closeCartDropdown() 
            if(!document.querySelector(\'#cart\').contains(document.activeElement)) 
                    setIsCartOpen(false)
            
	

	return (
		<div id="cart" className="relative" onBlur=closeCartDropdown>
			<div tabIndex=0 onClick=clickHandler className=\'relative hover:-translate-y-0.5 active:translate-y-0 transition-transform cursor-pointer\'>
				<img className=\'h-14\' src="/src/assets/images/shopping_bag.png"  />
				<span className=\'Z-10 text-2xl font-bold absolute left-1/2 -translate-x-1/2 -translate-y-2/3\' style=top:\'70%\'>4</span>
			</div>
			
				<CartDropdown isCartOpen=isCartOpen/>
			
		</div>
	)

这个版本的代码中在onBlur回调中使用document.activeElement函数想要获取当前聚焦的元素,之后通过判断聚焦的元素是cart组件内的来判断是否需要隐藏cartDropdown,但这里document.activeElement返回的都是body元素。

后面我加入了onFocus函数,并在其中获取document.activeElement却能返回正确的结果,并且是先触发onBlur再触发onFocus函数。这样就可以确定,在之前的元素失去焦点时,onBlur函数被调用,此时没有焦点因此默认给在body上;之后onFocus函数执行,此时新元素获得焦点,因此可以正常获取聚焦结果。

因此在onBlur中想要正确获取聚焦元素,应该在onFocus函数调用后,所以可以使用异步函数来完成这一点。我选取setTimeout来进行异步操作,并且成功在onBlur函数中获取到了正确的document.activeElement值。

function closeCartDropdown() 
	setTimeout(() => 
             if(!document.querySelector(\'#cart\').contains(document.activeElement)) 
		setIsCartOpen(false)
             
        , 0)

备注:传统html中blur事件是不能冒泡的,但react中进行了特殊的处理成功模拟了冒泡,因此可以实现子组件失去焦点,调用父组件回调函数的效果。

react-hook-form:使用 onBlur 模式时验证不起作用

【中文标题】react-hook-form:使用 onBlur 模式时验证不起作用【英文标题】:react-hook-form: Validation not working when using onBlur mode 【发布时间】:2021-06-19 20:29:28 【问题描述】:

当用户选择超过 5 个复选框但未成功时,我尝试使用 yupreact-hook-form 显示错误。

相反,选择了第七个复选框时显示了错误。

这是简化的代码:

imports...

const schema = yup.object().shape(
  option: yup.array().max(5)
);
function App() 
  const  register, handleSubmit, errors  = useForm(
    mode: "onBlur",
    resolver: yupResolver(schema)
  );

  const [state, setState] = useState(
    wasSubmitted: false
  );

  const submit = async (data) => 
    window.alert(JSON.stringify(data));
  ;

  if (state.wasSubmitted) 
    return <p>Congrats</p>;
   else 
    return (
      <>
        <CssBaseline />
        <Container maxWidth="sm">
          <Typography variant="h2" component="h1">
            My form
          </Typography>
          <form noValidate autoComplete="off" onSubmit=handleSubmit(submit)>
            <FormControl
              component="fieldset"
              error=!!errors.option
            >
              <FormLabel component="legend">
                Please select the category or categories of books the child is
                interested in:
              </FormLabel>
              <FormGroup>
                <FormControlLabel
                  control=<Checkbox name="option" inputRef=register />
                  value="Option1"
                  label="Option 1"
                />
                <FormControlLabel
                  control=<Checkbox name="option" inputRef=register />
                  value="Option2"
                  label="Option 2"
                />
                <FormControlLabel
                  control=<Checkbox name="option" inputRef=register />
                  label="Option3"
                  value="Option 3"
                />
                <FormControlLabel
                  control=<Checkbox name="option" inputRef=register />
                  value="Option4"
                  label="Option 4"
                />
                <FormControlLabel
                  control=<Checkbox name="option" inputRef=register />
                  value="Option5"
                  label="Option 5"
                />
                <FormControlLabel
                  control=<Checkbox name="option" inputRef=register />
                  value="Option6"
                  label="Option 6"
                />
                <FormControlLabel
                  control=<Checkbox name="option" inputRef=register />
                  value="Option7"
                  label="Option 7"
                />
                <FormControlLabel
                  control=<Checkbox name="option" inputRef=register />
                  value="Option8"
                  label="Option 8"
                />
                <FormControlLabel
              <FormHelperText>Up to five categories</FormHelperText>
            </FormControl>

            <Button
              type="submit"
              disableElevation
            >
              Submit
            </Button>
          </form>
        </Container>
      </>
    );
  


export default App;

您也可以在这里找到项目的沙箱:

有什么想法吗?

【问题讨论】:

useForm的模式改为onChange:useForm( mode: "onChange", ... 谢谢!解决了这个问题。另外,我刚刚意识到,如果您将useForm 的模式更改为useForm( mode: "all"...,您将同时获得onChanageonBlur 的行为 【参考方案1】:

正如@aadlc 所说,解决方案是将模式设置为onChangeall。我会解释原因。

来自 react-hook-form API docs:

mode: onChange | onBlur | onSubmit | onTouched | all = 'onSubmit'

Name Type Description
onSubmit (Default) string Validation will trigger on the submit event and invalid inputs will attach onChange event listeners to re-validate them.
onBlur string Validation will trigger on the blur event.
onChange string Validation will trigger on the change event with each input, and lead to multiple re-renders. Warning: this often comes with a significant impact on performance.
onTouched string Validation will trigger on the first blur event. After that, it will trigger on every change event.
all string Validation will trigger on the blur and change events.

在您的代码中,表单模式为onBlur。这意味着验证是在blur 事件上触发的(不聚焦输入)。当您选择选项n+1 时,它会从选项n 触发模糊事件。

例如就在您选择第 6 个选项(无效)之前,blur 事件从第 5 个选项(有效)触发,因为您不再关注它,并从选项 1-5 进行验证,因此您必须检查第 7 个选项以重新验证从 1 到 6 的选项。

-- select up to 5 options --
select option 4

blur event fires from option 4 -> validate -> pass
select option 5

blur event fires from option 5 -> validate -> pass
select option 6

blur event fires from option 6 -> validate -> fail
select option 7

将验证模式更改为onChange 将验证 change 事件触发后,所有值都是最新的:

-- select up to 5 options --
select option 4

blur event fires from option 4
select option 5
change event fires from option 5 -> validate -> pass

blur event fires from option 5
select option 6
change event fires from option 6 -> validate -> fail

blur event fires from option 6
select option 7
change event fires from option 7 -> validate -> fail

将验证模式更改为 all 将同时验证 blurchange 事件,这在此工作流程中可能是多余的,但它也有效。

【讨论】:

以上是关于React onBlur回调中使用document.activeElement返回body解决方案的主要内容,如果未能解决你的问题,请参考以下文章

React Native:onBlur 在 onPress 之后触发

React Formik:如何使用自定义 onChange 和 onBlur

react-hook-form:使用 onBlur 模式时验证不起作用

react js中的window.onblur事件没有在android webview中被调用

React - 带有 onBlur 事件的 ul 阻止 onClick 在 li 上触发

onFocus 和 onBlur 不起作用,反应原生