简体   繁体   中英

How to wrap a selected word with a span

I want to be able to hover over any word on a webpage and get a popup above that word with that word in it. That seems to be nearly impossible without huge performance problems, so I tried to do a double-click instead of hover.

With the double-click, I can detect the text of the word, but I can't do anything with the word itself in the page. Basically, I want to wrap the selected text with a span tag so that I can position things relative to it. I've seen it done before with highlighters, but I don't know how.

Note: this only needs to work in Firefox.

You can use these functions:

var range = window.getSelection().getRangeAt(0);
var newNode = document.createElement("b");
range.surroundContents(newNode);

This surrounds your selection with a b-tag.

It's not impossible with the mouseover event. You can use document.caretPositionFromPoint() , which is standardized and supported in Firefox 20 and later, and fall back to WebKit's proprietary equivalent.

http://jsfiddle.net/2zzjL/6/

The steps when handling a mouse event are:

  • Hide the word pop-up, if shown
  • Get the cursor coordinates from the event
  • Create a range from the cursor coordinates using document.caretPositionFromPoint() or equivalent
  • If the range's container node is a text node, its offset is a character index within the text node's text
  • From there, expand forwards and backwards in the text node's text until we hit white space to get the word
  • Update the range to encompass the whole word
  • Call getBoundingClientRect() on the range to get the coordinates of the word
  • Position the word pop-up element at some offset away from those coordinates and show it

The demo is quite limited (for example, it does not work with a word that crosses element boundaries, the word detection very crude and there is plenty of scope for optimization) but should be enough to get you started. It works in current browsers, except IE, which does not support document.caretPositionFromPoint() .

Here is the crucial code:

function expandToWord(str, offset) {
    var start = offset;
    while ( start >= 1 && /\S/.test( str.charAt(start - 1) ) ) {
        --start;
    }

    var end = offset, len = str.length;
    while ( end < len && /\S/.test( str.charAt(end) ) ) {
        ++end;
    }

    return {
        start: start,
        end: end,
        word: str.slice(start, end)
    };
}

var wordDiv = document.createElement("div");
wordDiv.className = "word";

var createRangeFromPoint = (function(doc) {
    // Try standards-based method first
    if (typeof doc.caretPositionFromPoint != "undefined") {
        return function(x, y) {
            var pos = doc.caretPositionFromPoint(x, y);
            var range = null;
            if (pos) {
                range = doc.createRange();
                range.setStart(pos.offsetNode, pos.offset);
                range.collapse(true);
            }
            return range;
        };
    }

    // Now try WebKit's proprietary method
    else if (typeof doc.caretRangeFromPoint != "undefined") {
        return function(x, y) {
            return doc.caretRangeFromPoint(x, y);
        };
    }

    // Give up
    else {
        return function() { return null; };
    }
})(document);

function mouseEventHandler(e) {
    if (wordDiv.parentNode) {
        wordDiv.parentNode.removeChild(wordDiv);
    }
    var range = createRangeFromPoint(e.clientX, e.clientY);
    if (range) {
        if (range.startContainer.nodeType == 3) {
            var wordInfo = expandToWord(range.startContainer.data, range.startOffset);
            if (wordInfo.word) {
                range.setStart(range.startContainer, wordInfo.start);
                range.setEnd(range.startContainer, wordInfo.end);

                var rect = range.getBoundingClientRect();

                // Get the difference between client and page coordinates from the event
                // for positioning the word div
                var offsetX = e.clientX - e.pageX;
                var offsetY = e.clientY - e.pageY;

                wordDiv.style.left = (rect.left + offsetX) + "px";
                wordDiv.style.top = (rect.top + offsetY - 20) + "px";
                wordDiv.innerHTML = "";
                wordDiv.appendChild( document.createTextNode(wordInfo.word) );

                document.body.appendChild(wordDiv);
            }
        }
    }
}

document.onmousemove = mouseEventHandler;
document.onmouseover = mouseEventHandler;
document.onmouseout = mouseEventHandler;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM