How would I assign the setStart and setEnd range to the first element (could be text, an image, hyperlink...) in the document?
If you mean the first element within the body of the document, I'd suggest the following. It uses setStartBefore() in case the element is one that cannot have children, such as <img> or <br>:
var range = document.createRange();
var firstElInBody = document.body.getElementsByTagName("*")[0];
if (firstElInBody) {
range.setStartBefore(firstElInBody);
range.collapse(true);
}
One final thing: in case you're not aware, IE < 9 does not support Range.
Related
<span name="workitemContent" contenteditable="">root 0 child 0 zzzz</span>
For instance, how could I use protractor to set my cursor after the word "child" and then send additional text (with sendKeys or similar) into the span at that position?
Afterwards it might look like:
<span name="workitemContent" contenteditable="">root 0 child with red hair 0 zzzz</span>
I've seen other questions that answer how to do this with JavaScript (such as How to set caret(cursor) position in contenteditable element (div)?), but not with protractor.
You can perform a click on the element with an offset, e.g.:
var elm = element(by.name("workitemContent"));
browser.actions().mouseMove(elm, 5, 0).click().perform();
But, I'm not sure whether you can call it a reliable approach.
Another option would be to follow the solution provided in the question you've linked and execute javascript via browser.executeScript():
var elm = element(by.name("workitemContent"));
function setCursor(arguments) {
var range = document.createRange();
var sel = window.getSelection();
range.setStart(arguments[0].childNodes[2], arguments[1]);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
browser.executeScript(setCursor, elm.getWebElement(), 5);
I've been trying to select only the last chars of a ContentEditable Element
I've used an example from Mozilla on http://jsfiddle.net/mh7HK/ which works on an input field but not on a ContentEditable field, although, selecting all does work
This works:
var elem = document.getElementById("contentEdit");
var range = document.createRange();
range.selectNodeContents(elem);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
This doesn't:
var elem = document.getElementById("contentEdit");
var range = document.createRange();
range.setStart(elem, 5) // breaks range Object?
range.setEnd(elem, 8) // breaks range Object?
range.selectNodeContents(elem);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
The .setStart() and .setEnd() should work like that, right?
I've looked at the limited Documentation of jQuery carrot, jQuery selection, Rangy etc., but they all seem to be aimed at getting the selection not setting it.
Two problems:
selectNodeContents(node) moves the range to encompass the whole content of node, so your setStart() and setEnd() calls are essentially ignored.
setStart(node, offset) moves the range's start boundary to offset offset within node. In the case of element, that means placing the boundary after offset child nodes of the element. In the case of a text node, which is probably what you want, the range boundary is placed after offset characters. Same with setEnd(). If you have a single text node within your editable element, you're probably done. Once you start have nested elements, you may want a more subtle approach if you want to set the selection relative to your editable element's text content.
See also https://stackoverflow.com/a/24117242/96100.
I'm having hard time figuring out how to set the caret position based on the last character in the Range object.
For instance I have contenteditable which has some text.
Hello world Test
Then I highlight portion of text ("wor") with mouse:
Then mouseup event will get selected range.
After focus it gone, the selected highlight will disappear.
Now, my goal is to set the caret to between "r" and "l" like image below (the end of the Range)
So far I got getting selection and restoring part worked but can't figure out the caret setting based on Range object.
Code:
var node = this.element.find(".reContentArea")[0];
var range = document.createRange();
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
You can use focusOffset to get the character position where the selection ends:
$('.reContentArea').on('mouseup', function() {
var offset= window.getSelection().focusOffset
})
In setting the caret position on a contenteditable element we can use setStart(startNode, startOffset). As you can see in this method we need to specify the node and the offset within that node.
var editable = $('.reContentArea')[0],
range = document.createRange(),
sel = window.getSelection();
range.setStart(editable.firstChild, offset);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
It's not clear to me what even you want to trigger the "to set the caret to", but I've made a simple click event with this solution on this jsfiddle.
I would recommend using this awesome plugin
https://github.com/acdvorak/jquery.caret
// Select everything after the 6th character
$('input').range(6);
$('textarea').val('Hello\nWorld').range(6).range().text === 'World';
check demo here :
http://www.examplet.org/jquery/caret.php
I am trying to include functionality that will enable a user to highlight text and have that text returned. I would also like to return the location of that text within #textcontainer div.
My issue: if the user highlights the second 'and' in the text below, position will equal the indexOf the first 'and' in the div instead of the second. How do I change this to have position return the position of the second 'and' (the one that is highlighted)?
<div id = 'textcontainer'>
and second third and
</div>
// beginning of highlight js
$('#highlight').click(function (e) {
e.preventDefault();
// get highlighted text
var selection = window.getSelection();
var txt = selection.toString();
console.log(txt);
fulltext = $("#textcontainer").text();
console.log(fulltext);
position = fulltext.indexOf(txt);
console.log(position);
})
// end of highlight js
You're in luck, since window.getSelection() returns a Selection object, which in turn has a getRangeAt method, which you can use like so:
var selection = window.getSelection();
var range = selection.getRangeAt(0);
var start = range.startOffset, end = range.endOffset;
Edit: Note that IE has a completely different selection API. For cross-browser compatibility, you may want to take a look at Rangy (note: I don't have any experience with the library).
Edit 2: See this answer for some jQuery plugins.
So I feel like I'm a bit out of my league here. But here's what I want to do.
Basically, I want to have a user select part of text within in a paragraph (which may contain many elemnts (i.e. <span> and <a>) to return the value of the id attribute of that paragraph. Here's what I thinking.
function getParaID() //function will be called using a mouseUp event
{
var selObj = window.getSelection();
var selRange = selObj.getRangeAt(0); //btw can anyone explain what this zero means
var paraElement = selRange.commonAncestorContainer;
var paraID = paraElement.getAttribute;
return paraID;
}
What do you think? Am I close?
The selection range's commonAncestorContainer property may be a reference to a text node, or a <span> or <a> element or <body> element, or whatever else you may have in your page. That being the case, you need to work up the DOM tree to find the containing <p> element, if one exists. You also need to be aware that IE < 9 does not support window.getSelection() or DOM Range, although it is possible to do what you want quite easily in IE < 9. Here's some code that will work in all major browsers, including IE 6:
jsFiddle: http://jsfiddle.net/44Juf/
Code:
function getContainingP(node) {
while (node) {
if (node.nodeType == 1 && node.tagName.toLowerCase() == "p") {
return node;
}
node = node.parentNode;
}
}
function getParaID() {
var p;
if (window.getSelection) {
var selObj = window.getSelection();
if (selObj.rangeCount > 0) {
var selRange = selObj.getRangeAt(0);
p = getContainingP(selRange.commonAncestorContainer);
}
} else if (document.selection && document.selection.type != "Control") {
p = getContainingP(document.selection.createRange().parentElement());
}
return p ? p.id : null;
}
Regarding the 0 passed to getRangeAt(), that is indicating which selected range you want. Firefox supports multiple selected ranges: if you make a selection and then hold down Ctrl and make another selection, you will see you now have two discontinous ranges selected, which can be accessed via getRangeAt(0) and getRangeAt(1). Also in Firefox, selecting a column of cells in a table creates a separate range for each selected cell. The number of selected ranges can be obtained using the rangeCount property of the selection. No other major browser supports multiple selected ranges.
You're quite close. If all you want is the id of the parent element, then you should replace your paraElement.getAttribute with paraElement.id, like:
var paraID = paraElement.id;
Regarding the parameter to getRangeAt(), it is specifying the index of the selection range to return, and it's only really relevant to controls that allow discontinuous selections. For instance, a select box in which the user can use ctrl + click to select several arbitrary groups of rows simultaneously. In such a case you could use the parameter to step from one selected region to the next. But for highlighting text within a paragraph you should never have a discontinuous selection and thus can always pass 0. In essence it means that you're asking for "the first selected region".
Also note that if your interface allows the user's selection to span multiple paragraphs then your commonAncestorContainer may not be a paragraph, it might also be whatever element it is that contains all of your paragraph tags. So you should be prepared to handle that case.
Edit:
After playing with this a bit, here is my suggestion: http://jsfiddle.net/vCsZH/
Basically, instead of relying on commonAncestorContainer this code applies a mouseDown and a mouseUp listener to each paragraph (in addition to the one already applied to the top-level container). The listeners will, in essence, record the paragraphs that the selection range starts and ends at, making it much simpler to reliably work out which paragraph(s) are selected.
If ever there was a case in favor of using dynamic event binding through a framework like jQuery, this is it.