Problem
Using the very useful Summernote Editor component for Bootstrap, you would like to insert some text at the current caret position programmatically.
Solution
The Summernote API does not provide any dedicated methods for inserting text. However, that’s not a problem since we can use the JQuery/native DOM API to insert text into the editor. Thankfully, the content of the Summernote editor is nothing but vanilla HTML/DOM elements. Thus, we can insert text at the current cursor position as follows (if the Summernote editor is focused):
To Insert at the End of the Current Paragraph
$(document.getSelection().anchorNode.parentNode).append(“appended!”);
To Insert at the Current Cursor Position
var selection = document.getSelection();
var cursorPos = selection.anchorOffset;
var oldContent = selection.anchorNode.nodeValue;
var toInsert = "InsertMe!";
var newContent = oldContent.substring(0, cursorPos) + toInsert + oldContent.substring(cursorPos);
selection.anchorNode.nodeValue = newContent;
Note: You probably will have to work some magic with the document.getSelection() call. The problem is that once you would click a button or trigger the action in some other way, the selection would change. Thus, I save a reference to the document.getSelection() upon every focus and key press event on the editor.
Insert at Current Position (Alternative)
As suggested by Dexter in the comments below, you can also insert text as follows:
$(‘#summernote’).summernote('editor.saveRange'); // Editor loses selected range (e.g after blur) $(‘#summernote’).summernote('editor.restoreRange'); $(‘#summernote’).summernote('editor.focus'); $(‘#summernote’).summernote('editor.insertText', 'This text should appear at the cursor');
References
Stackoverflow – Get caret position in contentEditable div
Stackoverflow – Inserting Text at Cursor Position using JS/JQuery
Thank you sir, solved alot. now i can use summernote as framework for editor and build a custom buttons and actions to insert my own content.
This is really cool. Thanks for posting.
This works great with plain text, but I’ve run into a problem using this technique when inserting into a rich text document. It looks like the additional HTML formatting is throwing off the math.
Have you had this same problem? And, any suggestions on working around it?
Did you find a solution to your problem yet? I don’t remember this being an issue when I tested it but it could well be!
Hi Max,
No, I never did get this to work, and now I’ve put it to the side to make progress on other things. I am using a summernote connector to AngularJS, which *should* not be interfering with the code here.
Thanks again for any insights you have on this.
Best wishes!
Just tried the example with formatted code. For me, it seems to work and inserts the text at the correct location.
Maybe it has something to do with the AngularJs connector?
Hey Max, It took me a while to get back on this particular issue. But, it DID work correctly when I bypassed AngularJS. So, going straight to the DOM nodes looks like the way to go. Thanks for your help!
Could you please let me know how to store selection in summernote on keypress?
Just use the example up to the assignment to old Content.
The selection will be in oldContent.
Enclose this into a callback for a JQuery keypress event.
Hope this helps.
Thanks so much, This problem cosumes me total day. oh my god
Hi ,
I tried your solution . It works for me for plain text . But for formatted text it is not replacing text at selected position .
Here is my code.
var selection = document.getSelection();
var endPos = selection.focusOffset;
var cursorPos = selection.anchorOffset;
var fieldId = $(e).attr(“data-id”).trim();
var desc = $(e).attr(“data-desc”).trim();
fieldId = fieldId.replace(” “, “”);
var toInsert = ‘{‘+fieldId+”:”+desc+’}’;
var newContent = “”;
if (endPos == cursorPos) {
var oldContent = $(‘.note-editable’).text();
newContent = oldContent.substring(0, cursorPos) + toInsert + oldContent.substring(cursorPos);
}
else {
var oldContent = $(‘.note-editable’).text();
newContent = oldContent.replace(oldContent.substring(cursorPos,endPos), toInsert);
}
selection.anchorNode.nodeValue = newContent
Cursor offset is getting wrong in above code.
That is strange. It should work for formatted text as well. Did you find a solution?
$(‘#summernote’).summernote(‘editor.saveRange’);
// Editor loses selected range (e.g after blur)
$(‘#summernote’).summernote(‘editor.restoreRange’);
$(‘#summernote’).summernote(‘editor.focus’);
$(‘#summernote’).summernote(‘editor.insertText’, ‘This text should appear at the cursor’);
Alternatively, you can invoke directly from the context variable
context.invoke(‘editor.saveRange’);
// Editor loses selected range (e.g after blur)
context.invoke(‘editor.restoreRange’);
context.invoke(‘editor.focus’);
context.invoke(‘editor.insertText’, ‘This text should appear at the cursor’);
Thank you, that is great. I’ve added this approach to the post above as well!
You are a hero. Thank you
I have added the code and works 100% but if i only click to add a text it adds the text to summernote but once i save it to database the change is not there, I have to make a space and delete it again then if i save it saves the changes.
$(‘#summernote’).summernote(‘editor.saveRange’);
// Editor loses selected range (e.g after blur)
$(‘#summernote’).summernote(‘editor.restoreRange’);
$(‘#summernote’).summernote(‘editor.focus’);
$(‘#summernote’).summernote(‘editor.insertText’, ‘This text should appear at the cursor’);
It is over-writing everything when I use .summernote(‘code’, ”) .
Any solution for this? to add Multimedia without over-writing prior content.
summernote(‘code’, ”) WILL empty everything. By that what you are asking is to replace the contents with an empty string