Например, если я введу 123456789 и оставлю фокус, ввод изменится на *****6789.
У меня есть рабочий пример, но я не уверен, что в нем нет потенциальных ошибок. Есть ли способ лучше?
Я пришел к этому методом проб и ошибок
import { useState } from "react";
import "./styles.css";
export default function App() {
const [hovered, setHovered] = useState(false);
const [testInput, setTestInput] = useState("");
const showLastFourDigits = (value) => {
if (value.length == 9) {
const result = "*".repeat(value.length - 4) + value.slice(-4);
return result;
} else {
return value;
}
};
const onSubmit = (e) => {
e.preventDefault();
console.info(testInput);
};
return (
<form className = "App">
<input
value = {!hovered ? showLastFourDigits(testInput) : testInput}
onChange = {(e) => setTestInput(e.target.value)}
onFocus = {() => setHovered(true)}
onBlur = {() => setHovered(false)}
maxLength = {9}
/>
<button onClick = {onSubmit}>Submit</button>
<p>testInput:{testInput}</p>
</form>
);
}
Как вы могли видеть. Если я наведу на него курсор, он покажет полный ввод. Если я открою его, он станет *****6789. По причине, я не уверен, почему, но onChange не запустится, если я сделаю это таким образом. Это безопасно? Меня беспокоит, что если я нажму «Отправить», вместо 123456789 появится *****6789. Это рекомендуемый способ сделать это?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Похоже, вы хотите замаскировать и снять маску в ответ на два критерия: элемент фокус и наведение состояние… и кажется, что вы уже поняли логику focus и blur. Большой!
В JS нет события «hover», но его близкое приближение можно реализовать с помощью событий mouseenter и mouseleave. Логика для mouseenter и mouseleave аналогична focus и blur соответственно, за исключением того, что они должны работать только в том случае, если элемент не сфокусирован.
Имеет ли элемент фокус, можно определить в JS, сравнив его с document.activeElement. Вот минимальный воспроизводимый пример:
<style>body { font-family: sans-serif; }</style>
<div id = "root"></div><script src = "https://cdn.jsdelivr.net/npm/[email protected]/umd/react.development.js"></script><script src = "https://cdn.jsdelivr.net/npm/[email protected]/umd/react-dom.development.js"></script><script src = "https://cdn.jsdelivr.net/npm/@babel/[email protected]/babel.min.js"></script>
<script type = "text/babel" data-type = "module" data-presets = "env,react">
// This Stack Overflow snippet demo uses UMD modules
const { StrictMode, useState } = React;
function maskAllExceptFinal(str, options = {}) {
const { maskToken = "*", revealCount = 4 } = options;
return str.length <= revealCount
? str
: `${maskToken.repeat(str.length - revealCount)}${str.slice(-4)}`;
}
function App() {
const [masked, setMasked] = useState(true);
const [testInput, setTestInput] = useState("");
const handleSubmit = (ev) => {
ev.preventDefault();
console.info(testInput);
};
const handleMask = (ev) => {
const isFocused = document.activeElement === ev.currentTarget;
switch (ev.type) {
case "blur": {
setMasked(true);
break;
}
case "focus": {
setMasked(false);
break;
}
case "mouseenter": {
if (!isFocused) setMasked(false);
break;
}
case "mouseleave": {
if (!isFocused) setMasked(true);
break;
}
}
};
return (
<form>
<input
onBlur = {handleMask}
onChange = {(ev) => setTestInput(ev.target.value)}
onFocus = {handleMask}
onMouseEnter = {handleMask}
onMouseLeave = {handleMask}
maxLength = {9}
value = {masked ? maskAllExceptFinal(testInput) : testInput}
/>
<button onClick = {handleSubmit}>Submit</button>
<p>testInput: {testInput}</p>
</form>
);
}
ReactDOM.createRoot(document.getElementById("root")).render(
<StrictMode>
<App />
</StrictMode>,
);
</script>Это нормально, если вы не используете <form onSubmit= {function}> для обработки ввода: в этом случае маскированное значение (*****6789) будет передано в качестве входного значения.
Честно говоря, я бы не рекомендовал возиться с реальным значением только для достижения чисто визуального эффекта. Я делал это раньше и столкнулся с несколькими проблемами, которые поначалу не были очевидны. Некоторые вещи я вспоминаю:
И есть замечательная альтернатива без заморочек: Выполните переключение исключительно с помощью CSS. Поместите в DOM два разных элемента:
pointer-events позволяет точно контролировать, какой элемент получает события наведения.Вот пример, просто чтобы проиллюстрировать идею:
const maskedValue = maskAllExceptFinal(value)
<div className = "container">
<input className = "actual-value" value = {value} onChange = {...} />
<input className = "masked-display" value = {maskedValue} readonly aria-hidden = "true" />
</div>
.container {
position: relative;
}
.container:hover {
& .masked-display {
display: none;
}
}
.actual-value {
// ...
}
.masked-display {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}