How to set caret position in contenteditable span and then sendKeys? - javascript

<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);

Related

setting caret (cursor) inside bold element present inside contenteditable paragraph

I have been trying to build a web based text editor. And as part of the process, I am trying to dynamically create and modify elements based and keystroke events for font editing. In this particular jsfiddle example I'm trying to create a strong element upon hitting CTRL+b and setting the focus/caret inside the strong element so that subsequent text entered will be part of the bold element and hence will have bold text. But my code is just creating a strong element but not transferring the focus hence no text is getting bolder.
In the below code I'm creating event listener to capture keystroke events
p=document.getElementsByTagName("p")[0];
//console.log(p)
// adding eventlistener for keydown
p.addEventListener("keydown",listener);
// eventlistenerr callback function
function listener(){
e=window.event;
if(e.ctrlKey && e.keyCode==66)
{
console.log("CTRL+B");
// creating bold element
belm=document.createElement("strong");
belm.setAttribute("contenteditable","true")
p.appendChild(belm);
//bug below
// setting focus inside bold element
setfocus(belm,0);
e.preventDefault();
}
}
Here is the function for setting the focus.
function setfocus(context, position){
var range = document.createRange();
position =position || 0;
var sel = window.getSelection();
range.setStart(context, position);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
context.focus();
}
However, I have not doubt that the function which sets focus is faulty, because in the fiddle if you observe, I have created a separate setup just to test this
out. Click on the button "Click Here" and the focus dynamically shifts to paragraph element without any hassle. I am unable to figure out what is going wrong.
It's pretty much impossible to move the cursor into an empty element in a contenteditable div. However, as shay levi suggested in another post, you can insert the zero-width character &#200B into your empty element to give it an index value.
Here's an example*:
function insertNode(nodeName) {
var sel = window.getSelection(),
range;
range = sel.getRangeAt(0);
range.deleteContents();
var child = document.createElement(nodeName);
child.innerHTML = '​';
range.insertNode(child);
}
var div = document.querySelector('div'),
btn = document.querySelector('button');
btn.addEventListener('click', function() {
insertNode('strong');
div.focus();
});
div.focus();
<div contenteditable></div><button><strong>B</strong></button>
*For the sake of simplicity, this script doesn't toggle bold text, it only sets it.

Select last word or specific range of Contenteditable

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.

How to set caret to the last character of Range object in contenteditable?

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

Inserting caret after an inserted node

So I have a method that takes a tag and wraps the selected text in that tag.
function wrap(tag)
{
var sel, range;
if (window.getSelection)
{
sel = window.getSelection();
if (sel.rangeCount)
{
range = sel.getRangeAt(0);
var selectedText = range.toString();
range.deleteContents();
range.insertNode(document.createTextNode('['+tag+']'+selectedText+'[/'+tag+']'));
}
}
}
This issue with this however, is after it's done wrapping the text and inserting the node the caret (where they are typing) is placed BEFORE the inserted text.
Is there such way to insert the text and have the caret remain at the end of it?
Please note i'd prefer if this could be done without the use of jQuery or any other library. I only need it to work in webkit (Safari).
You can use the range.setStartAfter and range.setEndAfter methods to set the start and end points to the point directly after your new node. I setup a jsfiddle example here: http://jsfiddle.net/phil_mcc/tM3mA/
//move the caret
range.setStartAfter(newNode);
range.setEndAfter(newNode);
sel.removeAllRanges();
sel.addRange(range);
do this after inserting the node to the range
range.collapse(false);
this will change position of selection range to the end of the range, so my guess is it should set the cursor at end position

Move cursor to placeholder element in a contentEditable DIV

I have a contentEditable DIV and, when the user presses a key, the underlying code is processed and replaced by updated code. Alas, this causes the cursor position to be lost.
However, in order to preserve the cursor position, I am successfully inserting a <span id="placeholder"></span> into the DIV at the correct position before processing begins. This preserves the cursor's intended position, but now I can't seem to set the range to select it.
Here's what I currently have:
function focusOnPlaceholder() {
var placeholder = document.getElementById('placeholder');
if( !placeholder ) return;
var sel, range;
if (window.getSelection && document.createRange) {
range = document.createRange();
range.selectNodeContents(placeholder);
range.collapse(true);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(placeholder);
range.select();
}
}
Any help would be appreciated, and a cross-browser solution would be incredible :)
A cross-browser solution would be to use my Rangy library and specifically the selection save/restore module, which uses a similar placeholder technique and is well tested. However, this can probably be fixed without using a library by putting some content (for example, a non-breaking space (\u00A0 or in HTML) inside your placeholder element. You may want to remove the placeholder in focusOnPlaceholder() after selecting the range.

Categories

Resources