get range and coordinate of selected text - javascript

How to select the text of the page in FireFox?
For example, there's a paragraph of text, user select the text in those paragraph in a regular way.then, I want to know in which paragraph the text selected by user (in which position-xy coordinates, range position).

You've asked about selection coordinates twice before. I know I've given you a working answer, so why are you asking again?
Here's some code that will return you the innermost element containing the selection in Firefox (assuming a single selection; Firefox allows multiple selections). Hope it's helpful.
function getSelectionContainerElement() {
var sel = window.getSelection(), el = null;
if (sel.rangeCount) {
var range = sel.getRangeAt(0);
el = range.commonAncestorContainer;
if (el.nodeType != 1) {
el = el.parentNode;
}
}
return el;
}

refer the getSelection(), to get an object which contains information about the selected text and its position in the parent Element

Selection - MDC might help you to find answer of all your questions.

You can find the exact answer here
but this module is under construction, You can browse project and find this module.

Related

Jquery set cursor at the end of the text in contenteditable

I am working on my jquery script to remove the link by replace the text when I click on a button. I have got a problem with the cursor at the end of the text because it will move the cursor at the start of the text, example: when I click next to 2!, it will move the cursor at the start before the Video Here 1 when I try this:
selected_text = window.getSelection().getRangeAt(0).endContainer.wholeText;
$('#text_editor').html($('#text_editor').html().replace('Video Here 2!', selected_text));
$('#text_editor').focus().val(selected_text);
Here is my code: https://jsfiddle.net/su9dktrz/
What I want to achieve is when I click on the text "Video Here 1!", "Video Here 2!", "Video Here 3!" or whatever it is and when I click on a button to remove the hyperlink to replace it with a text, I want to move the cusor next to the text 2! or whatever it is.
Can you please show me example how I can move the cursor next to the text 1!, 2!, 3! or whatever it is in the contenteditable?
I have tried to find the answer on google but I couldn't find it.
Thank you.
You may need to clean this up for your browsers, I tested in FireFox.
Working Example: https://jsfiddle.net/Twisty/ksd1gwp9/22/
JavaScript
$(function() {
function unlink(link) {
var txt = link.text();
var sp = $("<span>").html(txt);
link.replaceWith(sp);
}
function getObjectFromCursor() {
var obj, sel = window.getSelection() ? window.getSelection() : document.selection;
var range = sel.getRangeAt(0);
obj = $(range.startContainer.parentElement);
console.log(obj, range);
return obj;
}
$("#toolbar").on("click", "#toolbar_unlink", function() {
var target = getObjectFromCursor();
if (target.prop("nodeName") == "A") {
unlink(target);
}
})
});
The Range interface represents a fragment of a document that can contain nodes and parts of text nodes.
The range has nodes, for start and end of the selection, and these have references to parent elements. This is helpful for us to find the specific element the cursor is in or at. We can then target this element and replace it with plain text based on the content of the link.

Count cursor position with tags included in range

I am trying to find position of cursor in contentEditable div which has HTML tags inside it.
Example of div would be:
<div id='editor'>
<h2>some</h2> text <span>goes here</span>
</div>
So if cursor is on letter "T" in 'text' word i want to get position that includes:
"<h2>some</h2> t"
not just
"some "
I have found solution that doesn't count tags in position: Get a range's start and end offset's relative to its parent container
This works fine but i need to count tags as well in final range.
Range object gives a range indicating DOM elements currently selected, it gives you information regarding in which element, at which position selection started and in which element, at which position selection ended.
function replaceSelectionWith(value) {
const selection = window.getSelection();
const range = selection.getRangeAt(0);
const {
startContainer,
startOffset,
endContainer,
endOffset
} = range;
if(startContainer === endContainer) {
const container = startContainer;
// nodeType === 3 means text node, can also check same using nodeName === '#text'
if (container.nodeType === 3) {
container.nodeValue = container.nodeValue.slice(0, startOffset) + value + container.nodeValue.slice(endOffset);
}
}
}
// Once you change the DOM, you can just get your HTML value from #editor.innerHTML
This is a simplest usecase, which is your scenario right now. But it is possible that startContainer and endContainer are different nodes or they can be DOM elements instead of being simple text nodes. This example is just to set you in the right direction.
You can refer to following documentation to understand how they actually work.
https://developer.mozilla.org/en-US/docs/Web/API/Selection
https://developer.mozilla.org/en-US/docs/Web/API/Range
EDIT: Working solution for the case asked in question
function insertTextAndGetHTML(value) {
replaceSelectionWith(value);
return document.getElementById('editor').innerHTML;
}

Toggle text bold in the exact position from where selected in the browser in an editable iframe

I'm trying to make it so I can edit an iframe and be able to set selected text to bold. As you can see in the text below.
Now, I will explain how I am stuck, first of all here is some code:
var sel = $iFrame.get(0).contentWindow.getSelection();
if (sel.rangeCount) {
var text = sel.toString();
var range = sel.getRangeAt(0);
var parent = range.commonAncestorContainer;
if (parent.nodeType != 1) {
parent = parent.parentNode;
}
if (range.startOffset > 0) {
// change at offset
} else {
// change at beginning
}
}
So far I have acquired the parent node. But I'm struggling with how I can replace the content within the parent node with the new code including the tags. I also need it to consider that other text might already be bold and words might be repeated so i need it to make the exact highlighted option bold.
Also be able to reverse it to remove the bold from the selected text.
How can I proceed from this?
You don't want to insert tags yourself; just let the browser do it with the built-in commands. As long as the user has text selected in a contenteditable document, just running this function call will turn it bold:
document.execCommand('bold', false, null)
This works on practically every browser. Other commands and full documentation can be found here: https://developer.mozilla.org/en-US/docs/Web/API/document/execCommand

Javascript get range compared to a parent element

I have a function that return an array (won't work in IE) with two elements
the html code of what the user select inside a div (id=text)
the range of the selection
In case the user select a simple string inside the text div the range return the correct values but when the user select a string inside an element child of div (div#text->p for example) range's values are related to the child element but i want them to be related to the parent (div#text)
Here there's a JsFiddle http://jsfiddle.net/paglia_s/XKjr5/: if you select a string of normal text or normal text + bolded text in the teatarea you'll get the right selection while if you select the bolded word ("am") you'll get the wrong one because the range is related to the child element.
There's a way to do so that the range is always related to div#text?
You could use my Rangy library and its new TextRange module, which provides methods of Range and selection to convert to and from character offsets within the visible text of a container element. For example:
var container = document.getElementById("text");
var sel = rangy.getSelection();
if (sel.rangeCount > 0) {
var range = sel.getRangeAt(0);
var rangeOffsets = range.toCharacterRange(container);
}
rangeOffsets has properties start and end relative to the visible text inside container. The visible text isn't necessarily the same as what jQuery's text() method returns, so you'll need to use Rangy's innerText() implementation. Example:
http://jsfiddle.net/timdown/KGMnq/5/
Alternatively, if you don't want to use Rangy, you could adapt functions I've posted on Stack Overflow before. However, these rely on DOM Range and Selection APIs so won't work on IE < 9.
If you don't want to use a library here is a way which worked for me.
The function returns the cursor offset relative to the textContent of the given node (not in relation to the sub nodes).
Note: The current cursor position must lie in the given node or in any of its sub-nodes.
It's not cross-browser compatible (specially not for IE), but I think it's not much work to fix that as well:
function getCursorPositionInTextOf(element) {
var range = document.createRange(),
curRange = window.getSelection().getRangeAt(0);
range.setStart(element, 0);
range.setEnd(curRange.startContainer, curRange.startOffset);
//Measure the length of the text from the start of the given element to the start of the current range (position of the cursor)
return document.createElement("div").appendChild(range.cloneContents()).textContent.length;
}

how to get selected text, but can I get surrounding context in javascript?

I am able to grab the text that a user has selected on a web page,
using this code:
function getSelected() {
var userSelection;
if (window.getSelection) {
selection = window.getSelection();
} else if (document.selection) {
selection = document.selection.createRange();
}
}
is it posible for me to get the words around the
selected word.
Take these sentences for example: "If you need to
framglubble the zartbox, then you should buy the red widget.
Otherwise you can buy the blue widget and save some money."
My code will tell me if the person has selected the word "widget".
But I'd like to know if the selection is after "red" or "blue". Is
this possible? I've been scouring the Internet for some advice, and
I'm having trouble finding an answer.
thank you for your help
I have written quick script that can identify the part before selection and after selection inside the same DIV element.
However if the same DIV contains the same word more than one time and you select only that word, the current code I wrote can't identify if it's the first or second selected word so bottom line it will not answer your needs.
Anyway, you can see/copy/test the code here: http://jsfiddle.net/kvHxJ/ just select something and see the alert that appears.
If it's enough for your needs after all then great, accept this answer and move on... otherwise I need to know: can we assume the user will select whole words only, one word only? If the answer is yes I do have idea how to go around this.
The way to do this in non-IE browsers is to obtain a Range object from the selection. The range has a start and end boundary, and each boundary of the range is expressed as an offset within a node; if the boundary is within a text node, this offset will be a character offset.
For example, if the following was a text node and the selection is delimited by pipes:
"red |widget| blue widget"
... then the range you'd get from the selection would have a start offset of 4 within the text node.
The following will get you a Range representing the selection and alert the start boundary:
var sel = window.getSelection();
var selectedRange = sel.rangeCount ? sel.getRangeAt(0) : null;
if (range) {
alert("Offset " + selectedRange.startOffset
+ " in node " + selectedRange.startContainer.nodeName);
}
Ranges may be compared to other Ranges, so if you wanted to know, for example, if the current selection came after the word "blue" in the above text node, you could create a Range encompassing the word "blue" and compare it with the selected Range:
// Assume the text node is stored in a variable called textNode
var blueRange = document.createRange();
blueRange.setStart(textNode, 11);
blueRange.setEnd(textNode, 15);
var selectionIsAfterBlue =
(selectedRange.compareBoundaryPoints(Range.END_TO_START, blueRange) == 1);
In IE, none of this works and everything is done differently, generally with much more difficulty. To normalize this to single consistent interface, you could use my Rangy library.
IE has the move set of methods, which reduces this problem to just a couple of lines to expand the selection forward or backward any number of words (see http://www.webreference.com/js/column12/trmethods.html). From there, it's just a matter of comparing text against any arbitrary list of values. Other browsers don't have this feature AFAIK. Fate of the browser wars: one develops an awesome feature ignored or barred by patent from any other, so the feature is forever lost and avoided as burden of cross-browser support for all these innovations inevitably falls squarely on the website designers.
So, below is a generalized function to only get the ID of the parent element of the selected text. And, to work with this cross-browser solution, you have to wrap each word in it's own element complete with unique ID or other attribute. With this setup, it should then be a relatively painless jump to looking ahead and back at sibling or sequentially ID'd/named elements.
The catch here is that the client has to click/drag from the start of the word or phrase to the end, and absolutely no bordering spaces. Even double-clicking on a word will cause it to reference the next element (or in the case of IE, the parent DIV). Additionally, you should add code to restrict the selection boundary to a single parent DIV, as the below code may also expand the selection to surrounding elements. But hopefully you can take fixing that up from here. Otherwise, it's up to using vectors to pinpoint the coordinates of a text compared to all surrounding text.
<script type="text/javascript">
function get_selected_element_id() {
if (window.getSelection) {
// FF
var range = window.getSelection();
}
else if (document.selection) {
// IE
var range = document.selection.createRange();
}
if (range.focusNode) {
// FF
var test_value = range.focusNode.parentNode.id;
}
else {
// IE
var test_value = range.parentElement().id;
}
return test_value;
}
</script>
</head>
<body>
<div id="test_div">
<span id="test1">test</span> <span id="test2">asdf</span> <span id="test3">test2</span> <span id="test4">bla</span>
</div>
<button onclick="alert(get_selected_element_id());">go</button>

Categories