After experimenting a lot with contenteditable
elements, I finally chosed Quill to create a rich text message box on a chat app, because it is correctly handling selection, which appears to be a mess when dealing directly with a contenteditable
element.
In my experiments I used to parse the innerHTML and replace some strings by their emoji unicode counterpart (eg: '<3': '❤️'
), and used Autolinker library to replace found links by a link tag in my editable element.
google.com --> <a href="https://www.google.com">google.com</a>
I want to avoid Quill toolbar and replace automatically emojis and links.
So, I guess I have to replace editor's content at some place...
I'm really new to Quill, but I understand that it is working with a specific internal model called deltas, that allows operational transform (correct me if I'm wrong).
This assumption done, how would I check/parse these deltas and replace specific content in it in a way that preserves selection and caret position?
References:
You are correct that Quill uses Delta to describe its contents. To get the current Deltas, you can use quill.getContents
, and to set the new Deltas you can use quill.setContents
.
To preserve the caret position you can use getSelection
and setSelection
.
However, the problem is that the text-change event happens after the DOM updates. Currently there are no really built-in such as before-text-change
. This similar Issue has more details about problems modifying contents during text-change
event. One way is to wrap the updates in queueMicrotask
.
Here is a working example combining the methods mentioned above that replaces <3
to ❤️
on text-change. You can extend it to replace other emojis and links, though it might have problems as said in the issue.
var quill = new Quill('#editor', {
theme: 'snow'
});
quill.on('text-change', update);
update(); //update for initial texts
function update(delta, oldContents, source) {
if (source === 'silent') return;
const newContents = quill.getContents()
.map((op) => {
if (typeof op.insert === 'string') {
op.insert = op.insert.replaceAll('<3', '❤️');
}
return op;
});
queueMicrotask(() => {
//getSelection be queued after DOM update to get copy-paste position updates
const selection = quill.getSelection();
quill.setContents(newContents, 'silent');
//don't set caret position if the editor wasn't focused before
if (selection)
quill.setSelection(selection.index, 0, 'silent');
});
}
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
<div id="editor">
<p>Hello World!</p>
<p>Some initial <strong>bold <3 </strong> text <3 <3</p>
<p><br></p>
</div>
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>