I want to highlight the text of div tag.
It works when There is a single line but does not work for multiple:
function SelectChar(el, iStart, iLength) {
var div = el
if (document.createRange) {
var textNode = div.firstChild;
if (textNode.data.length > 1) {
var rangeObj = document.createRange();
rangeObj.setStart(textNode, iStart);
rangeObj.setEnd(textNode, iLength);
selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(rangeObj);
}
}
}
where textbox is content editable div tag.
UPDATE
<div contenteditable="true" id="textbox">The
text
is given
and</div>
Any suggestion please?
This is the plugin auto.js that is used to suggest the word..in that I want to do selection portion for multiple line text in div tag
AutoSuggestControl.prototype.typeAhead = function (sSuggestion /*:String*/) {
// debugger
//check for support of typeahead functionality
var range = document.createRange();
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
if (selection) {
var lastSpace = this.textbox.innerText.lastIndexOf(" ");
var lastQuote = this.textbox.innerText.lastIndexOf("'");
var lastHypen = this.textbox.innerText.lastIndexOf("-");
var lastDoubleQuote = this.textbox.innerText.lastIndexOf('"');
var lastEnter = this.textbox.innerText.lastIndexOf("\n");
var lastIndex = Math.max(lastSpace, lastEnter, lastQuote, lastHypen, lastDoubleQuote) + 1;
var contentStripped = this.textbox.innerText.substring(0, lastIndex);
var lastWord = this.textbox.innerText.substring(lastIndex, this.textbox.innerText.length);
this.textbox.innerText = contentStripped + sSuggestion; //.replace(lastWord,"");
var start = this.textbox.innerText.length - sSuggestion.replace(lastWord, "").length;
var end = this.textbox.innerText.length;
SelectChar(document.getElementById("textbox"), start, end);
}
};
jsfiddle
Related
I am working on a markdown editor in HTML and JS, which previously used textarea but I started moving it to a contenteditable div recently due to the limitations it had, tried a lot but I cannot get it working, I just need a basic function to insert markdown ** around a selection, but there seems to be something wrong with contenteditable divs, anyone has any idea how to fix?
function editorInsertFormatting(txtarea, text) {
var selectStart = txtarea.selectionStart;
var selectEnd = txtarea.selectionEnd;
var scrollPos = txtarea.scrollTop;
var caretPos = txtarea.selectionStart;
var front = txtarea.value.substring(0, caretPos);
var back = txtarea.value.substring(
txtarea.selectionEnd,
txtarea.value.length
);
var middle = txtarea.value.substring(caretPos, txtarea.selectionEnd);
txtarea.value = front + text + middle + text + back;
if (selectStart !== selectEnd) {
txtarea.selectionStart = selectStart + text.length;
txtarea.selectionEnd = selectEnd + text.length;
} else {
txtarea.selectionStart = selectStart + text.length;
txtarea.selectionEnd = txtarea.selectionStart;
}
txtarea.focus();
txtarea.scrollTop = scrollPos;
editorLiveParser();
}
Take a look at window.getSelection(), selection.getRangeAt(), and document.execCommand, but it's a very big mountain you're going to climb.
Here's a simple starting point...
var sel = window.getSelection();
var range = sel.getRangeAt(0);
var range0 = range.cloneRange();
range.collapse(true);
document.execCommand('insertText', false, '!!!START!!!');
sel.removeAllRanges();
sel.addRange(range0);
range0.collapse(false);
document.execCommand('insertText', false, '!!!END!!!');
See https://jsfiddle.net/Abeeee/o0e481q3/1/ for a running example
var input = document.getElementById("div_id");
var username = "TEST";
input.focus();
var selection = window.getSelection();
var range = selection.getRangeAt(0);
var span = document.createElement('span');
span.setAttribute('class', 'tagged-user');
span.setAttribute('id', 55);
span.innerHTML = username;
//var initialText = input.textContent;
//var before_caret = initialText.slice(0,getCaretPosition(input));
// var after_caret = initialText.slice(getCaretPosition(input), initialText.length);
// // var before_caret = input.textContent;
// console.log("before_caret " + before_caret);
// *******************************
//this is the regex that match last #something before caret and it work good.
//var droped_at = before_caret.replace(/#\w+(?!.*#\w+)/g, '');
// *******************************
// input.textContent = droped_at + after_caret;
// console.log("before_caret " + before_caret);
range.deleteContents();
range.insertNode(span);
range.collapse(false);
range.insertNode(document.createTextNode("\u00a0"));
// cursor at the last
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
.tagged-user {
color: #1e81d6;
font-weight: bold;
}
<div contenteditable="true" id="div_id">
First person <span class="tagged-user" id="1">#Joe </span> and #te and <span class="tagged-user" id="2">#Emilie</span> want to go to the beach.
</div>
An example of a text:
hello man, #te #emilie how are you?( #emilie is a span tag)
In this example if the cursor is after #te, I want to replace #te by TEST.
Hello,
I am trying to make a facebook like mention system. I'm able to trigger a message that shows a list of all my contacts as soon as I type "#".
Everything is working fine, the only problem is when I type "#te" for finding "TEST" in the list, it should be able to replace "#te" with "TEST". It inputs #teTEST, any clue how to do it ? I'm using a div with contenteditable.
Thanks a lot.
It seems to be sooo complicated to get/set caret position in a content-editable div element.
Anyway, I created a working snippet with the functions I found in these topics…
Get caret position in contentEditable div
Set Caret Position in 'contenteditable' div that has children
… and added a function oninput to make the replacement you wanted:
(See comments in my code for more details)
var input = document.getElementById("div_id");
var replaced = "#te"; // TAKIT: Added this variable
var replacer = "#TEST"; // TAKIT: Renamed this variable
var replacer_tags = ['<span class="tagged-user" id="0">', '</span>']; // TAKIT: Added after comment
input.focus();
// TAKIT: Added functions from these solutions:
// https://stackoverflow.com/questions/3972014/get-caret-position-in-contenteditable-div
function GetCaretPosition(element) {
var caretOffset = 0;
var doc = element.ownerDocument || element.document;
var win = doc.defaultView || doc.parentWindow;
var sel;
if (typeof win.getSelection != "undefined") {
sel = win.getSelection();
if (sel.rangeCount > 0) {
var range = win.getSelection().getRangeAt(0);
var preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(element);
preCaretRange.setEnd(range.endContainer, range.endOffset);
caretOffset = preCaretRange.toString().length;
}
} else if ( (sel = doc.selection) && sel.type != "Control") {
var textRange = sel.createRange();
var preCaretTextRange = doc.body.createTextRange();
preCaretTextRange.moveToElementText(element);
preCaretTextRange.setEndPoint("EndToEnd", textRange);
caretOffset = preCaretTextRange.text.length;
}
return caretOffset;
}
// https://stackoverflow.com/questions/36869503/set-caret-position-in-contenteditable-div-that-has-children
function SetCaretPosition(el, pos) {
// Loop through all child nodes
for (var node of el.childNodes) {
if (node.nodeType == 3) { // we have a text node
if (node.length >= pos) {
// finally add our range
var range = document.createRange(),
sel = window.getSelection();
range.setStart(node, pos);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
return -1; // we are done
} else {
pos -= node.length;
}
} else {
pos = SetCaretPosition(node, pos);
if (pos == -1) {
return -1; // no need to finish the for loop
}
}
}
return pos; // needed because of recursion stuff
}
// TAKIT: Added this whole function
input.oninput = function() {
var caretPos = GetCaretPosition(input); // Gets caret position
if (this.innerHTML.includes(replaced)) { // Filters the calling of the function
this.innerHTML = this.innerHTML.replace(replaced, replacer_tags.join(replacer) + ' '); // Replace
caretPos += replacer.length - replaced.length + 1; // Offset due to strings length difference
SetCaretPosition(input, caretPos); // Restores caret position
}
// console.clear(); console.log(this.innerHTML); // For tests
}
.tagged-user {
color: #1e81d6;
font-weight: bold;
}
<div contenteditable="true" id="div_id">
First person <span class="tagged-user" id="1">#Joe</span> and (add "te" to test) # and <span class="tagged-user" id="2">#Emilie</span> want to go to the beach.
</div>
Hope it helps.
I am trying to get the closest parent element of the selected text, it works if the selected text hasn't been replaced, but it fails after the text replacement.
Here is the code (I modified from other source):
jQuery(document).ready(function($){
$("#getparent").on("click touch", function(){
var selection, elements = [], ranges = [], rangeCount = 0, parent_id, parent_class;
if (window.getSelection){
selection = window.getSelection();
if (selection.rangeCount) {
var i = selection.rangeCount;
while (i--) {
selectionrange = selection.getRangeAt(i);
ranges[i] = selectionrange.cloneRange();
// SELECTION START'S CONTAINER
var parentDataStart = selectionrange.startContainer.childNodes[ranges[i].startOffset];
parentDataStart = parentDataStart || selectionrange.startContainer.parentNode;
var parentTagStart = parentDataStart.tagName.toLowerCase(); // parent tag name of selection
console.log(parentTagStart);
alert(parentTagStart);
// SELECTED TEXT NODE
selectedtext = selection.toString();
elements[i] = document.createTextNode(selectedtext);
ranges[i].deleteContents();
ranges[i].insertNode(elements[i]);
ranges[i].selectNode(elements[i]);
}
// Restore text selection
selection.removeAllRanges();
i = ranges.length;
while (i--) {
selection.addRange(ranges[i]);
}
}
}
});
});
And Here is A Fiddle
DEMO
the node selection boundary should be moved to the node's content.
So use selectNodeContents instead.
in the case of that question, it should be:
ranges[i].selectNodeContents(elements[i]); instead of ranges[i].selectNode(elements[i]);
Update:
Fiddle Demo
I understand that there is a CSS pseudo-element ::seleсtion. But nevertheless, I need it, because I learn JavaScript. Here's the code to which I came, but it does not work:
document.body.addEventListener('mouseup', function() {
var txt = window.getSelection().toString();
var txtNode = document.createTextNode(txt);
var rng = document.createRange();
rng.setStart(txtNode, 0);
rng.setEnd(txtNode, txt.length);
var span = document.createElement('span');
span.style.backgroundColor = 'green';
rng.surroundContents(span);
}, false);
Until that happened only highlight. What more do to when clicking on an empty space allocation reset?
document.body.addEventListener('mouseup' , function(){
var selection = window.getSelection();
var range = selection.getRangeAt(0);
var span = document.createElement('span');
span.style.backgroundColor = 'green';
range.surroundContents(span);
} , false);
I want to get all text from the selected text to end of page, that mean just the text we see on page. But when i try this, it also get text in tag 'script', 'noscript'... and other tags which not show on page:
function getTextFromCursor(){
count=0;
var allText ="";
if(window.getSelection){
var selection = window.getSelection();
var selRange = selection.getRangeAt(0);
var range = document.createRange();
range.setStart(selRange.startContainer, selRange.startOffset);
var theBody = document.getElementsByTagName('body')[0];
var lastEl = theBody.lastElementChild;
range.setEndAfter(lastEl);
allText = range.toString();
}
return allText;
}
How could i only get the text which show on page ?
The TextRange module of my Rangy library could help. It allows you to work on text as the user sees it on the page, more or less. With it, your example could be achieved with
function getTextFromCursor() {
var selection = rangy.getSelection();
var selRange = selection.getRangeAt(0);
var range = rangy.createRange();
range.selectNodeContents(document.body);
range.setStart(selRange.startContainer, selRange.startOffset);
return range.text();
}
Add the range to the selection, and use selection.toString() instead.
selection.addRange(range);
allText = selection.toString();