Coordinates of Selected Text in Browser Page

Coordinates of selected text in browser page

In IE >= 9 and non-IE browsers (Firefox 4+, WebKit browsers released since early 2009, Opera 11, maybe earlier), you can use the getClientRects() method of Range. In IE 4 - 10, you can use the boundingLeft and boundingTop properties of the TextRange that can be extracted from the selection. Here's a function that will do what you want in recent browsers.

Note that there are some situations in which you may wrongly get co-ordinates 0, 0, as mentioned in the comments by @Louis. In that case you'll have to fall back to a workaround of temporarily inserting an element and getting its position.

jsFiddle: http://jsfiddle.net/NFJ9r/132/

Code:

function getSelectionCoords(win) {
win = win || window;
var doc = win.document;
var sel = doc.selection, range, rects, rect;
var x = 0, y = 0;
if (sel) {
if (sel.type != "Control") {
range = sel.createRange();
range.collapse(true);
x = range.boundingLeft;
y = range.boundingTop;
}
} else if (win.getSelection) {
sel = win.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0).cloneRange();
if (range.getClientRects) {
range.collapse(true);
rects = range.getClientRects();
if (rects.length > 0) {
rect = rects[0];
}
x = rect.left;
y = rect.top;
}
// Fall back to inserting a temporary element
if (x == 0 && y == 0) {
var span = doc.createElement("span");
if (span.getClientRects) {
// Ensure span has dimensions and position by
// adding a zero-width space character
span.appendChild( doc.createTextNode("\u200b") );
range.insertNode(span);
rect = span.getClientRects()[0];
x = rect.left;
y = rect.top;
var spanParent = span.parentNode;
spanParent.removeChild(span);

// Glue any broken text nodes back together
spanParent.normalize();
}
}
}
}
return { x: x, y: y };
}

UPDATE

I submitted a WebKit bug as a result of the comments, and it's now been fixed.

https://bugs.webkit.org/show_bug.cgi?id=65324

Getting selected text position

The easiest way is to insert a temporary marker element at the start or end of the selection and get its position. I've demonstrated how to do this before on Stack Overflow: How can I position an element next to user text selection?

selected text and xy coordinates

Just googled it:

var txt = "";

if (window.getSelection) {
txt = window.getSelection();
} else if (document.getSelection) {
// FireFox
txt = document.getSelection();
} else if (document.selection) {
// IE 6/7
txt = document.selection.createRange().text;
}

txt = txt.toString()

There is no simple way to get X/Y coordinates of the selected text. Because it dependes on its container position and size, text font, text layout, and many many other variables.

How to get the screen coordinates for the selected text within a textarea

First issue: startOffset and endOffset don't relate to the position of a selection on the screen. Instead they relate to the character or node position within the startContainer or endContainer where a selection begins an ends. This allows you to determine the starting and ending character or node index of a selection spanning multiple elements (imagine a selection starting in a bold region of a paragraph and ending outside of it, startOffset would be the index of the within just the bold element's text, and endOffset would be the index within the Text range coming after the bold element).

Good news: there is an experimental function on the Range object called getBoundingClientRect that does get screen coordinates for a bounding box surrounding the current selection.

The rub: Unfortunately, at least in Google Chrome, getBoundingClientRect does not return accurate coordinates when a selection is within a textarea or input. So unless you can avoid using textarea, for example by using a content editable div (see: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content) this solution may not work for you.

Crazy alternative: the Canvas object has facilities for measuring the dimensions of text, so if you were really diehard, you could theoretically measure the dimensions of the text contained in the textarea, including emulating the word wrapping behavior, in order to calculate independently where the selected text is within the textarea (don't forget to account for scroll offset).


Here is a snippet that visually demonstrates the state of the selection Range object as it changes.

var outline = document.getElementById('selection_outline');var start_container = document.getElementById('start_container');var end_container = document.getElementById('end_container');var start_offset = document.getElementById('start_offset');var end_offset = document.getElementById('end_offset');
document.addEventListener('selectionchange', function() { var selection = document.getSelection(); if (selection) { var range = selection.getRangeAt(0); if (range) { var bounds = range.getBoundingClientRect(); outline.style.top = `${bounds.top + window.scrollY}px`; outline.style.left = `${bounds.left + window.scrollX}px`; outline.style.width = `${bounds.width}px`; outline.style.height = `${bounds.height}px`; start_container.value = range.startContainer.tagName || range.startContainer.parentNode.tagName; end_container.value = range.endContainer.tagName || range.endContainer.parentNode.tagName; start_offset.value = range.startOffset; end_offset.value = range.endOffset; } }})
#selection_outline {  position: absolute;  border: 2px solid red;  pointer-events: none;}
.flow-wrap-row { display: flex; flex-direction: row; flex-wrap: wrap;}
.flow-wrap-row > * { white-space: nowrap}
.flow-wrap-row > li { margin-right: 1.5em;}
ul { padding-left: 1.5em;}
input { width: 80px;}
<p>This is <b>a complex element</b> with lots of selectable text <i>and nested, <b> and double nested</b> elements</i>!!!</p><textarea cols="60" rows="5">Here is a text area with some simple content.</textarea><div contenteditable="true">Here is a contenteditable div with some simple content.</div><ul class="flow-wrap-row">  <li><label>startContainer: <input id="start_container" type="text" /></label></li>  <li><label>endContainer: <input id="end_container" type="text" /></label></li>  <li><label>startOffset: <input id="start_offset" type="text" /></label></li>  <li><label>endOffset: <input id="end_offset" type="text" /></label></li></ul><div id="selection_outline"></div>

how do I get co-ordinates of selected text in an html using javascript document.getSelecttion()

Here is the basic idea. You insert dummy element in the beginning of the selection and get the coordinates of that dummy html element. Then you remove it.

var range = window.getSelection().getRangeAt(0);
var dummy = document.createElement("span");
range.insertNode(dummy);
var box = document.getBoxObjectFor(dummy);
var x = box.x, y = box.y;
dummy.parentNode.removeChild(dummy);

Get selected text position and place an element next to it

You could position a marker span at the end of the selection, get its coordinates using jQuery, place your button at those coordinates and remove the marker span.

The following should get you started:

var markSelection = (function() {
var markerTextChar = "\ufeff";
var markerTextCharEntity = "";

var markerEl, markerId = "sel_" + new Date().getTime() + "_" + Math.random().toString().substr(2);

var selectionEl;

return function(win) {
win = win || window;
var doc = win.document;
var sel, range;
// Branch for IE <= 8
if (doc.selection && doc.selection.createRange) {
// Clone the TextRange and collapse
range = doc.selection.createRange().duplicate();
range.collapse(false);

// Create the marker element containing a single invisible character by creating literal HTML and insert it
range.pasteHTML('<span id="' + markerId + '" style="position: relative;">' + markerTextCharEntity + '</span>');
markerEl = doc.getElementById(markerId);
} else if (win.getSelection) {
sel = win.getSelection();
range = sel.getRangeAt(0).cloneRange();
range.collapse(false);

// Create the marker element containing a single invisible character using DOM methods and insert it
markerEl = doc.createElement("span");
markerEl.id = markerId;
markerEl.appendChild( doc.createTextNode(markerTextChar) );
range.insertNode(markerEl);
}

if (markerEl) {
// Lazily create element to be placed next to the selection
if (!selectionEl) {
selectionEl = doc.createElement("div");
selectionEl.style.border = "solid darkblue 1px";
selectionEl.style.backgroundColor = "lightgoldenrodyellow";
selectionEl.innerHTML = "<- selection";
selectionEl.style.position = "absolute";

doc.body.appendChild(selectionEl);
}

// Find markerEl position http://www.quirksmode.org/js/findpos.html
var obj = markerEl;
var left = 0, top = 0;
do {
left += obj.offsetLeft;
top += obj.offsetTop;
} while (obj = obj.offsetParent);

// Move the button into place.
// Substitute your jQuery stuff in here
selectionEl.style.left = left + "px";
selectionEl.style.top = top + "px";

markerEl.parentNode.removeChild(markerEl);
}
};
})();


Related Topics



Leave a reply



Submit