Stop operation in change()

An operation that was already applied (and the change event is fired after operations are applied) cannot be silently removed. They need to be either reverted (by applying new operations which will revert the state of the model to the previous one) or prevented before they actually get applied.

Reverting operations

The most obvious but extremely poorly performing solution is to use setData(). That will, however, reset the selection and may cause other issues.

A far more optimal solution is to apply reversed operations for each applied operation that you want to revert. Think like in git – you cannot remove a commit (ok, you can, but you'd have to do a force push, so you don't). Instead, you apply a reversed commit.

CKEditor 5's operations allow getting their reversed conterparts. You can then apply those. However, you need to make sure that the state of the model is correct after you do that – this is tricky because when working on such a low level, you lose the normalization logic which is implemented in the model writer.

Another disadvantage of this solution is that you will end up with empty undo steps. Undo steps are defined by batches. If you'll add to a batch operations which revert those which are already in it, that batch will be an empty undo step. Currently, I don't know of a mechanism which would allow removing it from the history.

Therefore, while reverting operations is doable, it's tricky and may not work flawlessly at the moment.

Preventing applying operations

This is a solution that I'd recommend. Instead of fixing an already changed model, make sure that it doesn't change at all.

CKEditor 5 features are implemented in the form of commands. Commands have their isEnabled state. If a command is disabled, it cannot be executed.

In CKEditor 5 even features such as support for typing are implemented in the form of commands (input, delete and forwardDelete). Hence, preventing typing can be achieved by disabling these commands.


Instead of stopping the change, maybe you could save the data value after a change and "reset" to the previous value when a delete happens:

var oldData = '';
var editor = ClassicEditor
    .create(document.querySelector('#editor'))
    .then(editor => {
        editor.model.document.on('change',(eventInfo, batch) => {
           if(oldData.length > editor.getData().length) {
           // or use eventInfo.source.differ.getChanges() and check for type: "remove"
                editor.setData(oldData);
            }
            oldData = editor.getData();
        });
    })
    .catch( error => {
        console.error( error );
    });

Note instead of checking the data length, you could loop through the changes happened using eventInfo.source.differ.getChanges() and checking if there were changes of type "remove".

Tags:

Ckeditor5