Full reproduction: codesandbox link .
I am trying to create a custom search/select component with react. My problem lies in execution order of events.
When a user focuses in on the text input field, a list with options should be shown. Then when the user clicks on one of the items in the dropdown, the element name should be filled in the text input field and then the list should be hidden.
I achive this by creating a inputFocused boolean state, and rendering list only when inputFocused is true. I attach twop event handlers on the text input itself:
onFocus={() => setInputFocused(true)}
onBlur={() => setInputFocused(false)}
But when a user clicks on one of the items, input is not getting filled.
<input
value={searchTerm}
onChange={(e) => {
setSearchTerm(e.target.value);
}}
onBlur={() => setInputFocused(false)}
onFocus={() => setInputFocused(true)}
/>
{inputFocused && (
<ul>
<li onClick={() => setSearchTerm(element.name)}>{element.name}</li>
</ul>
}
Check codesanbox link for better understanding.
I think i understand the problem. onBlur() event triggers before the onClick() event. If i wrap the onBlur callback function inside a setTimout, it will work as expected:
onBlur={() =>
setTimeout(() => {
setInputFocused(false);
}, 100)
}
I would like to avoid defining a setTimeout.
Is there solution to make sure that onClick() on <li> executes before onBlur() on <input /> ?
You can change onClick to onMouseDown on the list items, and the ordering will be handled properly by the browser.
The events get fired in the order of mousedown -> mouseup -> click (see
https://javascript.info/mouse-events-basics#events-order)
Related
I am using Antd 4.x library. I have a Collapse and a popover that shows on Click of button on Collapse which has a Checkbox in it. I would like to Stop Propagation when checking/unchecking the checkbox, i.e. the Collapse opens/closes when checking/unchecking
Although when I click on Label of Checkbox the Collapse doesn't open/close but when the I do it on the checkable part of checkbox it happens.
I would like to Stop the open/close of Collapse when checking/unchecking of checkbox on popover
Demo Link to show the issue
TIA
You can wrapped your Panel extra content in a div and add onClick to stopPropagation. Also, you do not have to handle stopPropagation in Setting Button & Checkbox.
const onChange1 = (e: { target: { checked } }) => {
console.log(`checked = ${e.target.checked}`);
};
const genExtra = () => (
<div onClick={(e) => e.stopPropagation()}>
<Popover content={<Checkbox onChange={onChange1}>Checkbox</Checkbox>} title='Title'>
<SettingOutlined />
</Popover>
</div>
);
First of all, you have to understand that when an event occurs, the event always has an event source, that is, the object that caused the event. An event cannot be generated out of thin air. This is the occurrence of an event.
When the event occurs, the event will start to propagate. In your case, when we click the checkbox, a click event will be generated, but the checkbox cannot handle this event. The event must be propagated from the checkbox to reach the code that can handle the event. (for example, we assign a function name stopPropagation to the Fragment's onClick property, which is to let "stopPropagation" handle the Chekbox's click event).
import React, { Fragment } from "react";
function stopPropagation(e) {
//The stopPropagation() method of the Event interface prevents further propagation of the current event in the capturing and bubbling phases.
if (e && e.stopPropagation) {
e.stopPropagation();
} else {
//If it is IE browser
window.event.cancelBubble = true;
}
}
const genExtra = () => (
<Fragment onClick={(e) => stopPropagation(e)}>
<Popover
content={<Checkbox onChange={onChange1}>Checkbox</Checkbox>}
title="Title"
>
{/* <Button type="primary" shape="circle" icon={<SearchOutlined />} /> */}
<SettingOutlined
onClick={(event) => {
event.stopPropagation();
}}
/>
</Popover>
</Fragment>
);
For your reference: https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation
In order to benefit the design of reactstrap dropdown, I want to use it as a search bar with results shown in the dropdown menu. But the default key listener that enables navigating through results by keyboard (arrow keys Up/Down), only captured by Input, and cannot propagate it to the parent or whatever is listening to key events when the result is visible.
<Dropdown toggle={() => setIsOpen(!isOpen)} isOpen={isOpen}>
<DropdownToggle>
<Input onChange={(e) => setQuery(e.target.value)} placeholder="search placeholder" />
</DropdownToggle>
<DropdownMenu>
{fetchedItems.map((item) => (
<div key={item}>
<DropdownItem>action #1</DropdownItem>
<DropdownItem>action #1</DropdownItem>
<DropdownItem text>Dropdown Item Text</DropdownItem>
<DropdownItem disabled>Action (disabled)</DropdownItem>
<DropdownItem divider />
</div>
))}
</DropdownMenu>
</Dropdown>
Now looking at this sandbox I am searching for two approaches, either add key event listeners to default as default dropdown behavior, or customize the <Input type=" search"/>
Now the question is how to do it. I assume handling key listener might be better.
One way to do this would be by adding an eventListener with the dropdown component.
You would also want to maintain a state which tracks the currently navigated dropdown item's index.
The eventListener can increment and decrement the selected index
If nothing is selected, on up keystroke set 0 and on down keystroke set length - 1 as the current index
Now increment or decrement on different keystrokes
If something new is searched, change it to undefined or 0 at your convenience
Give custom styling to the currently navigated index
Please follow following thing:
1)The above "event listner" should be part of parent of list.
2)Set value of "current-index" to 0 once you get new set of data.
3) When user move the selection with the arrow button "incsease" and "decrease" once as per "key-value".
4)If use jump from one point to other get the "id" or "serial number".
5) Ideal event listener should be "onfoucs" if using on the children.
The whole problem was from a type of event listener, so the DropDownToggle component receives the onKeyDown events not the onKeyPress
<DropdownToggle onKeyDown={(e) => {/* now have access to e.key */} }
I have made an App that is simple you can check it out here but the problem is now that when a user is logged in and creating a post there is a button that adds a post now I want "Enter" key to perform the same task the code to my button is
<Button variant="contained" color="secondary" onClick = {updateList}>Add item</Button>
now I want to bind the same updateList function to Enter key please guide me on how can I do that???
The button is from Material-ui
also currently as I click enter key it reloads the page I also want to stop that from happening
Also, can anyone tell me that is it possible to take multikey stroke input like ctrl + Z ?
If you want to bind function to some key you can do it by event listeners called onKeyDown or onKeyPress:
handleKeyPress = (event) => {
if(event.key === 'Enter'){
console.log('enter press here! ')
}
}
function App(){
return(
<div>
<input type="text" id="one" onKeyPress={this.handleKeyPress} />
</div>
);
}
Because you pager is reloading im guessing your using the HTML form tag. To prevent it the page from reloading your need to prevent the default event behavior after submitting.
<form onSubmit={(event) => {event.preventDefault() /* this stops reloading the page */ }} ></form>
To be able to submit on enter, you should add a button in side the form tag and add type "submit" to it.
<form onSubmit={(event) => {event.preventDefault() /* this stops reloading the page */ }} >
<button type={"submit"}/>
</form>
That should do it.
I have been writing a custom Material-UI Select dropdown which has an optional text field at the top to allow the user to search / filter items in the Select if there were many entries.
I am struggling with how to keep the Select open when I click on the text field (rendered as an InputBase) and just have the normal behavior (of closing the Select when a regular MenuItem is selected.
CodeSandbox here : https://codesandbox.io/s/inspiring-newton-9qsyf
const searchField: TextField = props.searchable ? (
<InputBase
className={styles.searchBar}
onClick={(event: Event) => {
event.stopPropagation();
event.preventDefault();
}}
endAdornment={
<InputAdornment position="end">
<Search />
</InputAdornment>
}
/>
) : null;
return (
<FormControl>
<Select
className={styles.root}
input={<InputBase onClick={(): void => setIconOpen(!iconOpen)} />}
onBlur={(): void => setIconOpen(false)}
IconComponent={iconOpen ? ExpandMore : ExpandLess}
{...props}
>
{searchField}
{dropdownElements.map(
(currEntry: string): HTMLOptionElement => (
<MenuItem key={currEntry} value={currEntry}>
{currEntry}
</MenuItem>
)
)}
</Select>
</FormControl>
);
As you can see above I've tried using stopPropagation and preventDefault but to no avail.
check out this codesandbox link: https://codesandbox.io/s/busy-paper-9pdnu
You can use open prop of Select API
I was able to make a controlled open select by providing open prop as a react state variable and implementing correct event handlers. To make it controlled you must provide onOpen and onClose props of the Select and make sure the open prop stays true when the custom textfield is clicked.
One more important thing I had to do was override the default keyDown behavior of the Select component. If you open up a Select and start typing into it, it shifts focus to the select option that matches what you are typing. For example, if you Select had an option with the text Foobar and if you start typing Food and water, it would cause focus to shift from your custom text input onto the Foobar option. This behavior is overridden in the onKeyDown handler of the custom textfield
Working sandbox here
Edit: even though this worked in the codepen, I had to add onChange={handleOpen} to the Select as well to get it working on a real browser with React and Next.
You can still use stopPropagation to make it work
// open state
const [isSelectorOpen, setisSelectorOpen] = useState(false)
// handle change
const handleChange = event => {
const { value } = event.target
event.stopPropagation()
// set your value
}
// selector
<Select
multiple
open={isSelectorOpen}
onChange={handleChange}
input={(
<Input
onClick={() => setisSelectorOpen(!isSelectorOpen)}
/>
)}
// other attribute
>
<MenuItem>a</MenuItem>
<MenuItem>b</MenuItem>
<MenuItem>c</MenuItem>
</Select>
In my case, none of the above worked, but this did the trick to stop closing the Select:
<MenuItem
onClickCapture={(e) => {
e.stopPropagation();
}}>
You can also change onMouseEnter to change some default styling that comes with using MenuItem (like pointer cursor when mouse enters its layout)
onMouseEnter={(e) => {
e.target.style.backgroundColor = "#ffffff";
e.target.style.cursor = "default";
}}
In my case i also needed to remove the grey clicking effect that MenuItem makes on click, which is a new object generated in MuiTouchRipple-root, so changing display to none did the trick.
sx={{
"& .MuiTouchRipple-root": {
display: "none",
},
}}
I have a nested list in React. The LI elements have an onClick event handler. It needs to stop propagation or else all the higher lying event handlers will fire too.
I can do this by having:
<li key='myKey' onClick={this.onClick}/>
combined with
onClick (event) {
event.preventDefault()
}
I can also pass the key by doing:
<li key='myKey' onClick={this.onClick.bind(this, 'myKey'}/>
But how can I pass BOTH?
I have searched long but not found a way to extract the key from the event.
Try something like this:
<li
key={key}
onClick={e => {
e.preventDefault();
// invoke your onClick callback here
this.handleOnClick(key);
}}
/>