Change the color of text selection - javascript

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

Related

How to clear background color of previously seelected text?

I have div element with multiple lines of text. User can select texts using the mouse. I have javascript that sets the background color of the selected text when user click on a btnChangeColor button.
$(function(){
var result = [];
$("#btnChangeColor").click(function () {
let currentColor = "red";
if (window.getSelection()) {
var sel = window.getSelection();
var range = sel.getRangeAt(0);
var selectedText = range.toString();
var startOffset = range.startOffset;
var endOffset = startOffset + selectedText.length;
var obj = {
Id: "SomeUniqueIdPerSelection",
start: startOffset,
end: endOffset,
text: selectedText
}
result.push(obj);
document.designMode = "on";
//if (range) {
// sel.removeAllRanges();
// sel.addRange(range);
//}
// Colorize text
document.execCommand("BackColor", false, currentColor);
// Set design mode to off
document.designMode = "off";
}
})
})
The scripts stores the selection with unique id in an array.
There is another button on the same page. When user click, I want to clear the background color of the particular selection. Assume that SomeUniqueIdPerSelection is supplied as parameter
$("btnClearBtn").click(function(){
let id = GetSelectionId();
let selected = result.find(x=>x.Id === id);
//How do i clear the background color of this selection
})

Deleting background color from one word in a highlighted sentence

I can highlight text by creating a span but when I go to create a span in that highlighted span text with background color transparent, it inherits the parent tag background color still instead of adopting a background color of transparent.
The text will, however, adopt any color that is not transparent.
I'm basing my highlighting of text off of: http://help.dottoro.com/ljcvtcaw.php
and more specifically http://help.dottoro.com/external/examples/ljcvtcaw/execCommand_4.htm.
I've also used document.execCommand('ForeColor', false, yellow) in the past which was really intuitive and would delete or combine span tags but was never able to delete the span tags created in that way.
function Highlight(color) {
document.designMode = "on";
var sel = window.getSelection();
sel.removeAllRanges();
var range = document.createRange();
range.setStart(editor.startContainer, editor.startOffset);
range.setEnd(editor.endContainer, editor.endOffset);
sel.addRange(range);
if (!sel.isCollapsed) {
var range = sel.getRangeAt(0);
var startContainer = range.startContainer;
var startOffset = range.startOffset;
var endContainer = range.endContainer;
var endOffset = range.endOffset;
if(startContainer == endContainer){
ColorizeNodeFromTo(startContainer, color, startOffset, endOffset);
}
else {
if(startContainer.firstChild){
var startLeaf = startContainer.childNodes[startOffset];
}
else {
var startLeaf = GetNextLeaf(startContainer);
ColorizeLeafFromTo(startContainer, color, startOffset, -1);
}
if(endContainer.firstChild){
if(endOffset > 0){
var endLeaf = endContainer.childNodes[endOffset-1];
}
else{
var endLeaf = GetPreviousLeaf(endContainer);
}
}
else{
var endLeaf = GetPreviousLeaf(endContainer);
ColorizeLeafFromTo(endContainer, color, 0, endOffset);
}
while(startLeaf){
var nextLeaf = GetNextLeaf(startLeaf);
ColorizeLeaf(startLeaf, color);
if(startLeaf == endLeaf){
break;
}
startLeaf = nextLeaf;
}
}
}
Solution
I've gone back and refactored my code using the execCommand function.
if (!sel.isCollapsed) {
if (!document.execCommand("HiliteColor", false, color)) {
document.execCommand("BackColor", false, color);
}
}
This dynamically creates and deletes spans how I want.

How to replace a custom text before caret in a contenteditable div that contains many html tags?

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.

Hightlight the multiple line text of div

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

contentEditable div no focus after text replace

I have a contentEditable div which in some cases replaces text with images. It works but when it replaces specific text with an image the div lose focus, and I can't write anything in, how to make it to work?
obj.onfocus = function() {Replace(this)};
obj.onblur = function() {Replace(this)};
obj.onkeyup = function() {Replace(this)};
obj.onkeydown = function() {Replace(this)};
obj.onclick = function() {Replace(this)};
function Replace(element)
{
element.innerHTML = element.innerHTML.replace(/some_text/gi, '<img src="image.." />')
}
contentEditable areas seem to be buggy when replacing the innerHTML with actual HTML elements directly. Instead, you'll have to do low-level node replacement with a created element:
function Replace(element) {
var someText = 'some_text',
index, child, img;
// traverse nodes
for (var i = 0, il = element.childNodes.length; i < il; i++) {
child = element.childNodes[i];
// if an element node
if (child.nodeType == 1) {
// recurse
Replace(child);
// if a text node
} else if (child.nodeType == 3) {
index = child.data.indexOf(someText);
if (index >= 0) {
// create img element
var img = document.createElement('img'),
sel, rng;
img.src = "src.gif";
// split target text into its own node and replace with img
child.splitText(index);
child.nextSibling.splitText(someText.length);
child.parentNode.replaceChild(img, child.nextSibling);
// BONUS CODE!
// move cursor to the end of the editable area
if (document.createRange) {
rng = document.createRange();
rng.selectNodeContents(element);
rng.collapse(false);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(rng);
} else if (document.selection) {
rng = document.body.createTextRange();
rng.moveToElementText(element);
rng.collapse(false);
rng.select();
}
}
}
}
}
See demo

Categories