0

Ok, I am trying to create a dropdown and when selecting an item, the page should scroll to a specific section (ref).

I have several divs with the same ref (as I have more than 30 divs that require a ref).

const SettingView = () => {
    const selectedItem = "el2"; // This is temporarily as it will be the item from a dropdown
    const ref = useRef<HTMLDivElement[]>([]);
    const filterRef = (el: HTMLDivElement) => ref.current.push(el);

    return (
        <>
            for (let item of sortedRows) {
                <div ref={filterRef} id={item.some.name}>{item.text}</div>
            }
        </>
    )
}

export default SettingView;

So on a button click it should find the div ref that has the id from the selectedItem and scroll to it.

How would I do that?

3 Answers 3

0

https://stackblitz.com/edit/stackblitz-starters-tuw9cm?file=src%2FApp.tsx

import { FC, useEffect, useRef, useState } from 'react';

const Div = ({ text, isActive }) => {
  const ref = useRef<any>();
  useEffect(() => {
    if (isActive) {
      ref.current.scrollIntoView({ behavior: 'smooth' });
    }
  }, [isActive]);
  return (
    <div
      ref={ref}
      style={{
        background: isActive ? '#006699' : '#dddddd',
        height: '200px',
        margin: '10px',
      }}
    >
      {text}
    </div>
  );
};

export const App: FC<{}> = () => {
  const data = [1, 2, 3, 4, 5, 6, 7];
  const [activei, setActivei] = useState(5);
  return (
    <div>
      <div>
        {data.map((item) => (
          <div
            onClick={() => {
              setActivei(item);
            }}
            key={item}
          >
            {' '}
            {item}{' '}
          </div>
        ))}
      </div>
      {data.map((item, index) => (
        <Div isActive={item === activei} text={item} key={item} />
      ))}
    </div>
  );
};

Sign up to request clarification or add additional context in comments.

1 Comment

This was the solution that worked the best for me
0

If you render your buttons using a map(), which should be fine since only the id differs, then you can use the iterator of map to use that as an id.

Then when clicking the button, get that index in the ref.current array and call (eg) scrollIntoView() on that.


Example:

  • Click on a button to call scrollIntoView on that
  • Click on the uppermost (real) button to call scrollIntoView on index 50

const { useState, useRef } = React;

const SettingView = () => {

    const ref = useRef([]);
    
    let sortedRows = [
        { name: 'Foo', text: 'Bar' }
    ];
    
    // Add 99 other rows to sortedRow as debug values
    sortedRows = sortedRows.flatMap(i => Array(99).fill(i));
    
    const onButtonClick = (i) => {
        console.info('Clicked on ', i);
        if (ref.current[i]) {
            ref.current[i].scrollIntoView();
        }
    }
    
    return (
      <React.Fragment>
        <button onClick={() => onButtonClick(50)}>Scroll to Button #50</button>
        
        {sortedRows.map((row, i) => {
            const { name, text } = row;
            
            return (
                <div onClick={() => onButtonClick(i)} ref={el => ref.current[i] = el} id={"el" + i}>
                    Button {i} - {text}
                </div>
            );
            
        })}
      </React.Fragment>
     );
}

ReactDOM.render(<SettingView />, document.getElementById("react"));
<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>

7 Comments

thanks for your reply. I do not use indexes, as the dropdown value are string. It should match with the ref of any of the divs
NOt sure what your trying to say, doesn't matter that your values are strings, It's still better to render those options through a loop, regarding DRY.
The divs are inside a loop (generated from an array), as there is more data shown inside those divs, but not relevant for this.
Well apparently it is relevant haha. I'm also rendering from an array so not sure what's not working for you.
I have updated my answer to also use an array of objects to render the buttons. Hope this makes it more clear.
|
-4

You can achieve this by using the scrollIntoView() method on the selected element's ref. Here's how you can modify your SettingView component to achieve this functionality:

import React, { useRef } from 'react';

const SettingView = () => {
    const selectedItem = "el2";
    const ref = useRef<HTMLDivElement[]>([]);
    const filterRef = (el: HTMLDivElement) => ref.current.push(el);

    const scrollToRef = (ref: React.RefObject<HTMLDivElement>, id: string) => {
        const selectedRef = ref.current.find(item => item.id === id);
        if (selectedRef) {
            selectedRef.scrollIntoView({ behavior: 'smooth', block: 'start' });
        }
    };

    React.useEffect(() => {
        scrollToRef(ref, selectedItem);
    }, [selectedItem]);

    return (
        <>
            <div ref={filterRef} id="el1">Some Text</div>
            <div ref={filterRef} id="el2">Some Text2</div>
            <div ref={filterRef} id="el3">Some Text3</div>
            <div ref={filterRef} id="el4">Some Text4</div>
        </>
    );
}

export default SettingView;

In this code: scrollToRef function takes the ref array and the id of the selected item. It finds the ref with the matching id and scrolls it into view using scrollIntoView() with options for smooth behavior and starting from the block's start. useEffect hook is used to trigger the scrolling effect whenever selectedItem changes.

1 Comment

Did you generate this answer using a tool like ChatGPT ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.