How to pass out focus of input when clicked out? - javascript

I'm using next js for my application, and I am designing a search field. The suggestions start coming in once the user types something, but I would like to hide it when the input bar is not in focus.
When the user clicks on it again, I would like to show it back. The search suggestions contain routes, so I am not able use onFocus and onBlur as the element loses focus when I register a click and the route happens only when I release it.
I tried css too, but I'm not able to register the focus out, or is there a way?
Please help me out!!
Here is my sample code:
const [suggestionState,setSuggestionState] = useState(false);
<input type="input"
ref={inputRef}
autoFocus
className={styles['search-bar-input']}
onFocus={()=>{setSuggestionState(true)}}
onBlur={()=>{setSuggestionState(false)}}
placeholder="Search Bloggo"
onChange={(e)=>{
var trimmedQuery = e.target.value;
trimmedQuery = trimmedQuery.trim();
setSearchQuery(trimmedQuery);
getSuggestions(trimmedQuery)
}}
onKeyDown={(e)=>{handleKeypress(e)}}
/>
{
searchQuery.length == 0 || suggestionState == false? '':
<div className={styles['search-bar-suggestions']}>
<Link>... </Link>
</div>
}

You could do this with css :focus-within
.suggestions {
display: none;
}
form:focus-within .suggestions {
display: block;
}
input:focus~.suggestions {
display: block;
}
<form>
<input type="input" placeholder="Search Bloggo" value="">
<div class="suggestions">Suggestions...
<div>Suggestion 1</div>
<div>Suggestion 2</div>
<div>Suggestion 3</div>
<div>Suggestion 4</div>
</div>
</form>
Applying the above in react might looks something like this:
import "./styles.css";
import { useState, useEffect } from "react";
export default function App() {
const [searchQuery, setSearchQuery] = useState("");
const [results, setResults] = useState([]);
useEffect(() => {
if (!searchQuery) {
setResults([]);
return;
}
fetch(`https://rickandmortyapi.com/api/character/?name=${searchQuery}`)
.then((response) => response.json())
.then(({ results }) => setResults(results));
}, [searchQuery]);
return (
<form>
<input
value={searchQuery}
type="input"
autoFocus
placeholder="Search Bloggo"
onChange={(e) => {
setSearchQuery(e.target.value);
}}
/>
{!!results.length && (
<div className={`suggestions `}>
<h3>Suggestions</h3>
{results.map((result) => {
return (
<Link key={result.id} url={result.url}>
{result.name}
</Link>
);
})}
</div>
)}
</form>
);
}
const Link = ({ url, children }) => (
<div>
<a href={url}>{children}</a>
</div>
);

Related

radio button input controlling in react js

I have a form which has both boolean and string data to be shown in. I was able to control text fields with the help of state, but boolean fields are controlled with the help of enable and disable radio button. Was not able to control the boolean fields as there are many to be done. Is there any way that I can get the inputs for the fields?
import React, { useState } from 'react';
import moment from 'moment';
export const ConfigurationPage = (props) => {
const { t } = useTranslation();
const [configResp, setConfigResp] = useState({});
const [configSet,setConfigSet]=useState(null);
const [checked,isChecked]=useState(false);
React.useEffect(() => {
fetchConfig();
}, []);
React.useEffect(() => {
setInputConfig();
}, [configResp]);
const setInputConfig=()=>{
setConfigSet(configResp);
}
const fetchConfig = async() => {
try {
const resp = await APIEndpoint.get(`customer/app/config`);
if(resp.success){
const {result}=resp;
setConfigResp(result);
}
}
catch (resp) {
console.log(resp.msg)
}
}
const onChange = (e) => {
setConfigSet({[e.target.name]: e.target.value})
}
const getDate=()=>{
return moment().subtract(1, 'days').format('YYYY-MM-DD')+"T18:30:00.000Z"
}
return (
<div className="overlay" role="dialog">
<div className="dialog sm-scrollbar">
<div className="dialog__content">
<h2 className="dialog__title subheading-text-medium fontMedium">{preCompile.title}</h2>
<hr />
<div class="dialog__body_container flexRow justifyContentSpaceBetween flexWrap">
<div class="flexCol justifyContentSpaceEvenly ">
<div class=" dialog__container_item input-container">
<div class="dialog__description">
<label class="req_conf">Account Alias</label>
<input
id={''}
name="accountAlias"
onChange={(e)=>onChange(e)}
value={configSet?configSet.accountAlias:""}
type={'text'}
className='edit-input'
/>
</div>
</div>
</div>
<div class="flexCol justifyContentSpaceEvenly ">
<div class=" dialog__container_item input-container">
<div class="dialog__description">
<label class="req_conf">mSwipe</label>
<>
<div className={`dialog__description__radio`}>
<label>Enable</label>
{configSet.mswipeEnabled?<input id="" type="radio"
name="mswipeEnabled"
value={configSet.mswipeEnabled}
checked={configSet?.mswipeEnabled}
onChange={(e)=>onChange(e)} />:<input id="" type="radio"
name="mswipeEnabled"
value={!configSet.mswipeEnabled}
checked={!configSet?.mswipeEnabled}
onChange={(e)=>onChange(e)} />}
<label>Disable</label>
<input id="" type="radio"
name="mswipeEnabled"
value={configSet?configSet.mswipeEnabled:checked}
checked={configSet?.mswipeEnabled}
onChange={(e)=>onChange(e)} />
</div>
</>
</div>
</div>
</div>
</div>
<div className="dialog__footer flexCol alignItemsCenter">
<button className="done-button" onClick={props.onSubmit}>{props.title === 'Edit Item' ? 'Update' : 'Save'}</button>
</div>
</div>
</div>
);
}
Here I am trying to change values of mswipe fields which is a boolean with enable and disabled. Is there any way to do this?

How to prevent onBlur for a sibling or related component events?

I am implementing a complex autocomplete functionality, and the point is that when the input gets focused, I should show the result box, and when the user goes to the next input or clicks anywhere on the screen, or presses Escape, then I should close the result box.
To close the result box, I'm using the onBlur event.
The problem is that inside the result box I have a button that takes the user to a more complete search dialog, but when I click it, the onBlur event is fired and prevents the click of this button to happen.
Here is my code:
import { useState } from "react";
export default function IndexPage() {
const [isOpen, setIsOpen] = useState(false);
const [text, setText] = useState("");
return (
<div>
<input
onBlur={() => setIsOpen(false)}
onFocus={() => setIsOpen(true)}
/>
{isOpen && (
<div>
something
<br />
<button
onClick={() => setText("i am clicked")}>click me</button>
</div>
)}
<br />
<input />
<br />
<p>{text}</p>
</div>
);
}
How can I make it work properly?
You can see a live example in this codesandbox
I'm using Next.js
Firstly, you need to group input and button to have a wider focusing area. I'm using div for that purpose, but onBlur and onFocus are not applied for a usual div, so we need to have tabIndex="1" which is to make that element interactive with those events.
<div
tabIndex="1"
onBlur={() => setIsOpen(false)}
onFocus={() => setIsOpen(true)}
>
</div>
Secondly, we should use onMouseDown event instead of onClick, which is to avoid focusing state on the button element.
<button onMouseDown={() => setText("i am clicked")}>
click me
</button>
The full implementation can be (https://codesandbox.io/s/strange-violet-z0u3jr)
import { useState } from "react";
export default function IndexPage() {
const [isOpen, setIsOpen] = useState(false);
const [text, setText] = useState("");
return (
<div>
<div
tabIndex="1"
onBlur={() => setIsOpen(false)}
onFocus={() => setIsOpen(true)}
>
<input />
{isOpen && (
<div>
something
<br />
<button onMouseDown={() => setText("i am clicked")}>
click me
</button>
</div>
)}
</div>
<br />
<input />
<br />
<p>{text}</p>
</div>
);
}

React multiple checkbox with one check at a time

Hello I have question about how I can make user select one checkbox at a time. So for example, if there is three checkbox(none are selected when page loads) on react component and user select one checkbox, the other checkbox will not be checked unless user uncheck it. I am trying to use useref to make it to work... but seems like it is not working..
const refCheckBoxOne = useRef(null);
const refCheckBoxTwo = useRef(null);
const refCheckBoxThree = useRef(null);
const onchangefunction = (propertyname, value) => {
if(refcheckBoxOne.current.check){
refcheckBoxOne.current.check = false;
refcheckBoxOne.current.check = false;
}
}
<input id="one" ref={refCheckBoxOne} userefonchange={(e) => onchangefunction("checkboxOne",e.target.value) }/>
<input id="two" ref={refCheckBoxTwo} onchange={(e) => onchangefunction("checkboxTwo",e.target.value) }/>
<input id="three" ref={refCheckBoxThree} onchange={(e) => onchangefunction("checkboxThree",e.target.value) }/>
I have tried many ways to do it... but cant get it to work. I would be really appreciated if anyone can give me an idea on how to approach this kind of issue.
Thank you
If two items you can use this
const App = () => {
const [checked, setChecked] = React.useState(false);
const handleChange = () => {
setChecked(!checked);
};
return (
<div>
<label>
<input
type="checkbox"
checked={checked}
onChange={handleChange}
/>
</label>
</div>
);
};
If multiple value then use it
export default function App() {
const [checkedState, setCheckedState] = useState(
[false,false,false]
);
const handleOnChange = (position) => {
const updatedCheckedState = checkedState.map((item, index) =>
index === position ? !item : item
);
setCheckedState(updatedCheckedState);
};
return (
<div className="App">
<h3>Select Toppings</h3>
<ul className="toppings-list">
<li key=0>
<div className="toppings-list-item">
<div className="left-section">
<input
type="checkbox"
id=`custom-checkbox-1`
checked={checkedState[0]}
onChange={() => handleOnChange(0)}
/>
</div>
</div>
</li>
<li key=1>
<div className="toppings-list-item">
<div className="left-section">
<input
type="checkbox"
id=`custom-checkbox-1`
checked={checkedState[1]}
onChange={() => handleOnChange(1)}
/>
</div>
</div>
</li>
<li key=2>
<div className="toppings-list-item">
<div className="left-section">
<input
type="checkbox"
id=`custom-checkbox-2`
checked={checkedState[2]}
onChange={() => handleOnChange(2)}
/>
</div>
</div>
</li>
</ul>
</div>
If you have any question , Comment below here .

How do I make a button trigger a file input onChange in React?

I have an input of type file. For some reason it doesn't let me change the value attribute to it, and it looks ugly. I swapped it with a button, but now I need the button to somehow trigger the input file on click. How can I do this in React?
Edit:
It should trigger the input onClick, not the onChange like the title says. Unless it's the same thing in this case.
const fireInput = () => {
let input = document.getElementById('inputFile');
}
<div>
<input
id="inputFile"
type='file'
className='mt-2 mb-3 text-primary'
onChange={uploadProfilePic}
/>
<button
type='button'
className='btn btn-primary py-2 px-5 mb-3'
onClick={fireInput}
>
Upload Picture
</button>
</div>
You shouldn't put display:none then the element will not be in dom, you need to use opacity:0 or visibility css.
Code for doing above can be done like this:
import "./styles.css";
import { useRef } from "react";
export default function App() {
const fileUpload = useRef(null);
const uploadProfilePic = (e) => {
console.log(e);
};
const handleUpload = () => {
console.log(fileUpload.current.click(), "fileUpload");
};
return (
<div className="App">
<input
type="file"
ref={fileUpload}
onChange={uploadProfilePic}
style={{ opacity: "0" }}
/>
<button onClick={() => handleUpload()}>Upload Picture</button>
</div>
);
}
You can simply use a label:
.d-none {
display: none;
}
.button {
background-color: #123456;
color: white;
padding: 15px 32px;
text-align: center;
}
<label for="inputFile" class="button">Upload</label>
<input type="file" id="inputFile" name="inputFile" class="d-none">
You could use the html label element without using any JS:
const Component = () => {
// You should also use a ref instead of document.getElementById
const inputRef = useRef()
return (
<label>
<div> {/*Style this however you want*/}
Upload Photo
</div>
<input type="file" style={{display: "none"}} ref={inputRef} />
</label>
)
}
I think you can do like this.
<div>
<input type="file" onChange={uploadProfilePic} ref={(ref) => this.upload = ref} style={{ display: 'none' }}/>
<button
type='button'
className='btn btn-primary py-2 px-5 mb-3'
onClick={(e) => this.upload.click()}
>
Upload Picture
</button>
</div>
You can confirm here.
https://codesandbox.io/s/youthful-cdn-lpntx

using button to increment divs in react

I am fairly new to React/Next and I had a quick question.
I am trying to create a button that will increment the number of divs in real time.
Here is my code:
import React from 'react'
const Clown = () => {
const [clownCounter, setClownCounter] = React.useState(1);
function addClown(event) {
event.preventDefault();
}
return(
<React.Fragment>
<div>
<form>
{Array.from({ length: clownCounter}, (_unused, index) => index + 1).map(
(clownIndex) => {
const clownid = `${clownIndex}`
return (
<div key={clownid } className="clown-box">
<label htmlFor={clownid }>Activity {clownIndex}</label>
<br />
<input type="text" onChange={(e)=> onChangeForm(e)} name={activityId} id={activityId} />
<br />
</div>
)
},
)}
<span className="clown-add">
<button onClick={addClown} onChange={() => { setClownCounter(clownCounter++) }}>Add Clown</button>
</span>
<br />
</form>
</div>
</React.Fragment>
)
}
export default Clown
As you can see the goal is to increase the amount of clown-box divs everytime the button is clicked. I think I am close but it is not currently working. Can anyone help?
There are few small this wrong with your code.
First, you have an extra comma(,) after the return statement in map function
Second, you are updating state clownCounter on onChange event in button, which is incorrect. You should update it on click and also prevent the default behaviour of form submit on click of button or you can define the button type to be type="button"
Lastly, you need to define your onChangeForm function
const Clown = () => {
const [clownCounter, setClownCounter] = React.useState(1);
function onChangeForm() {
}
function addClown(event) {
event.preventDefault();
setClownCounter(prev=> prev+1);
}
console.log(clownCounter);
return(
<div>
<form>
{Array.from({ length: clownCounter}, (_unused, index) => index + 1).map(
(clownIndex) => {
const clownid = `${clownIndex}`;
return (
<div key={clownid } className="clown-box">
<label htmlFor={clownid }>Activity {clownIndex}</label>
<br />
<input type="text" onChange={(e)=> onChangeForm(e)} name={'activityId'} id={'activityId'} />
<br />
</div>
)
})
}
<span className="clown-add">
<button type="button" onClick={addClown}>Add Clown</button>
</span>
<br />
</form>
</div>
)
}
ReactDOM.render(<Clown />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="app" />
Edit: Thought issue was caused by Array.from, but, it's not. I've removed that part, but kept the example since OP might find it useful
const { useState } = React;
const Clowns = ({ title }) => {
const [clownCounter, setClownCounter] = React.useState(1);
return (
<div>
<button onClick={() => setClownCounter(clownCounter + 1)}>
Add clown
</button>
<div className='clowns'>
{Array.from({ length: clownCounter}, (_unused, index) => index + 1).map((e, i) => (
<div>
<h4>{`Clown #${i + 1}`}</h4>
<img src={`https://placehold.it/150x150&text=Clown%20%23${i + 1}`} />
</div>
))}
</div>
</div>
);
};
ReactDOM.render(<Clowns />, document.getElementById("react") );
.clowns { display: flex; flex-direction: column; }
h4 { margin-bottom: 5px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Categories

Resources