next.js 当用户离开页面时,有没有办法防止输入模糊?

mbjcgjjk  于 2023-05-22  发布在  其他
关注(0)|答案(1)|浏览(210)

我正在做一个next.js 13项目,我想创建交互式身份验证表单。我创建了一个窗体,当聚焦时,它显示输入,并在其上方显示标签。模糊标签后,标签将恢复原样,就像占位符一样。这就是它的样子。
这是它聚焦时的样子:

这是模糊的文字:

这是模糊的,没有文本(初始加载):

最后,当它被聚焦然后模糊但没有添加文本时,它看起来是什么样子:

app/login/page.jsx

'use client'
import { useState } from 'react'
import Link from 'next/link'
import Input from '../components/Input'
import '../styles/auth.css'

export default function LoginPage() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [show, setShow] = useState(false)
  const [error, setError] = useState('')

  async function login(e) {
    e.preventDefault()
    if (!email || !password) {
      setError('All fields are required.')
      return
    }
    console.log('send')
  }

  return (
    <div className='auth-container'>
      <form className='login'>
        <h2>Sign In</h2>
        <Input name='Email' type='email' value={email} setValue={setEmail} setMsg={setError} />
        <Input name='Password' type='password' value={password} setValue={setPassword} show={show} setShow={setShow} setMsg={setError} />
        <Link href='/forgot-password' className='forgot'>Forgot Password</Link>
        <button onClick={login}>Sign In</button>
        <p className='account'>Don't have an account yet, <Link href='/signup'>Sign Up</Link>.</p>
        <p className='error-msg'>{error}</p>
      </form>
    </div>
  )
}

components/Input.jsx

'use client'
import { useRef } from 'react'
import { FaEye, FaEyeSlash } from 'react-icons/fa'
import '../styles/auth.css'

export default function Input({ type, name, value, setValue, show, setShow, setMsg }) {
  const labelRef = useRef()
  const parentRef = useRef()
  const inputRef = useRef()
  
  const changeVisibility = () => setShow(!show)

  const handleFocus = () => {
    setMsg('')
    labelRef.current.style.transform = labelRef.current.style.animation = null
    parentRef.current.classList.remove('red-border')
    parentRef.current.classList.add('blue-border')
    labelRef.current.classList.add('entered', 'blue-text')
  }

  const handleBlur = () => {
    parentRef.current.classList.remove('blue-border')
    labelRef.current.classList.remove('blue-text')

    if (value.length === 0) {
      labelRef.current.style.transform = 'translate(2px, -20px)'
      labelRef.current.classList.remove('entered')
      parentRef.current.classList.add('red-border')   
      labelRef.current.style.animation = 'move-back 0.1s linear both'
    }
  }

  return (
    <div className="input-container" ref={parentRef}>
      <label ref={labelRef}>{name}</label>
      <input type={type === 'password'? show? 'text': type : type} value={value} ref={inputRef}
        onChange={e => setValue(e.target.value)} 
        onFocus={handleFocus}
        onBlur={handleBlur}
      />
      {
        type === 'password'?
          !show? 
            <FaEye className='icon' size={22} onClick={changeVisibility} /> 
          : 
            <FaEyeSlash className='icon' size={24} onClick={changeVisibility} />
        : 
          <></>
      }
    </div>
  )
}

styles/auth.css

.auth-container {
  width: 100%;
  min-height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

form {
  width: 300px;
  background-color: #fefefe;
  border: solid 1px #cdcdcd;
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  gap: 25px;
  padding: 20px 20px;
}

.login {
  min-height: 365px;
}

.signup {
  min-height: 455px;
}

form h2 {
  align-self: center;
  font-weight: 600;
}

form button {
  width: fit-content;
  border: 0;
  border-radius: 3px;
  background-color: #1A73E8;
  color: #fff;
  font-size: 16px;  
  cursor: pointer;
  padding: 10px 20px;
  align-self: flex-end;
}

form button:hover {
  background-color: #075cca;
}

.input-container {
  display: flex;
  align-items: center;
  position: relative;
  outline: 1px solid #ccc;
}

.input-container input {
  flex: 1;
  position: relative;
  background-color: #ffffff00;
  outline: none;
  border: 0;
  color: #808080;
  padding: 12px 5px;
}

.input-container label {
  position: absolute;
  top: 12px;
  left: 5px;
  color: #808080;
  user-select: none;
}

.input-container label.entered {
  font-size: 13px;
  font-weight: 500;
  background-color: #fff;
  padding: 0 7px;
  animation: move 0.1s linear both;
  z-index: 3;
}

.input-container label.blue-text {
  color: #1A73E8;
}

.input-container.blue-border {
  outline: 2px solid #1A73E8;
}

.input-container.red-border {
  outline: 1px solid #8c0c0c;
}

@keyframes move {
  100% {
    transform: translate(2px, -20px);
  }
}

@keyframes move-back {
  100% {
    transform: translate(0, 0);
  }
}

.icon {
  color: #808080;
  padding: 0 7px;
  cursor: pointer;
}

.account a {
  color: #1A73E8;
  text-decoration: none;
}

.forgot {
  color: #000;
  text-decoration: none;
}

.error-msg {
  font-size: 14px;
  color: #8c0c0c;
  margin-top: -10px;
}

我的问题是标签的动画。它应该从看起来像一个占位符文本到向上翻译几个像素。如果我当前专注于一个输入,然后转到另一个页面,它会模糊输入。当我返回到我的表单时,我发现动画会重播,因为输入已经模糊,然后再次聚焦。我想防止动画重播。我该怎么做?

ddarikpa

ddarikpa1#

您可以通过添加document.hasFocus()方法来检查用户是否在您的页面上来避免这种行为。

const handleBlur = () => {
    if (!document.hasFocus()) return
    
    parentRef.current.classList.remove('blue-border')
    labelRef.current.classList.remove('blue-text')

    if (value.length === 0) {
      labelRef.current.style.transform = 'translate(2px, -20px)'
      labelRef.current.classList.remove('entered')
      parentRef.current.classList.add('red-border')   
      labelRef.current.style.animation = 'move-back 0.1s linear both'
    }
  }

相关问题