How to set focus to input after the element stops moving? - reactjs

In React a have a search bar which shows up after I click "Open" button. Search bar appears smoothly because I use translateX property. Then I would like to use focus() on the input element in this search bar. I tried to use refs. Focus appears but it appears as soon as I press the button and stays all the way the search bar moves from one part of the screen to the other(rememeber, it because I use translateX). So it all rides before my eyes. How to make focus appers after the search bar 'reached the destination' on the screen? I tried
if (props.visibleSearchBar) {
ReactDOM.findDOMNode(inputRef.current).focus()
}
instead of
if (props.visibleSearchBar) {
inputRef.current.focus();
} // same story
const Search = (props) => {
let inputRef = useRef(null);
if (props.visibleSearchBar) {
inputRef.current.focus();
}
return (
<div>
<input ref={inputRef} />
</div >
)

You can disable the input initially, and enable it after the transition ends.
something like this, (it is not tested).
const [disabled, setDisabledState] = useState(true)
useEffect(() => {
const callback = () => {
setDisableState(false)
inputRef.current.focus();
}
inputRef.current.addEventListener('transitionend', callback)
return () => inputRef.current.removeEventListener('transitioned', callback)
}, [])
return (
<div>
<input ref={inputRef} disabled={disabled}/>
</div >
)

Related

How to handle onClick event to display text after the click

Using react typescript and I’m confused that when I click a button I want some text to appear below the button or at-least anywhere so I made a function to handle the onClick from the button and returned a h1 from the function but turns out no h1 appears on screen after button click. Any idea why?
const handleOnClick=(id:any)=>{
console.log("button clicked" + id)
return(
<h1>Clicked it</h1>
);
}
My Function is this and in another function I have
<button onClick={()=>{handleOnClick(someId)}}>a</button>
I can see the console log but the h1 doesn’t work. Any ideas?
If you think about it, what your handleOnClick doing is returning a bunch of jsx, where do you think these jsx will appear since we didn't specify any location for them? Now if you try something like this:
<button>{ handleOnClick('someId') }</button>
You will see the h1 on the screen because you specify that's where you want to render it, right inside the button element.
A classic way in js to render out something on button click is like this:
const handleOnClick=(e)=>{
// create the element
const newEle = document.createElement('h1');
newEle.innerText = 'Hello';
// append it inside the button
e.target.appendChild(newEle);
}
export default function App() {
const [nameId, setNameId] = useState<String>("");
const handleClick = (id: String) => () => {
console.log("button clicked: ", id);
setNameId(nameId ? "" : id);
};
return (
<>
<button onClick={handleClick("Rohan")}>
{nameId ? "Hide" : "Greet"}
</button>
{!!nameId && <h1>Hello {nameId} Haldiya</h1>}
</>
);
}
When the click is triggered, you need to add the <h1> element into your JSX code, and returning it from the click handler is not enough because you need to tell it where is should be added.
A good way of doing that in React is by using a state which tells you if the button was clicked or not, and if it was, then you display the <h1> element onto the screen. See the code below:
const [isActive, setIsActive] = useState(false);
const handleOnClick = (id) => {
console.log("button clicked" + id);
setIsActive(true);
};
and in your JSX code, below the button, you just need to add the second line of the following:
<button onClick={()=>{handleOnClick(someId)}}>a</button>
{isActive && <h1>Button was clicked.</h1>}
And if you want to toggle the click, So the first time you click the <h1> is showing , but if you click again it disappears, then you could simply do this in the handleOnClick function instead of the above:
const handleOnClick = (id) => {
console.log("button clicked" + id);
setIsActive((prevState) => (prevState === false ? true : false));
};
Hope this helps!

clickOutside hook triggers on inside select

I have a card component which consists of 2 selects and a button, select1 is always shown and select2 is invisible until you press the button changing the state. I also have an onClickOutside hook that reverts the state and hides select2 when you click outside the card.
The problem Im having is that in the case when select2 is visible, if you use any select and click on an option it registers as a click outside the card and hides select2, how can I fix this?
Heres the relevant code from my card component:
const divRef = useRef() as React.MutableRefObject<HTMLInputElement>;
const [disableSelect2, setDisableSelect2] = useState(true);
const handleActionButtonClick = () => {
setDisableSelect2(!disableSelect2)
}
useOutsideClick(divRef, () => {
if (!disableSelect2) {
setDisableSelect2(!disableSelect2);
}
});
return (
<div ref={divRef}>
<Card>
<Select1>[options]</Select1>
!disableSelect2 ?
<Select2>[options]</Select2>
: null
<div
className="d-c_r_action-button"
onClick={handleActionButtonClick}
>
</Card>
</div>
);
};
And this is my useoutsideClick hook
const useOutsideClick = (ref:React.MutableRefObject<HTMLInputElement>, callback:any) => {
const handleClick = (e:any) => {
if (ref.current && !ref.current.contains(e.target)) {
callback();
}
};
useEffect(() => {
document.addEventListener("click", handleClick);
return () => {
document.removeEventListener("click", handleClick);
};
});
};
Extra informtaion: Im using customized antd components and cant use MaterialUI
I tried to recreate your case from the code you shared. But the version I 'built' works.
Perhaps you can make it fail by adding in other special features from your case and then raise the issue again, or perhaps you could use the working code from there to fix yours?
See the draft of your problem I made at https://codesandbox.io/s/serverless-dust-njw0f?file=/src/Component.tsx

Trap non interactive element's focus

I have an non interactive element like a div on the page that serves as a loading modal. When it is loading, I want to show this modal as well as the rest of the page containing interactive elements with a opacity of 50%. When it is loading(loading modal shows up), I want the focus trapped in this modal and make it so the user cannot tab through the rest of the elements on the page. I've tried the code below by adding the ReactFocusLock around the div and give the div a tabIndex of -1 so it is focusable and set it to be focusable at the beginning but it does not work. However, if I put a link or something interactable inside the div, it works well, which means the ReactFocusLock is fine. Could you please help?
const MyComponent = () => {
const {loading, data} = fetchData();
return <>
{loading && <LoadingModal />}
{data && <div>data</div>}
<input></input>
<button></button>
<>;
}
// original modal
const LoadingModal = () => {
return <div>loading data....<div>;
}
// modal that with ReactFocusLock from react-focus-lock
const LoadingModal = () => {
const ref = React.useRef(null);
React.useEffect(() => {
ref.current.focus();
}, []);
return <ReactFocusLock><div ref={ref} tabIndex={-1}>loading data....</div></ReactFocusLock>;
}

input onClick and onFocus collision

I have an <input> element, and I want to show content (div) below the input if it has focus.
Also, on every click on the input, I want to hide/show the div.
The problem is that if the input is not focused, than clicking the input will trigger both onClick and onFocus events, so onFocusHandler will run first so the menu will appear but immidately after that onClickHandler will run and hide it.
This is the code (that doesn't work):
import React from 'react';
const MyComp = props => {
/*
...
state: focused, showContent (booleans)
...
*/
const onFocusHandler = () => {
showContent(true);
setFocused(true);
}
const onClickHandler = () => {
if (focused) {
showContent(false);
} else {
showContent(true);
}
}
return (
<>
<input
onFocus={onFocusHandler}
onClick={onClickHandler}
/>
{
showContent &&
<div>MyContent</div>
}
</>
);
};
export default MyComp;
How can I solve this issue?
This is a very odd desired behavior as an active toggle is rather opposed to an element being focused or not. At first I couldn't see any clear way to achieve the behavior you desire. But I thought it could be achieved.
Use the onMouseDown handler instead of the onClick handler.
Use onFocus handler to toggle on the extra content.
Use onBlur handler to toggle off the extra content.
Code:
const MyComp = (props) => {
const [showContent, setShowContent] = useState(false);
const onFocusHandler = () => {
setShowContent(true);
};
const onBlurHandler = () => {
setShowContent(false);
};
const onClickHandler = () => {
setShowContent((show) => !show);
};
return (
<>
<input
onBlur={onBlurHandler}
onFocus={onFocusHandler}
onMouseDown={onClickHandler}
/>
{showContent && <div>MyContent</div>}
</>
);
};
Note: It should be noted that onMouseDown isn't a true replacement to onClick. With a click event the mouse down and up have to occur in the same element, so if. user accidentally clicks into the input but then drags out and releases, the onClick event should not fire. This may be ok though since the focus event fires before the click event so the input may get focus and toggle the content on anyway. Maybe this quirk is acceptable for your use-case.
You should you onBlur event to setContent to false. There is no need to use the onClick handler for that.

onClick event not called when clicking

React onClick event not working when clicking on glyphicon.
const SortableItem = SortableElement(({value, parentId}) =>
<ListGroupItem bsStyle='success' style={{width:'300px',textAlign:'left'}}>
{value}
{parentId === null && <span className='glyphicon glyphicon-remove' style={{float:'right',width:'10px',height:'10px'}}
onClick={e => {e.preventDefault(); console.log('yes')}}/>}
</ListGroupItem>
);
I ran into something similar. My onClick events on my <a> elements were not getting triggered when a user clicked them.
This is what I was doing wrong and maybe you are doing the same mistake as what I was doing. Without more context, it's impossible to diagnose what your actual problem is.
(code is basically saying, when I click the background, stop propagation of the click)
// Example of what I was doing WRONG
const Dialog = ({ onClose, children }) => {
const $overlay = React.useRef(null)
// on mount
React.useEffect(() => {
const handleBackgroundClick = (e) => {
e.stopPropagation() // <<-- This was the line causing the issue
onClose()
})
$overlay.current?.addEventListener('click', handleBackgroundClick)
// on unmount
return () => {
$overlay.current?.removeEventListener('click', handleBackgroundClick)
}
}, [])
return (
<div className="Dialog" ref={$overlay}>{children}</div>
)
}
Basically what I'm saying is this:
Do not use event.stopPropagation() directly on DOM elements in React
The events need to be able to bubble all the way to the top level or in-built React event's will stop working.
I ended up getting around the issue by adding an extra div inside the dialog to act as the overlay.
// How I fixed the issue
const Dialog = ({ onClose, children }) => {
return (
<div className="Dialog">
<div className="Dialog__overlay" onClick={()=> onClose()}></div>
<div className="Dialog__content">{children}</div>
</div>
)
}
Note: These are simplified examples to demonstrate a point. The exact code used was more complex. Using this code as displayed here would cause accessibility issues on your website.

Resources