Selection selectfinish/selectend 事件应该如何实现?

How should Selection selectfinish/selectend event be implemented?

我想在用户完成选择时做一些事情——我认为基本上是在每个 selectstart 事件之后的第一个 mouseup 事件上——在页面上。我想采用该选择并将其包装在要通过 CSS 设置样式的元素中。我推测选择 API 为此提供了一个事件;然而,似乎并没有。

我不只是听 mouseup 因为我特别希望它能与浏览器的查找功能产生的选择一起工作(“在此页面中查找...”;+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                                                                                                                                                                                                        */

我挑了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.

已接受答案的更简单替代方案。

 document.addEventListener('mouseup', e => {
     var s = document.getSelection();
     if (!s.isCollapsed) {
          // do sth with selection
     }
 });

或者更好?

 document.addEventListener('selectstart', e => {
     document.addEventListener('mouseup', somefunction);
 });
 function somefunction(e) {
     // do sth
     document.removeEventListener('mouseup', somefunction);
 }

我让它工作的一种方法是跟踪 selectionchange 事件,当 selection 在超过 500 毫秒内没有变化时,我认为 select 结尾。它并不完美,但它可以正常工作并正确触发任何类型的 selection,无论是鼠标、键盘还是 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);
        }
    });

这比它应该的更棘手。我使用了 element.onselectstartelement.onmouseupdocument.onselectionchange 的组合。看看这个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);
    };
}

这不会处理非鼠标交互。容纳指针和键盘事件将是一个很好的增强。不过,我认为总体模式成立。