I am using selectionStart
and selectionEnd
in order to get the starting and ending point of a text selection.
Code: https://codesandbox.io/s/busy-gareth-mr04o
However, I am struggling to defined the type of the event on which they can be called on.
If I use any
, the code works properly but I would prefer to know the right event.
I tried with these types:
Element
React.SyntheticEvent<HTMLDivElement>
<HTMLDivElement>
with no luck
export default function App() {
const [startText, setStartText] = useState<number | undefined>();
const [endText, setEndText] = useState<number | undefined>();
const handleOnSelect = (event: any) => { <--- I CANNOT FIND THE RIGHT EVENT TYPE
setStartText(event.target.selectionStart);
setEndText(event.target.selectionEnd);
};
return (
<Grid container direction="column" className="App">
You can type here below:
<TextField
value={"This is a example, select a word from this string"}
onSelect={(event) => handleOnSelect(event)}
/>
<br />
<Grid item>The selected word starts at character: {startText}</Grid>
<Grid item>The selected word ends at character: {endText}</Grid>
</Grid>
);
}
This is a tricky one because the material-ui TextField
component involves multiple nested nodes. The argument that is passed to the onSelect
function is a div
. However the event itself occurs on an input
inside the div
.
const handleOnSelect = (event: React.SyntheticEvent<HTMLDivElement, Event>) => {
console.log(event.target, event.currentTarget);
};
This logs the input
and then the div
.
Using event.currentTarget
gets very specific Typescript information. We know that it is an HTMLDivElement
. But the div
doesn't have the properties selectionStart
and selectionEnd
that we want to access. Those exist on the input
.
event.target
gives us a very vague type for an EventTarget
. We don't know that the target is an input
.
One option is to verify the element at runtime.
const handleOnSelect = (event: React.SyntheticEvent<HTMLDivElement, Event>) => {
if ( event.target instanceof HTMLInputElement ) {
setStartText(event.target.selectionStart);
setEndText(event.target.selectionEnd);
}
};
Since you know that the event will always occur on an HTMLInputElement
, I think it's safe to make an assertion.
const handleOnSelect = (event: React.SyntheticEvent<HTMLDivElement, Event>) => {
const target = event.target as HTMLInputElement;
setStartText(target.selectionStart);
setEndText(target.selectionEnd);
};
Note that selectionStart
and selectionEnd
properties use null
instead of undefined
. So you'll want to either change your state type to <number | null>
or replace null
with undefined
by using null coalescing event.target.selectionStart ?? undefined
.