redactor icon indicating copy to clipboard operation
redactor copied to clipboard

Redactor randomly deletes all links in a block, or in the rest of the field

Open nicbou opened this issue 3 years ago • 12 comments

Description

In Firefox and Chrome, inserting a link will randomly delete all surrounding links. Once in a while, I don't notice it happening, and I have to manually reinsert dozens of references and external links on the page. It's infuriating, since it can erase hours of work.

This issue is very common (~60% of the time), and it has been there since I started using Craft. I wanted to report it to Redactor, but they have no sort of community forum.

Steps to reproduce

  1. In a long article, double-click a word to select it
  2. Insert a link (Cmd + K). It happens more often with text
  3. When you save the link, watch for surrounding links being removed, and appearing as normal text.

https://user-images.githubusercontent.com/88917/122943603-73111380-d377-11eb-9a54-835f8aaf3d3e.mov

Additional info

  • Craft version: all versions from the last 4 years, including a fully updated Craft setup.
  • Browser: all Chrome and Firefox versions from the last 4 years

nicbou avatar Jun 22 '21 14:06 nicbou

First mentioned here 4 years ago: https://github.com/craftcms/cms/issues/2158

nicbou avatar Jun 22 '21 14:06 nicbou

Long shot, but does this bug happen after using Live Preview? I'm wondering if this pr for a long standing JS error would fix other odd issues like this too: https://github.com/craftcms/redactor/pull/329

croxton avatar Jun 23 '21 14:06 croxton

I never use live preview, so that is very unlikely. The example above happened on a freshly loaded page.

nicbou avatar Jun 23 '21 14:06 nicbou

I've had editors report this bug but I've never been able to reproduce it. My editors say it happens randomly but there must be a cause. Would you mind pasting the exact html you are editing above and the Redactor config?

croxton avatar Jun 23 '21 15:06 croxton

It happens very frequently. The problem is completely random, but the easiest way to reproduce it is to select a word, italicise it (Cmd + I), and link to it (Cmd + K). Redactor seems to hate this.

https://user-images.githubusercontent.com/88917/123619890-eb5d5600-d809-11eb-872d-2d1f282ef003.mov

For example, select the word "their" in the HTML below, then Cmd + I, then Cmd + K. When you save the link, all links before that word get deleted.

<h2>Tipping at the restaurant</h2>
<p>Germans usually tip their waiters<sup><a href="https://en.wikipedia.org/wiki/Gratuity#Germany">1</a></sup>. Tipping is not mandatory, but it's common<sup><a href="https://en.wikipedia.org/wiki/Gratuity#Germany">1</a></sup>. German waiters do not depend on tips to survive, but it's a big part of their income<sup><a href="https://www.reddit.com/r/germany/comments/b48xti/how_does_one_tip_in_berlin/ej56dbp/">1</a>, <a href="https://www.reddit.com/r/germany/comments/2wptvp/do_you_guys_give_tips/cotaw6a/">2</a></sup>.</p>

Redactor and field configs:

{
	"buttons": ["html","format","bold","italic","lists","link","file","image","video","horizontalrule"],
	"plugins": ["inlinestyle", "fullscreen", "table", "video", "pagebreak", "widget"],
	"imageResizable": true,
	"imagePosition": true
}
contentColumnType: mediumtext
fieldGroup: 24164084-1fbe-4cb3-8263-5c3215381e7d
handle: body
instructions: ''
name: Body
searchable: true
settings:
  availableTransforms: '*'
  availableVolumes: '*'
  cleanupHtml: true
  columnType: mediumtext
  purifierConfig: ''
  purifyHtml: '1'
  redactorConfig: Advanced.json
  removeEmptyTags: '1'
  removeInlineStyles: '1'
  removeNbsp: '1'
  showUnpermittedFiles: false
  showUnpermittedVolumes: false
translationKeyFormat: null
translationMethod: none
type: craft\redactor\Field

This is a huge problem for me, especially with auto-saving drafts. It can undo large swathes of my work, and if I don't immediately notice, I have to manually fix everything. I have wasted hours on this bug this years.

nicbou avatar Jun 28 '21 10:06 nicbou

Thank you! I can reproduce the problem exactly as per your video.

Some initial observations:

  • It occurs when you attempt to link italicized text.
  • Any existing links preceding the text you are trying to link, within the same block-level element (e.g. <p>)- are removed.

This strongly suggests the regular expression used when inserting a new link is too greedy. Clearly a bug in Redactor that can also be easily reproduced on their own editor demos: https://imperavi.com/redactor/examples/initialization/base-example/

croxton avatar Jun 28 '21 11:06 croxton

It sometimes spreads across multiple blocks. I spend a whole hour repairing links after Redactor erased them across a five page article. I have written to Imperavi and linked to this ticket. I hope they'll join the conversation.

In one instance, it replaced every link on the first half of the page to use the same URL. Again, another hour hunting through my search history for citations. It's incredibly frustrating.

nicbou avatar Jun 28 '21 11:06 nicbou

I've sort of figured out roughly why this happens. After selecting some text and wrapping with a tag - for example, <em> or <strong>, the selection start point remains the same position as before the text was wrapped. It doesn't move inside the newly inserted tag. When you subsequently try to make the selection a link, Redactor uses a start position for the selected text outside the <em> but an end position inside the <em>. Redactor inserts some <spans> to mark those positions so you can see where it's going wrong.

This shows what I mean:

Screenshot 2021-06-28 at 16 10 39

As a work around editors could click anywhere else in the editor window and then re-select the text, before adding the link.

Obviously that's not ideal but I wonder if there is a way to automate that, say by unselecting text after any new insert?

croxton avatar Jun 28 '21 15:06 croxton

Ok, here's a crude workaround. It will unselect a text selection after you apply an inline style in Redactor, thus forcing the editor to reselect the text they wish to link. Create a plugin config/redactor/plugins/fixlinks.js with this script:

(function($R)
{
    $R.add('plugin', 'fixlinks', {
        onformat: function(type, nodes)
        {
            if (type == 'inline') {
                // clear selection
                if (window.getSelection) {
                    window.getSelection().removeAllRanges();
                } else if (document.selection) {
                    document.selection.empty();
                }
            }
        }
    });
})(Redactor);

And add to the plugins array in your Redactor config, e.g.:

{
"plugins": ["fixlinks"]
}

Let me know if that helps.

croxton avatar Jun 28 '21 16:06 croxton

Here's another stab at that which (as far as I can tell) solves the issue, by forcing Redactor to select the contents of the newly inserted wrapping element. The editor can do multiple operations to the same text selection without triggering the error.

Create a plugin config/redactor/plugins/fixlinks.js with this script:

(function($R)
{
    $R.add('plugin', 'fixlinks', {
        onformat: function(type, nodes)
        {
              var sel = window.getSelection(),
                  nextSibling = sel.anchorNode.nextSibling;
              if (nextSibling) {
                  sel.removeAllRanges();
                  var newRange = document.createRange();
                  newRange.selectNodeContents(nextSibling);
                  sel.addRange(newRange);
              }
        }
    });
})(Redactor);

And add to the plugins array in your Redactor config, e.g.:

{
"plugins": ["fixlinks"]
}

croxton avatar Jun 29 '21 11:06 croxton

Thanks for the suggested fix! I'll implement it as soon as I can and report back.

nicbou avatar Jul 08 '21 09:07 nicbou

@croxton, I just deployed your recommended fix. I'll give it a go over the next few weeks. Thank you for suggesting it in the first place.

nicbou avatar Feb 08 '22 17:02 nicbou