Reputation: 6134
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.
Upvotes: 0
Views: 376
Reputation: 324617
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.
The steps when handling a mouse event are:
document.caretPositionFromPoint()
or equivalentgetBoundingClientRect()
on the range to get the coordinates of the wordThe 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;
Upvotes: 1
Reputation: 2159
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.
Upvotes: 3