OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

How do I set focus on a div in React in order to trigger an effect with onBlur?

  • Thread starter Thread starter OptimusGrime
  • Start date Start date
O

OptimusGrime

Guest
I'm working on a pretty basic note taking app with React. Purely a front end question.

On a sidebar I have a list of all the current notes, which the user can use to swap between them. Each note represented in the list has a delete button to the right that will remove that note. On clicking the button, a div (I'm calling it "approval-box") will show up asking for confirmation. "Delete? Yes or No." Approval-box is shown based on a boolean useState.

enter image description here

Here's what I'm trying to do. I would like the approval-box to be dismissed if the user clicks anywhere else on the page. My intention is to set focus on the div and then have an onBlur event toggle the useState, which should then dismiss the div. If focus is lost, no more approval-box.

The trouble is I cannot for the life of me figure out how to actually set focus on approval-box.

Here's the code for the component I'm writing this in:

Code:
import { useRef } from 'react';
import { useAppContext } from '../../App';
import { BsX } from 'react-icons/bs';

const useFocus = () => {
  const htmlElRef = useRef(null);
  const setFocus = () => {
    htmlElRef.current && htmlElRef.current.focus();
  };

  return [htmlElRef, setFocus];
};

const DeleteButton = ({ note, showApproval, setShowApproval }) => {
  const { deleteSelectedNote } = useAppContext();
  const [approvalBoxRef, setApprovalBoxFocus] = useFocus();

  const handleClickDelete = () => {
    setShowApproval(!showApproval);
    setApprovalBoxFocus();
  };

  const handleConfirmDelete = (noteId) => {
    deleteSelectedNote(noteId);
  };

  return (
    <div className='delete-btn-container'>
      <button
        className={showApproval ? 'delete-btn btn pending' : 'delete-btn btn'}
        onClick={handleClickDelete}
      >
        <BsX size={'1em'} />
      </button>

      <div
        className={showApproval ? 'approval-box active' : 'approval-box'}
        tabIndex='1'
        onBlur={() => {
          setShowApproval(false);
          console.log('approval removed');
        }}
        ref={approvalBoxRef}
      >
        Delete?
        <span className='yes' onClick={() => handleConfirmDelete(note.id)}>
          {' '}
          Y{' '}
        </span>
        /
        <span className='no' onClick={() => setShowApproval(false)}>
          {' '}
          N
        </span>
      </div>
    </div>
  );
};
export default DeleteButton;

Right now I have tabIndex set to 1 on the approval-box, and I'm using a custom hook to set focus on it when the user clicks the initial delete button.

But when I do this the approval box doesn't receive focus. The user can still click around and interact with the rest of the app and the approval-box's onBlur event never fires. Focus is only set on approval-box once the user actually clicks on it. Then, if they click away the desired effect happens and the approval-box is dismissed.

Is there something about how focus works that I'm not understanding?

I don't know if this matters, but as you can see in the code, this is the delete button for a single note item in my list. Each note will have one of these and I'd prefer the user only be able to interact with one at a time.

Also the approval-box is always rendered, but I'm using CSS to make it visible and interactive. I've tried conditionally rendering the approval-box instead of just revealing it, but the focus issue is the same in that case.

If there's a better way to achieve the effect I'm going for, I'm definitely open to advice on alternatives.

<p>I'm working on a pretty basic note taking app with React. Purely a front end question.</p>
<p>On a sidebar I have a list of all the current notes, which the user can use to swap between them. Each note represented in the list has a delete button to the right that will remove that note. On clicking the button, a div (I'm calling it "approval-box") will show up asking for confirmation. "Delete? Yes or No." Approval-box is shown based on a boolean useState.</p>
<p><a href="https://i.sstatic.net/wjTOTuGY.jpg" rel="nofollow noreferrer">enter image description here</a></p>
<p>Here's what I'm trying to do. I would like the approval-box to be dismissed if the user clicks anywhere else on the page. My intention is to set focus on the div and then have an onBlur event toggle the useState, which should then dismiss the div. If focus is lost, no more approval-box.</p>
<p>The trouble is I cannot for the life of me figure out how to actually set focus on approval-box.</p>
<p>Here's the code for the component I'm writing this in:</p>
<pre><code>import { useRef } from 'react';
import { useAppContext } from '../../App';
import { BsX } from 'react-icons/bs';

const useFocus = () => {
const htmlElRef = useRef(null);
const setFocus = () => {
htmlElRef.current && htmlElRef.current.focus();
};

return [htmlElRef, setFocus];
};

const DeleteButton = ({ note, showApproval, setShowApproval }) => {
const { deleteSelectedNote } = useAppContext();
const [approvalBoxRef, setApprovalBoxFocus] = useFocus();

const handleClickDelete = () => {
setShowApproval(!showApproval);
setApprovalBoxFocus();
};

const handleConfirmDelete = (noteId) => {
deleteSelectedNote(noteId);
};

return (
<div className='delete-btn-container'>
<button
className={showApproval ? 'delete-btn btn pending' : 'delete-btn btn'}
onClick={handleClickDelete}
>
<BsX size={'1em'} />
</button>

<div
className={showApproval ? 'approval-box active' : 'approval-box'}
tabIndex='1'
onBlur={() => {
setShowApproval(false);
console.log('approval removed');
}}
ref={approvalBoxRef}
>
Delete?
<span className='yes' onClick={() => handleConfirmDelete(note.id)}>
{' '}
Y{' '}
</span>
/
<span className='no' onClick={() => setShowApproval(false)}>
{' '}
N
</span>
</div>
</div>
);
};
export default DeleteButton;
</code></pre>
<p>Right now I have tabIndex set to 1 on the approval-box, and I'm using a custom hook to set focus on it when the user clicks the initial delete button.</p>
<p>But when I do this the approval box doesn't receive focus. The user can still click around and interact with the rest of the app and the approval-box's onBlur event never fires. Focus is only set on approval-box once the user actually clicks on it. Then, if they click away the desired effect happens and the approval-box is dismissed.</p>
<p>Is there something about how focus works that I'm not understanding?</p>
<p>I don't know if this matters, but as you can see in the code, this is the delete button for a single note item in my list. Each note will have one of these and I'd prefer the user only be able to interact with one at a time.</p>
<p>Also the approval-box is always rendered, but I'm using CSS to make it visible and interactive. I've tried conditionally rendering the approval-box instead of just revealing it, but the focus issue is the same in that case.</p>
<p>If there's a better way to achieve the effect I'm going for, I'm definitely open to advice on alternatives.</p>
 

Latest posts

Top