Last active
August 4, 2024 00:29
-
-
Save drbr/ea9e643e536b77243fca060815c9af3d to your computer and use it in GitHub Desktop.
useScopedSelectionCallback
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect } from 'react'; | |
/** | |
* Using the browser's Selection API, sets up a listener to detect when the user selects text that | |
* resides completely within a specified element. | |
* | |
* @param containerRef The element to detect selections within. | |
* @param callback The function to call when the selection changes. It may be called additional | |
* times, particularly with `null`. If the selected text resides only partially within the container | |
* element, the callback will be called with `null`. | |
*/ | |
export function useScopedSelectionCallback( | |
containerRef: React.RefObject<Node>, | |
callback: (text: string | null) => void | |
) { | |
useEffect(() => { | |
const eventHandler = () => { | |
const selection = document.getSelection(); | |
if (!selection) { | |
callback(null); | |
return; | |
} | |
const selectionIsValid = | |
containerRef.current?.contains(selection.anchorNode) && | |
containerRef.current?.contains(selection.focusNode) && | |
!selection.isCollapsed; | |
if (!selectionIsValid) { | |
callback(null); | |
return; | |
} | |
callback(selection.toString()); | |
}; | |
document.addEventListener('selectionchange', eventHandler); | |
return () => { | |
document.removeEventListener('selectionchange', eventHandler); | |
}; | |
}, [callback, containerRef]); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Could you keep a ref of the last selection value and then only call
callback(null)
if the selection went from a value to no value?