I'm looking to do something whenever a user finishes making a selection —essentially, on the first mouseup
event after every selectstart
event, I think— on the page. I want to take that selection and wrap it in an element to be styled via CSS. I presumed the Selection API offered an event for this; however, it doesn't seem to.
I don't simply listen for mouseup
'cause I'm especially looking for this to work with the selection that results from the browser's find functionality ("Find in This Page…"; ⌘+f).
let selContainer = document.createElement('span')
span.classList.add('user-selection')
const wrapSelection = () => {
window.getSelection().getRangeAt(0).surroundContent(selContainer)
}
/* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ ┃
┃ The Selection API only ┃
┃ affords these events: ┃
┃ ┃
┃ - selectionchange ┃
┃ - selectstart ┏━━━━━┫
┃ ┃issue┃
┗━━━━━━━━━━━━━━━━━━━━┻━━━━━┛
*/document.addEventListener('selectfinish', wrapSelection)/*
┗━━━━┳━━━━━┛
┃
┃
no such
event */
I'm looking to do something whenever a user finishes making a selection —essentially, on the first mouseup
event after every selectstart
event, I think— on the page. I want to take that selection and wrap it in an element to be styled via CSS. I presumed the Selection API offered an event for this; however, it doesn't seem to.
I don't simply listen for mouseup
'cause I'm especially looking for this to work with the selection that results from the browser's find functionality ("Find in This Page…"; ⌘+f).
let selContainer = document.createElement('span')
span.classList.add('user-selection')
const wrapSelection = () => {
window.getSelection().getRangeAt(0).surroundContent(selContainer)
}
/* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ ┃
┃ The Selection API only ┃
┃ affords these events: ┃
┃ ┃
┃ - selectionchange ┃
┃ - selectstart ┏━━━━━┫
┃ ┃issue┃
┗━━━━━━━━━━━━━━━━━━━━┻━━━━━┛
*/document.addEventListener('selectfinish', wrapSelection)/*
┗━━━━┳━━━━━┛
┃
┃
no such
event */
Share
Improve this question
edited Nov 26, 2020 at 0:30
tjfwalker
asked Mar 1, 2018 at 22:41
tjfwalkertjfwalker
4944 silver badges18 bronze badges
4 Answers
Reset to default 4This is trickier than it should be. I’ve used a bination of element.onselectstart
, element.onmouseup
, and document.onselectionchange
. Take a look at this demo.
function onSelect(element, callback) {
// console.log(element, callback);
let isSelecting = false;
let selection = null;
function handleSelectStart(event) {
// console.log(event);
isSelecting = true;
}
function handleMouseUp(event) {
// console.log(event, isSelecting);
if (isSelecting && !document.getSelection().isCollapsed) {
callback((selection = document.getSelection()));
isSelecting = false;
}
}
function handleSelectionChange(event) {
// console.log('change', isSelecting);
if (document.getSelection().isCollapsed && null !== selection) {
callback((selection = null));
}
}
element.addEventListener('selectstart', handleSelectStart);
element.addEventListener('mouseup', handleMouseUp);
document.addEventListener('selectionchange', handleSelectionChange);
return function destroy() {
element.removeEventListener('selectstart', handleSelectStart);
element.removeEventListener('mouseup', handleMouseUp);
document.removeEventListener('selectionchange', handleSelectionChange);
};
}
This won’t handle non-mouse interactions. Acmodating pointer and keyboard events would be a nice enhancement. I think the overall pattern holds, though.
One way that I've gotten it to work is to track the selectionchange
event and when the selection doesn't change for over 500ms, I consider that a select end. It's not perfect, but it works and correctly triggers for any kind of selection, be it mouse, keyboard, or CTRL+F.
let selectionDelay = null, selection = '';
document.addEventListener('selectionchange', () => {
const currentSelection = document.getSelection().toString();
if (currentSelection != selection) {
selection = currentSelection;
if (selectionDelay) {
window.clearTimeout(selectionDelay);
}
selectionDelay = window.setTimeout(() => {
wrapSelection();
selection = '';
selectionDelay = null;
}, 500);
}
});
I picked through the source code of the hypothes.is web annotation client in an effort to understand how they get their toolbar
to appear on user select action end. It seems to be a matter of employing an observer via zen-observable.
simpler alternative to the accepted answer.
document.addEventListener('mouseup', e => {
var s = document.getSelection();
if (!s.isCollapsed) {
// do sth with selection
}
});
or maybe better?
document.addEventListener('selectstart', e => {
document.addEventListener('mouseup', somefunction);
});
function somefunction(e) {
// do sth
document.removeEventListener('mouseup', somefunction);
}