someuser2491
someuser2491

Reputation: 1968

How to scroll a list item into view using scrollintoview method using reactjs?

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

Answers (1)

soupette
soupette

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

Related Questions