I am working on my jquery code to highlight on the text to replace it with anchor tag.
When I click on the button to replace the highlight text with the anchor tag, I want to put the cursor at the end of that highlighted text, example: when I have the full text "My own selected text sentence" and I highlight on the text called "selected" then replace it with anchor tag then I want to put the cursor at the end of the "selected" text just before the "text".
When I highlight on the text called Selected and click on the modal, the text will show as this:
<span class="highlight">Selected</span>
So when I put the url in the input textbox and when I click on the ok button, it will replace the text with the anchor tag just like this:
Selected
But I am unable to put the cursor just after the Selected before the text. Here is what I have tried:
highlight_text = '<span class="highlight">' + Text + '</span>';
anchor_tag = '' + Text + '';
$('#replyMessage').html($("#replyMessage").html().replace(highlight_text, anchor_tag));
placeAtEndOfText(document.querySelector('#replyMessage')
Here is the full code:
$(document).on('click', '#quick_insert-link', function(e) {
e.preventDefault();
var selected_text = window.getSelection ? '' + window.getSelection() : document.selection.createRange().text;
if (selected_text) {
highlightSelection();
$('#quick_linkdialog-text').val(selected_text);
}
});
$(document).on('click', '#quick_ok', function(e) {
if ($('#quick_linkdialog-web-button').is(':checked')) {
var selected_text = window.getSelection ? '' + window.getSelection() : document.selection.createRange().text;
var linkURL = $('#quick_linkdialog-onweb-tab-input').val();
var Text = $('#quick_linkdialog-text').val();
$('.dialog_background_cover').remove();
$('#quick_linkdialog').hide();
$('#replyMessage').focus();
if ($('#quick_linkdialog-onweb-tab-input').val().indexOf('http://') == -1) {
//$('#replyMessage').focus();
if(selectedNode != null && selectedNode.nodeName === 'A') {
selectedNode.href = "http://"+linkURL;
selectedNode.innerText = Text;
}
else {
highlight_text = '<span class="highlight">' + Text + '</span>';
anchor_tag = '' + Text + '';
$('#replyMessage').html($("#replyMessage").html().replace(highlight_text, anchor_tag));
placeAtEndOfText(document.querySelector('#replyMessage').find(anchor_tag));
//document.execCommand('insertHTML', null, '' + Text + '');
}
}
}
});
function highlightSelection() {
var userSelection = window.getSelection();
for(var i = 0; i < userSelection.rangeCount; i++) {
highlightRange(userSelection.getRangeAt(i));
}
}
function placeAtEndOfText(el) {
el.focus();
if (typeof window.getSelection != "undefined"
&& typeof document.createRange != 'undefined') {
var range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.body.createTextRange != 'undefined') {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(false);
textRange.select();
}
}
When I tried to use placeAtEndOfText function, it will only put the cursor at the end of the text just after "sentence" for the element #replyMessage. I am unable to put the cursor at the end of the "selected" when I try to use placeAtEndOfText function.
Can you please show me an example how I can put the cursor at the end of the text called selected after when I replaced with the anchor tag?
Thank you.
Related
Im trying to do some basic syntax highlighting within a contenteditable. It works and I can replace the word so that its wrapped in a to style it, but any other text I type stays within those span tags.
Heres my code:
hiliter("Carrot", document.getElementById("myText"));
placeCaretAtEnd(document.getElementById("myText"));
function hiliter(word, element) {
var rgxp = new RegExp(word, 'g');
var repl = '<span style="color:red;">' + word + '</span>';
element.innerHTML = element.innerHTML.replace(rgxp, repl);
console.log(element.innerHTML);
}
function placeCaretAtEnd(el) {
el.focus();
if (typeof window.getSelection != "undefined"
&& typeof document.createRange != "undefined") {
var range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.body.createTextRange != "undefined") {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(false);
textRange.select();
}
}
EDIT: Heres a fiddle showing the issue. If you some text and the word 'test' into the box and click highlight It will highlight the word. But then after that any more text you type stays within those span tags instead of going after the closing tag. You can verify by clicking the show html button.
https://jsfiddle.net/f3wmekLL/
So, what I did is added an input event listener, which removes the highlight by replacing the <span style="color:red;"> and </span> with empty string.
EDIT: Added a fix for Chrome. I modified the event listener to remove all highlights and re-highlight the words. Seems to fix the issue!
function highlight(addEventLis) {
hiliter("test", document.getElementById("textBox"), addEventLis);
placeCaretAtEnd(document.getElementById("textBox"));
}
function showHTML() {
alert(document.getElementById("textBox").innerHTML);
}
function placeCaretAtEnd(el) {
el.focus();
if (typeof window.getSelection != "undefined" &&
typeof document.createRange != "undefined") {
var range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.body.createTextRange != "undefined") {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(false);
textRange.select();
}
}
function hiliter(word, element, addEventLis) {
var rgxp = new RegExp(word, 'g');
var repl = '<span style="color:red;">' + word + '</span>';
element.innerHTML = element.innerHTML.replace(rgxp, repl);
addEventLis && element.addEventListener("input", function remove() {
removeHighlight();
highlight(false);
element.removeEventListener("input", remove);
});
//alert(element.innerHTML);
}
function removeHighlight(e) {
let element = document.getElementById("textBox");
let i = element.innerHTML.replaceAll('<span style="color:red;">', "");
i = i.replaceAll('</span>', "");
element.innerHTML = i;
placeCaretAtEnd(document.getElementById("textBox"))
}
#textBox {
width: 400px;
height: 100px;
border: 1px solid #000;
}
<pre id="textBox" contenteditable="true"></pre>
<input type="button" value="Highlight word 'test'" onclick="highlight(true);" />
<input type="button" value="Show HTML" onclick="showHTML();" />
I have a contenteditable div
<div id="divTest" contenteditable="true">
I need to get the last word from caret position and on certain condition I have to test and remove this specific word only. Below is how am I doing
$('#divTest').on('keyup focus', function (e) {
if (e.keyCode == 32) {
var lastWord = getWordPrecedingCaret(this), spanLastWord = $('#lastWord');
}
});
function getWordPrecedingCaret(containerEl) {
var preceding = "",
sel,
range,
precedingRange;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount > 0) {
range = sel.getRangeAt(0).cloneRange();
range.collapse(true);
range.setStart(containerEl, 0);
preceding = range.toString();
}
} else if ((sel = document.selection) && sel.type != "Control") {
range = sel.createRange();
precedingRange = range.duplicate();
precedingRange.moveToElementText(containerEl);
precedingRange.setEndPoint("EndToStart", range);
preceding = precedingRange.text;
}
var words = range.toString().trim().split(' '),
lastWord = words[words.length - 1];
if (lastWord) {
var resultValue = 'some'; // this value is coming from some other function
if (resultValue == lastWord) {
alert('do nothing');
// do nothing
}
else
{
alert('replace word');
// delete That specific word and replace if with resultValue
}
return lastWord;
}
}
Demo: http://codepen.io/anon/pen/ogzpXV
I have tried range.deleteContents(); but that will delete all the content in the div.
How can I replace specific word only?
To work with Ranges we need to keep in mind that we are working with Nodes, not only the text that is rendered. The structure you want to manipulate is:
<div id="divTest" contenteditable="true"> <-- Element Node
"some text" <-- TextNode
</div>
But it also could be:
<div id="divTest" contenteditable="true"> <-- Element Node
"some text" <-- TextNode
"more text" <-- TextNode
"" <-- TextNode
</div>
To solve your problem is simplier to handle only one TextNode, I propose to use the normalize() function to join all of them into a single one.
Then you only need to set the Range to the word's bounds before deleteContents(). Once deleted, you can insert a new TextNode with the substitution using insertNode().
var wordStart = range.toString().lastIndexOf(lastWord);
var wordEnd = wordStart + lastWord.length;
/* containerEl.firstChild refers to the div's TextNode */
range.setStart(containerEl.firstChild, wordStart);
range.setEnd(containerEl.firstChild, wordEnd);
range.deleteContents();
range.insertNode(document.createTextNode(resultValue));
For this to work, you need that the text is in a single TextNode. But after ìnsertNode the div will contain multiple text nodes. To fix this simply call normalize() to join all TextNode elements.
containerEl.normalize();
Edit:
As Basj points out, the original solution fails for multiline. That's because when hitting ENTER the structure changes from:
<div id="divTest" contenteditable="true"> <-- Element Node
"some text" <-- TextNode
</div>
to something like:
<div id="divTest" contenteditable="true"> <-- Element Node
<div>"some text"</div>
<div>"more text"</div>
</div>
I've updated this answer, but it's also worth to read Basj's answer at this question: Replace word before cursor, when multiple lines in contenteditable
JSFiddle demo or runnable code snippet:
document.getElementById('divTest').onkeyup = function (e) {
if (e.keyCode == 32) {
getWordPrecedingCaret(this);
}
};
function getWordPrecedingCaret(containerEl) {
var preceding = "",
sel,
range,
precedingRange;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount > 0) {
range = sel.getRangeAt(0).cloneRange();
range.collapse(true);
range.setStart(containerEl, 0);
preceding = range.toString();
}
} else if ((sel = document.selection) && sel.type != "Control") {
range = sel.createRange();
precedingRange = range.duplicate();
precedingRange.moveToElementText(containerEl);
precedingRange.setEndPoint("EndToStart", range);
preceding = precedingRange.text;
}
var words = range.toString().trim().split(' '),
lastWord = words[words.length - 1];
if (lastWord) {
var resultValue = 'some'; // this value is coming from some other function
if (resultValue == lastWord) {
console.log('do nothing: ' + lastWord);
// do nothing
} else {
console.log('replace word ' + lastWord);
/* Find word start and end */
var wordStart = range.endContainer.data.lastIndexOf(lastWord);
var wordEnd = wordStart + lastWord.length;
console.log("pos: (" + wordStart + ", " + wordEnd + ")");
range.setStart(range.endContainer, wordStart);
range.setEnd(range.endContainer, wordEnd);
range.deleteContents();
range.insertNode(document.createTextNode(resultValue));
// delete That specific word and replace if with resultValue
/* Merge multiple text nodes */
containerEl.normalize();
}
return lastWord;
}
}
<div id="divTest" contenteditable="true">Write words here and hit SPACE BAR</div>
words = ['oele', 'geel', 'politie', 'foo bar'];
function markWords() {
var html = div.html().replace(/<\/?strong>/gi, ''),
text = html.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' '),
exp;
$.each(words, function(i, word) {
exp = new RegExp('\\b(' + word + ')\\b', 'gi');
html = html.replace(exp, function(m) {
console.log('WORD MATCH:', m);
return '<strong>' + m + '</strong>';
});
});
//html = html.replace(' ', ' ').replace(/\s+/g, ' ');
console.log('HTML:', html);
console.log('----');
div.html(html);
}
Call this function on setinterval
Fiddle
Tobías' solution works well for single-line contenteditable div. But if you add multiple lines, it doesn't work anymore.
Here is a general solution that works for both single-line or multiline contenteditable div.
I want to know how to select Highlighted text using JQuery selector.
For example, to select elements with a class, you use .class, for IDs, you use #id.
What do I use for highlighted text so that I can (for example) hide them:
$("Highlighted text").hide();
What is the highlighted text selector, and how to hide highlighted text?
This is one your are looking for i believe:
text = window.getSelection().toString();
DEMO
Hide selected/highlighted text javascript
You have to get parent of Element from DOM:
function getSelectionParentElement() {
var parentEl = null, sel;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
parentEl = sel.getRangeAt(0).commonAncestorContainer;
if (parentEl.nodeType != 1) {
parentEl = parentEl.parentNode;
}
}
} else if ( (sel = document.selection) && sel.type != "Control") {
parentEl = sel.createRange().parentElement();
}
return parentEl;
}
NEW DEMO
Update
Fixed demo to hide text we have to find startOffset
function getStartOffset() {
var sel = document.selection, range, rect;
var x = 0, y = 0;
if (sel) {
if (sel.type != "Control") {
range = sel.createRange();
range.collapse(true);
}
} else if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0).cloneRange();
if (range.getClientRects) {
range.collapse(true);
}
}
}
return range.startOffset;
}
Updated DEMO
if($("idDiv").html().contains('Highlighted text')==true)
{
var a=$("#idDiv").html();
a=a.replace("Highlighted text","<p id='highlightedtext'>Highlighted text</p>");
$("#idDiv").html(a);
$("#highlightedtext").hide();
}
The above code check the highlighted text from the div and if it found it set that text in p tag with id and using that id you can hide it
how i can to find selected text in a div , in html
for example we have selected from 5 to 11 in this text :
<div id="txt" >This is some <i> text </i> </div>
selected : is some ,
but in html is : id="txt
how to find this and replace between <p> or <span> that other tags to avoid loss of ?
excuse me for my bad english :)
Demo: http://jsfiddle.net/dKaJ3/2/
function getSelectionHtml() {
var html = "";
if (typeof window.getSelection != "undefined") {
var sel = window.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
}
alert(html);
}
Taken from: How to replace selected text with html in a contenteditable element?
Try to find things before you ask ;]
I have this code for replacing selected text: (it putts "1" and "2" before and after selected text):
var content=$("#text").html();
if (window.getSelection) {
// not IE case
var selObj = window.getSelection();
var selRange = selObj.getRangeAt(0);
content2 = content.substring(0,selRange.startOffset) + "1" + content.substring(selRange.startOffset,selRange.endOffset) + "2" + content.substring(selRange.endOffset,content.length);
$("#content").html(content2);
selRange.removeAllRanges();
} else if (document.selection && document.selection.createRange && document.selection.type != "None") {
// IE case
range = document.selection.createRange();
var selectedText = range.text;
var newText = '1' + selectedText + '2';
document.selection.createRange().text = newText;
}
And HTML:
<div id="text">aaa as asd das d</div>
This works well with "pure" text, but if my HTML looks like this (bolded text)
<div id="text">aaa as <b>asd</b> das d</div>
It breaks down in firefox, because selRange.startOffset object is not returning desired location...
And another question... In IE this is working fine with bolded and "normal" text but since for IE I'm not using jquery html() function - text can't be replaced with HTML code. So if I want to use "< b>" and "< /b>" rather than "1" and "2", text would not be bolded like that in firefox.
Can this two problems be fixed?
startOffset and endOffset are offsets in current node, to get it you need range.startContainer and range.endContainer.
EDIT: It is working good if startContainer and endContainer are on the same level (DOM tree structure is preserved).
EDIT2: Now it makes every selected text bold.
Also I rewrote the IE part, now it operates on HTML, so it's good.
http://jsfiddle.net/FYJtN/11/
if (window.getSelection) {
// not IE case
var selObj = window.getSelection();
var selRange = selObj.getRangeAt(0);
var newElement = document.createElement("b");
var documentFragment = selRange.extractContents();
newElement.appendChild(documentFragment);
selRange.insertNode(newElement);
selObj.removeAllRanges();
} else if (document.selection && document.selection.createRange && document.selection.type != "None") {
// IE case
var range = document.selection.createRange();
var selectedText = range.htmlText;
var newText = '<b>' + selectedText + '</b>';
document.selection.createRange().pasteHTML(newText);
}