contenteditable div is showing raw html instead of rendered html - javascript

When I replace the user selection in a contenteditable div using this function below, then the "new" HTML appears as text (raw HTML) instead of rendered HTML in the div.
In other words, the <h2> tag is being inserted as <h2>, so the display is <h2> instead of a "html heading line".
What am I doing wrong?
function replaceWYS() {
var sel, range;
if (window.getSelection) {
sel = window.getSelection();
selT = window.getSelection().getRangeAt(0);
replacementText = "<h2>" + selT + "</h2>";
if (sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
range.insertNode(document.createTextNode(replacementText));
}
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.text = replacementText;
}
}
<div id="wys" contenteditable>
This is line<br><br> This line should be heading after replacement
<br><br> Select it and click the replace button
</div>
<p><button onclick="replaceWYS(); return false;"> Replace now </button></p>

document.createTextNode renders your HTML as text (if you log it you get "<h2>...</h2>" instead of <h2>...</h2>). You can create an HTML element manually.
function replaceWYS() {
var sel, range;
if (window.getSelection) {
sel = window.getSelection();
selT = window.getSelection().getRangeAt(0);
const h = document.createElement("h2");
h.innerText = selT;
if (sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
range.insertNode(h);
}
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.text = replacementText;
}
}
<div id="wys" contenteditable>
This is line<br><br> This line should be heading after replacement
<br><br> Select it and click the replace button
</div>
<p><button onclick="replaceWYS(); return false;"> Replace now </button></p>

Related

Caret tracking in a ContentEditable or a Textarea copied to. Account for child div, span, img, p, br HTML tags and more

I'm dabbling in jquery and javascript to get done what I need. At the heart of the problem is an emoji picker that I coded in PHP. It inserts emoji just fine, but it doesn't insert at the caret position. Every script I've found somehow fails to account for the html tags. The count is always wrong, so where the emoji is inserted can vary, sometimes in the middle of an html tag, which turns the editor box into a horrible hurricane-stricken site.
<script>
/* VERSION 2 8/21/2022 */
function getCharacterOffsetWithin_final(range, node) {
var treeWalker = document.createTreeWalker(
node,
NodeFilter.ELEMENT_NODE,
function(node) {
var nodeRange = document.createRange();
nodeRange.selectNodeContents(node);
return nodeRange.compareBoundaryPoints(Range.END_TO_END, range) < 1 ?
NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
},
false
);
var charCount = 0, lastNodeLength = 0;
if (range.startContainer.nodeType == 3) {
charCount += range.startOffset;
}
while (treeWalker.nextNode()) {
charCount += lastNodeLength;
lastNodeLength = 0;
if(range.startContainer != treeWalker.currentNode) {
if(treeWalker.currentNode instanceof Text) {
lastNodeLength += treeWalker.currentNode.length;
} else if(treeWalker.currentNode instanceof HTMLBRElement ||
treeWalker.currentNode instanceof HTMLImageElement ||
treeWalker.currentNode instanceof HTMLSpanElement ||
treeWalker.currentNode instanceof HTMLDivElement)
{
lastNodeLength++;
}
}
}
return charCount + lastNodeLength;
}
var update = function() {
var el = document.getElementById("interim");
var range = window.getSelection().getRangeAt(0);
$(\'#cursorPos\').text(getCharacterOffsetWithin_final(range, el));
};
$("#commentsbox").on("mouseup keyup", update);
/* VERSION 2 END 8/21/2022 */
var interim = function() {
$(\'#interim\').val($(this).text());
};
$(\'#commentsbox\').on("mousedown mouseup keydown keyup", interim);
<div class="input" role="textbox" contentEditable="true" data-placeholder="Comment here..." name="comment-content" style="word-break: break-word;word-wrap: break-word;font-size:x-large;padding:5px;text-align:left;display:inline-block;min-height:100px;width:95%;border:solid 0.75px '.$bordercolor.';" id="commentsbox" class="'.$_SESSION['theme'].'"></div>
<div id="cursorPos"></div>
<textarea id="interim" style="display:none;"></textarea>
<div style="position:relative;display:inline-block;display:none;padding:5px;text-align:left;min-height:20px;min-width:95%;border-left:solid 0.75px '.$bordercolor.';border-right:solid 0.75px '.$bordercolor.';" id="menubox" class="hiddencontrols">
<span style="display:inline-block;height:100%;width:50px;" class="material-icons emojilist_tog"><img src="https://rellawings.com/emoji/blobdrool2.png" height="24px" \>arrow_drop_down</span><span class="material-icons" style="display:inline-block;padding-left:10px;padding-right:10px;">format_quote</span><span style="display:inline-block;"><img src="https://rellawings.com/images/spoiler.png" height="20px" /></span>
</div>
<div class="emojilist" style="display:none;width:300px;max-height:200px;position:absolute;z-index:1000000;text-align:center;">
<div style="display:inline-block;font-weight:bolder;font-size:large;text-align:center;background-color:#292929;width:280px;padding:10px;">Emoji</div>
<div style="position:relative;padding-left:8px;text-align:center;background-color:#1e1e1e;padding-top:12px;padding-bottom:12px;max-height:150px;overflow-x:hidden;overflow-y:scroll;">';
$emojifullpath = '/home/gwingdivineknight/rellawings.com/emoji/';
$emojidir = 'https://rellawings.com/emoji/';
$emojifiles = array_diff(scandir($emojifullpath), array('..', '.')); $i = 1;
foreach ($emojifiles as &$value) {
$cleanname = basename($value,".png");
$body .= '<span style="display:inline-block;padding:3px;"><img src="'.$emojidir.$value.'" height="24px" id="emoji-'.$i.'" /></span>';
$body .= '
<script>
$(document).ready(function(){
$("#emoji-'.$i.'").click(function(event){
var emoimg = "<span style=\'display:inline;padding:4px;position:relative;bottom:-4px;\'><img src=\'https://rellawings.com/emoji/'.$value.'\' title=\':'.$cleanname.':\' alt=\':'.$cleanname.':\' height=\'24px\' /></span>";
/* Proven To Work Except for Positioning */
/*$(\'#commentsbox\').append(emoimg);
var TotalContents = $(\'#commentsbox\'),
fix = TotalContents.html().replaceAll("<br>", "");
TotalContents.html(fix);*/
cursorPos = $(\'#cursorPos\').html();
var v = $(\'#commentsbox\').html();
var textBefore = v.substring(0, cursorPos );
var textAfter = v.substring( cursorPos, v.length );
$(\'#commentsbox\').html(textBefore+ emoimg +textAfter);
});
});
</script>
';
$i++;
}
$body .= '
</div>
</div>
<div style="display:inline-block;display:none;padding:5px;text-align:right;min-height:40px;min-width:95%;position:relative;border:solid 0.75px '.$bordercolor.';" id="buttonsbox" class="hiddencontrols">
<span style="display:inline-block;padding:5px;background-color:#292929;border-radius:7px;margin-right:4px;margin-bottom:6px;margin-top:6px;
white-space:nowrap;color:#ffffff;cursor: pointer;position:relative;width:67px;" id="cancelbtn"><span class="material-icons" style="position:absolute;left:2px;top:2px;">clear</span>Cancel</span>
<span style="display:inline-block;padding:5px;background-color:#292929;border-radius:7px;margin-right:4px;margin-bottom:6px;margin-top:6px;
white-space:nowrap;color:#ffffff;cursor: pointer;position:relative;width:53px;" id="postbtn"><span class="material-icons" style="position:absolute;left:2px;top:2px;">create</span>Post</span>
</div>
I'm at the end of my patience with attempting to get my emoji picker + contenteditable div work. The functions that I find don't return a perfect number of characters in tags and outside, so it's not able to place emoji accurately into the contenteditable div.
Thanks in advance for your help!
The answer was found! This lovely bit of code was contributed by a friend. Thanks to everyone for all the wonderful assistance! Here's the lovely solution! I'm sure it will help many people who get frustrated over the same sort of situation, looking for a simpler solution! <3
<script>
function pasteHtmlAtCaret(emojiHtml) {
let sel, range;
if (window.getSelection) {
// IE9 and non-IE
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
// Range.createContextualFragment() would be useful here but is
// non-standard and not supported in all browsers (IE9, for one)
const el = document.createElement("div");
el.innerHTML = emojiHtml;
let frag = document.createDocumentFragment(),
node,
lastNode;
while ((node = el.firstChild)) {
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
// Preserve the selection
if (lastNode) {
range = range.cloneRange();
range.setStartAfter(lastNode);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
}
} else if (document.selection && document.selection.type != "Control") {
// IE < 9
document.selection.createRange().pasteHTML(emojiHtml);
}
}
function addToCommentsBox(emojiHtml) {
const chatBox = document.getElementById("commentsbox");
chatBox.focus();
pasteHtmlAtCaret(emojiHtml);
}
</script>

how to get the correct caret position

I'm making a contenteditable based input and I want to add emoticon support
i have the contenteditable div, and inside is only text and IMG_TAG for emoticons. On blur, I want to save the caret position. And when I click to open the emoticon box to focus the contenteditable and set the caret where it was. Something to simulate web.whatsapp.com behaviour
I've found this piece of code
function getCaretCharacterOffsetWithin(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;
}
But it does not give me the correct position.
"hello IMG_TAG john". If the cursor is just before the IMG_TAG -> the position is 6, which is correct, but if it is just after the IMG_TAG is still 6.
Also, I didn't find a good "setCaretPosition" function to work well for me.
I've been searching for two hours now. Anyone got a good answer ?
In the demo we have used the Selection and Range APIs for for setting and gettingšŸ—” the caret's position. Since the OP needs to find a caret's position, we will concentrate on the getCaret() function:
var btn2 = document.getElementById('btn2');
btn2.addEventListener('click', getCaret, false);
function getCaret() {
var sel = window.getSelection();
var end = sel.focusOffset;
var out1 = document.getElementById('out1');
out1.textContent = end;
}
The function references the Selection object
Then you store the Selection object's property, focusOffset
focusOffset will give us an integer representing the number of characters there are between it (focusNode) and the beginning of the Selection (anchorNode). If the Selection contains an element, then it includes the element's childNodes into the count as well.
When testing the getCaret() function, just select some text, then click the Get Focus button, the result is displayed next to the area labeled Caret Position:
SNIPPET
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<style>
fieldset {
margin: 20px auto;
}
input {
width: 5ex;
text-align: center;
padding: 1px 3px;
}
</style>
</head>
<body>
<div id="editor1" contenteditable="true">
<br/>123456789abcdefghijklmnopqrstuvwxyz
<br/>12 45678 abcd fg ijk mnopqrst vwxyz
<br/>123 5 789a cde ghijklm o qrst v xyz
<br/>12345 789abcd fg ijklmnopq s u wxyz
<br/>123 567 9abc efg ijklm op rst vwxy
<br/>1 34 6789a cdef hij lm opq stuvwxyz
<br/>1 3456 89a cd fghij l no qrst vwx z
<br/>12 4 6789 bcd fgh j lmno q stuv xy
</div>
<fieldset>
<input type="button" class="fontStyle" onclick="document.execCommand('italic',false,null);" value="I" title="Italicize Highlighted Text">
<input type="button" class="fontStyle" onclick="document.execCommand('bold',false,null);" value="B" title="Bold Highlighted Text">
<input id="row" name="row" placeholder="Row#" />
<input id="col" name="col" placeholder="Col#" />
<button id="btn1">Set Focus</button>
<button id="btn2">Get Focus</button>
<label for='out1'>Caret Position:</label>
<output id="out1"></output>
</fieldset>
<p>Each row has a number of non-repeated string of alphanumeric characters:</p>
<pre><code>123456789abcdefghijklmnopqrstuvwxyz</code></pre>
<p>Maximum of 35 characters. Every row except the first row has some missing characters and in their place is a whitespace.
</p>
<script>
function setCaret(x, y) {
var ele = document.getElementById("editor1");
var rng = document.createRange();
var sel = window.getSelection();
var row = parseInt(x, 10) * 2;
var col = parseInt(y, 10);
rng.setStart(ele.childNodes[row], col);
rng.collapse(true);
sel.removeAllRanges();
sel.addRange(rng);
ele.focus();
}
function getCaret() {
var sel = window.getSelection();
var end = sel.focusOffset;
var out1 = document.getElementById('out1');
out1.textContent = end;
}
var btn2 = document.getElementById('btn2');
var btn1 = document.getElementById('btn1');
btn1.addEventListener('click', function(event) {
var rowSet = document.getElementById('row').value;
var colSet = document.getElementById('col').value;
setCaret(rowSet, colSet);
}, true);
btn2.addEventListener('click', getCaret, false);
</script>
</body>
</html>
šŸ—”Just figured it out after trying to remember how setCaret() function worked.

Get and set cursor position with contenteditable div

I have a contenteditable div which I would like to be able to have users insert things such as links, images or YouTube videos. At the moment this is what I have:
function addLink() {
var link = $('#url').val();
$('#editor').focus();
document.execCommand('createLink', false, link);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- Text Editor -->
<div id="editor" contenteditable="true"></div>
<!-- Add Link -->
<input type="text" id="url">
<button onclick="addLink()">Submit</button>
As you can see, the user has to type into a separate text box to enter the link address. As a result, when the link is added to the editor, it is not added to the position that the pointer/caret was on.
My question is how I can get and set the location of the pointer/caret. I have seen other questions such as this for setting the pointer however I would prefer to have a solution which is supported in all modern browsers, including Chrome, Safari, Firefox and IE9+.
Any ideas? Thanks.
Edit:
I found the code below which gets the position however, it only gets the position according to the line it is on. For example if I had this (where | is the cursor):
This is some text
And som|e more text
Then I would be returned the value 7, not 24.
function getPosition() {
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt) {
return sel.getRangeAt(0).startOffset;
}
}
return null;
}
There's a ton of related info onsite. This one works for me and my clients.
DEMO
https://stackoverflow.com/a/6249440/2813224
function setCaret(line, col) {
var ele = document.getElementById("editable");
var rng = document.createRange();
var sel = window.getSelection();
rng.setStart(ele.childNodes[line], col);
rng.collapse(true);
sel.removeAllRanges();
sel.addRange(rng);
ele.focus();
}
//https://stackoverflow.com/a/6249440/2813224
var line = document.getElementById('ln').value;
var col = document.getElementById('cl').value;
var btn = document.getElementById('btn');
btn.addEventListener('click', function(event) {
var lineSet = parseInt(line, 10);
var colSet = parseInt(col, 10);
setCaret(lineSet, colSet);
}, true);
<div id="editable" contenteditable="true">
<br/>text text text text text text
<br/>text text text text text text
<br/>text text text text text text
<br/>
<br/>
</div>
<fieldset>
<button id="btn">focus</button>
<input type="button" class="fontStyle" onclick="document.execCommand('italic',false,null);" value="I" title="Italicize Highlighted Text">
<input type="button" class="fontStyle" onclick="document.execCommand('bold',false,null);" value="B" title="Bold Highlighted Text">
<input id="ln" placeholder="Line#" />
<input id="cl" placeholder="Column#" />
</fieldset>
A good rich-text editor is one of the harder things to do currently, and is pretty much a project by itself (unfriendly API, huge number of corner cases, cross-browser differences, the list goes on). I would strongly advise you to try and find an existing solution.
Some libraries that can be used include:
Quill (http://quilljs.com)
WYSGIHTML (http://wysihtml.com)
CodeMirror library (http://codemirror.net)
I have tried to find a solution,
With a little help it can be perfected.
It is a combination of answers I've found on SO, and my exp.
Its tricky, its messy... but if you must, you can use it but it requires a bit of work to support inner links (if you cursor is on an anchor it will create anchor inside anchor)
Here's the JS:
var lastPos;
var curNode = 0;
function setCaret() {
curNode=0;
var el = document.getElementById("editor");
var range = document.createRange();
var sel = window.getSelection();
console.log(el.childNodes);
if (el.childNodes.length > 0) {
while (lastPos > el.childNodes[curNode].childNodes[0].textContent.length) {
lastPos = lastPos - el.childNodes[curNode].childNodes[0].textContent.length;
curNode++;
}
range.setStart(el.childNodes[curNode].childNodes[0], lastPos);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
el.focus();
};
function savePos() {
lastPos = getCaretCharacterOffsetWithin(document.getElementById('editor'));
}
function addLink() {
console.log(lastPos);
setCaret();
console.log(getCaretCharacterOffsetWithin(document.getElementById('editor')));
console.log('focus');
// $("#editor").focus();
var link = $('#url').val();
document.execCommand('createLink', false, link);
}
function getCaretCharacterOffsetWithin(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;
}
fiddle
This is what you asked for, in your bounty: on the following example you can see how to detect the exact number of characters of the actual point where you clicked the mouse on:
<!-- Text Editor -->
<div id="editor" class="divClass" contenteditable="true">type here some text</div>
<script>
document.getElementById("editor").addEventListener("mouseup", function(key) {
alert(getCaretCharacterOffsetWithin(document.getElementById("editor")));
}, false);
function getCaretCharacterOffsetWithin(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;
}
</script>
I found a problem with emojis. Emojis have a length which is more than 1.
I solved it.
<script>
function getCursorPosition() {
if(window.getSelection()){
var sel = window.getSelection();
if(sel.getRangeAt){
var pos = sel.getRangeAt(0).startOffset;
var endPos = pos + Array.from(editor.innerHTML.slice(0,pos)).length - editor.innerHTML.slice(0,pos).split("").length;
return endPos;
}
}
return null;
}
var editor = document.querySelector("#editor");
var output = document.querySelector("#output");
editor.addEventListener("input",function(){
output.innerHTML = "Selection: " + getCursorPosition();
});
</script>
<div contenteditable id="editor">text</div>
<div id="output">Selection:</div>

javasscript allow writing rtl and ltr in sentence

i am trying to write rtl and ltr in one sentence in contenteditable div,but the whole sentence screwup..
i have tried to stick the cursor to end while writing but the words mirrored
like ybab instead of baby and i cant seprate the words with space
var div = document.getElementById("editor_content");
div.onkeydown = function() {
var sel, range;
if (window.getSelection && document.createRange) {
range = document.createRange();
range.selectNodeContents(div);
range.collapse(true);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(div);
range.collapse(true);
range.select();
}
};
please help me
I dont know exactly what you want to do, but i guess you want to type text from left to right and then have it mirrored from right to left....by words...not by letters.
<div id="content-left" style="min-height: 10em;" contenteditable="true">L </div>
<div id="content-right" style="min-height: 10em;" contenteditable="true">R </div>
var content = document.getElementById("content-left");
var mirror = document.getElementById("content-right");
var update = function() {
}
window.onkeyup = function(evt) {
var text = content.innerText;
var words;
if (!text) {return ;}
words = text.split(" ");
words = words.reverse();
mirror.innerText = words.join( " " );
}
#content-left, #content-right {background: #FAFAFA;padding: 0;margin: 0;width: 49.5%;float: left;}
#content-right {float: right;}
http://jsfiddle.net/rainerpl/asrny8zd/1/

How to Get div id at caret position

How to get div id at caret position in jquery? for example my main div is main-box and mouse position is on <div id="content"></div>
<div id="main-box"> ........
<div id="wrap" class="clearfix"> ............
<div id="header"> ........
<div id="content"> ...............
<div id="section1"> ...... </div>
<div id="section2"> ........ </div>
</div>
</div>
</div>
<script type="text/javascript">
$(document).ready(function(){
$('#main-box').click(function() {
alert(getCaretPosition(this));
});
});
function getCaretPosition(editableDiv) {
var caretPos = 0, containerEl = null, sel, range;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0);
if (range.commonAncestorContainer.parentNode == editableDiv) {
caretPos = range.endOffset;
}
}
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
if (range.parentElement() == editableDiv) {
var tempEl = document.createElement("span");
editableDiv.insertBefore(tempEl, editableDiv.firstChild);
var tempRange = range.duplicate();
tempRange.moveToElementText(tempEl);
tempRange.setEndPoint("EndToEnd", range);
caretPos = tempRange.text.length;
}
}
return caretPos;
}
</script>
I need to get dive id when mouse over or click?
if someone has any idea please share!
Verry easy, http://jsfiddle.net/Ncnq6/1/
Attach the event listener on the document for all div elements. You can replace "mouseenter" by any other handler. $(document).on("mouseover","div",function(e){
Did you tried something like
$(e.currentTarget).attr('id)
inside your event function
You can try below code:
alert($('.YourClass').attr('id'));
Good luck..

Categories

Resources