Reputation: 1968
i want to move a list item into view using scrollIntoView method using reactjs.
What i am trying to do? i have an array of objects stored in variable some_arr and i display those values in a dropdown menu. when user presses down key then the dropdown item gets highlighted. also using up arrow key to navigate up in the dropdown menu. and clicking enter key will select the dropdown item and replaces the value in the input field.
I have implemented code below and it works fine. but when user presses down arrow key and highlighted dropdown menu is not in view i want it to be visible to user.
So to implement that i have used ref (this.dropdown_item_ref) to the dropdown item. however this ref always points to last item in the dropdown menu. meaning consider i have
some_arr = [
{
id:1,
name: somename,
},
{
id: 2,
name: fname,
},
{
id: 3,
name: lname, //ref is always pointing to this item
},
],
so here the ref is always pointing to lname in the dropdown menu.
Below is what i have tried and is not working,
class Dropdownwithinput extends React,PureComponent {
constructor(props) {
super(props);
this.list_item_ref = React.createRef();
this.state = {
input_val: '',
dropdown_values: [],
dropdown_item_selection: 0,
};
}
componentDidMount = () => {
const values = [
{
id:1,
name: somename,
},
{
id: 2,
name: fname,
},
{
id: 3,
name: lname, //ref is always pointing to this item
},
],
this.setState({dropdown_values: values});
}
handle_key_down = (event) => {
if (this.state.dropdown_values > 0) {
if (event.keyCode === 38 && this.state.dropdown_item_selection
> 0) {
this.setState({dropdown_item_selection:
(this.state.dropdown_item_selection - 1) %
this.state.dropdown_values.length});
this.list_item_ref.current.scrollIntoView();
} else if (event.keyCode === 40) {
this.setState({dropdown_item_selection:
(this.state.dropdown_values_selection + 1) %
this.state.dropdown_values.length});
this.list_item_ref.current.scrollIntoView();
}
if (event.keyCode === 13) {
event.preventDefault();
const selected_item =
this.state.dropdown_values[this.state.user_selection];
const text = this.replace(this.state.input_val,
selected_item);
this.setState({
input_val: text,
dropdown_values: [],
});
}
}
replace = (input_val, selected_item) => {
//some function to replace value in input field with the
//selected dropdown item
}
render = () => {
return (
<input
onChange={this.handle_input_change}
onKeyDown={this.handle_key_down}/>
<div>
{this.state.dropdown_values.map((item, index) => (
<div key={index} className={"item" + (index ===
this.state.dropdown_item_selection ? ' highlight'
: '')}>
{item.name}
</div>
))}
</div>
)
};
}
}
Could someone help me fix this. thanks.
Upvotes: 2
Views: 3494
Reputation: 1280
I have adapted a bit your code:
import React from "react";
class Example extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
const dropdownValues = Array.from({ length: 100 }, (_, k) => k).reduce(
(acc, curr) => {
return acc.concat([{ id: curr, name: `${curr}.so` }]);
},
[]
);
this.state = {
input_val: "",
dropdownValues,
selectedItem: 0
};
this.listRefs = dropdownValues.reduce((acc, current, index) => {
acc[index] = React.createRef();
return acc;
}, {});
}
componentDidMount() {
window.addEventListener("keydown", this.handleKeyDown);
}
componentWillUnmount() {
window.removeEventListener("keydown", this.handleKeyDown);
}
componentDidUpdate(prevProps, prevState) {
if (prevState.selectedItem !== this.state.selectedItem) {
this.listRefs[this.state.selectedItem].current.scrollIntoView();
}
}
handleKeyDown = event => {
const keyCodes = {
up: 38,
down: 40
};
if (![38, 40].includes(event.keyCode)) {
return;
}
this.setState(prevState => {
const { dropdownValues, selectedItem } = prevState;
let nextSelectedItem;
if (keyCodes.up === event.keyCode) {
nextSelectedItem =
dropdownValues.length - 1 === selectedItem ? 0 : selectedItem + 1;
}
nextSelectedItem =
selectedItem === 0 ? dropdownValues.length - 1 : selectedItem - 1;
return { ...prevState, selectedItem: nextSelectedItem };
});
};
replace = (input_val, selected_item) => {
//some function to replace value in input field with the
//selected dropdown item
};
render() {
return (
<>
<input
onChange={this.handle_input_change}
onKeyDown={this.handle_key_down}
/>
<button
type="button"
onClick={() => this.setState({ selectedItem: 50 })}
>
Focus element 50
</button>
<div ref={this.listRef}>
{this.state.dropdownValues.map((item, index) => (
<div key={index} ref={this.listRefs[index]}>
<div
style={
this.state.selectedItem === index
? { background: "yellow" }
: {}
}
>
{item.name}
</div>
</div>
))}
</div>
</>
);
}
}
export default Example;
Upvotes: 1