I’m developing a Chrome extension that shows a popup when text is selected on web pages. It works fine on most websites, but I’m having issues with Google Docs. Here’s the relevant part of my content script:
import React from 'react'
import ReactDOM from 'react-dom/client'
import PopupComponent from './popup'
let popupContainer = null
function showPopup(rect, selectedText) {
if (!popupContainer) {
popupContainer = document.createElement('div')
document.body.appendChild(popupContainer)
}
const popupStyle = {
position: 'absolute',
left: `${rect.left}px`,
top: `${rect.bottom + window.scrollY}px`,
zIndex: 9999999999999999999,
}
const root = ReactDOM.createRoot(popupContainer)
root.render(
<div style={popupStyle}>
<PopupComponent />
</div>
)
}
function hidePopup() {
if (popupContainer) {
const root = ReactDOM.createRoot(popupContainer)
root.unmount()
// ReactDOM.unmountComponentAtNode(popupContainer)
}
}
function handleTextSelection() {
const selectedText = window.getSelection().toString().trim()
if (selectedText) {
const range = window.getSelection().getRangeAt(0)
const rect = range.getBoundingClientRect()
showPopup(rect, selectedText)
} else {
hidePopup()
}
}
// Listen for text selection
document.addEventListener('mouseup', handleTextSelection)
// Listen for scroll events to reposition the popup
window.addEventListener('scroll', () => {
const selection = window.getSelection()
if (!selection.isCollapsed) {
const range = selection.getRangeAt(0)
const rect = range.getBoundingClientRect()
showPopup(rect, selection.toString().trim())
}
})
// Listen for clicks outside the popup to close it
document.addEventListener('mousedown', (event) => {
if (popupContainer && !popupContainer.contains(event.target)) {
hidePopup()
}
})
// Cleanup function
function cleanup() {
document.removeEventListener('mouseup', handleTextSelection)
window.removeEventListener('scroll', handleTextSelection)
if (popupContainer) {
document.body.removeChild(popupContainer)
}
}
// Call cleanup when the content script is unloaded
window.addEventListener('unload', cleanup)
Problem is in this method:
function handleTextSelection() {
const selectedText = window.getSelection().toString().trim()
if (selectedText) {
const range = window.getSelection().getRangeAt(0)
const rect = range.getBoundingClientRect()
showPopup(rect, selectedText)
} else {
hidePopup()
}
}
document.addEventListener('mouseup', handleTextSelection)
This works well on regular web pages, but in Google Docs, window.getSelection() returns an empty string. I suspect this is because Google Docs uses a canvas or some other technology that doesn’t interact with the standard DOM selection API.
I’ve noticed that other extensions like Grammarly can successfully capture text selection in Google Docs. Is there a way to achieve this functionality?
Any insights or suggestions would be greatly appreciated. thanx!