Failure in support for CKEditor5 when the form is too large or takes too long to load.

Created on 14 June 2023, over 1 year ago
Updated 29 May 2024, 7 months ago

Problem/Motivation

I have a fairly extensive content type that has several groupings with Field group (Contributed module), and some of its fields are rich text fields with CKEditor5. The issue that was found was that in this extensive content type with a lot of JavaScript interaction, the CKEditor5 counters stopped working. However, even the same field in another content type with less load seems to be working fine.

Steps to reproduce

Someone needs to reproduce these steps using the MaxLength 3.x version to see if this is still an issue.

This only happens in excessively large forms. In order to reproduce it, you would need to load a new content type or any entity with a very large form, causing the DOM to take longer to load. When the code performs the validation if (this.dataset.ckeditor5Id), this data may not yet be loaded, despite having CKEditor5 fields. This would result in the counter being stuck and without interaction when typing.

Proposed resolution

Add a wait event for the full page load at the moment of evaluating whether the fields have the ckeditorId before executing the process. This will ensure that the DOM is properly loaded and that CKEditor5 has done its job of setting the ID in its respective textarea.

Remaining tasks

  • ❌ Reproduce the steps for MaxLength 3.x
  • ❌ Get maintainer approval for the proposed resolution
  • ❌ Implement resolution
  • ❌ Write test coverage (contact a maintainer if you need help)
  • ❌ Maintainer review via the UI
  • ❌ Maintainer Code Review #1
  • ❌ Maintainer code review #2
  • ❌ Merge into dev branch, with credit to author and participants
🐛 Bug report
Status

Postponed

Version

3.0

Component

Code

Created by

🇨🇴Colombia yasuarezclavijo

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @yasuarezclavijo
  • 🇨🇴Colombia yasuarezclavijo

    Here is a short patch that solves the problem.

  • 🇨🇴Colombia yasuarezclavijo

    Here is a short patch that solves the problem.

  • Hey, thanks for creating this issue - I've run into the same problem. I tried the patch you've provided and it works for fields that are visible when the form loads, but it doesn't work for dynamically added fields (e.g. from paragraphs). I changed the "window load event listener" part to a 100ms setTimeout which seems to sort it out, but I know that's hacky and race condition-y so I haven't created a patch for it.

    I might look for a better fix later but just thought I'd provide my 2 cents!

  • 🇩🇪Germany broon Potsdam

    I was running into this problem a few weeks ago, but I was puzzled as maxlength was activated for three fields and only working for two of them. Only if I disabled maxlength for the two working ones, it started to work for the third field. Through this issue I figured, that my specific form's size might be very close and above to the threshold to trigger that error.

    Implementing the patch from #4, it now works for all three fields
    (using Maxlength 2.1.1 on Drupal 9.5.9).

  • Status changed to Needs review over 1 year ago
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 8.1 & MySQL 5.7
    last update over 1 year ago
    5 pass
  • 🇺🇸United States cedewey Denver, CO
  • Assigned to joevagyok
  • 🇺🇸United States hipp2bsquare Temple, New Hampshire
  • 🇺🇸United States cedewey Denver, CO

    Hi all,

    Thanks for reporting this and contributing a patch. hipp2bsquare and I have reviewed this. We've confirmed that the patch does fix the issue of the countdown message not working properly, when a form has many fields with MaxLength enabled on it. The code also generally looks good.

    Assigning this to joevagyok for a final code review. Also, Joe, we're not sure that test coverage is doable/relevant for this specific scenario of a form with many fields, but defer to you.

  • First commit to issue fork.
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 8.1 & MySQL 5.7
    last update over 1 year ago
    6 pass
  • @joevagyok opened merge request.
  • Status changed to RTBC over 1 year ago
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 8.1 & MySQL 5.7
    last update over 1 year ago
    6 pass
    • joevagyok committed c4c9a545 on 2.1.x
      Issue #3366798 by yasuarezclavijo, joevagyok, cedewey, joel-speedwell,...
  • Issue was unassigned.
  • Status changed to Fixed over 1 year ago
  • 🇧🇪Belgium joevagyok

    Thank you the work on this!

  • Status changed to Needs work over 1 year ago
  • 🇧🇪Belgium joevagyok

    Reopening the issue after having a major regression on CKEditor4 fields.
    The once function is used in combination with the window.load event listener. once is a Drupal function that ensures a behavior is attached only once to a set of elements, and it's very useful in the Drupal AJAX context where behaviors could be attached multiple times. However, it's used inside the window.load event listener, which could potentially lead to certain timing issues.

    The window.load event fires when the complete page is fully loaded, including all frames, objects and images. If the window.load event triggers before Drupal attaches its behaviors, the once function may apply the behavior only to the first field, since it's the only one existing in the DOM at that moment.

    We can't rely on window.load event because CKEditor4-5 has different initialization timing. We need to find a different solution. For now I revert the changes.

    • joevagyok committed 93440a43 on 2.1.x
      Revert "Issue #3366798 by yasuarezclavijo, joevagyok, cedewey, joel-...
  • 🇮🇳India mahtab_alam

    Also this solution does not work when we work with layout builder when we add a block on that. this function is not getting called

  • Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 8.1 & MySQL 5.7
    last update over 1 year ago
    Not currently mergeable.
  • @mahtab_alam opened merge request.
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.5 + Environment: PHP 8.1 & MySQL 5.7
    last update over 1 year ago
    4 pass, 1 fail
  • 🇺🇸United States cedewey Denver, CO
  • 🇺🇦Ukraine Foxy-vikvik

    In my case In node with paragraph - if I press the Collapse button - MaxLength message will disappear

  • Status changed to RTBC about 1 year ago
  • 🇫🇮Finland jhuhta

    TL;DR: @mahtab_alam's change fixes this for me, but I noticed it a bit late. The CKEditor5 library dependency declaration was indeed missing.

    Longer explanation: I had the similar problem when using Maxlength on Webform's text_format fields. First of all, there is no UI that I know of for adding Maxlength to the fields, but you can achieve it in YAML by issuing something like this:

    field_name:
      '#type': text_format
      '#title': 'Field title'
      '#allowed_formats':
        simple_text: simple_text
      '#hide_help': true
      '#maxlength_js': true
      '#attributes':
        class:
          - maxlength
        data-maxlength: 200
      '#attached':
        library:
          - maxlength/maxlength
    

    Now the maxlength text appeared, but the calculation didn't work at all, and I found this issue after having realized that CKEditor is not initialized by the time the calculation functionality is attached. After reading @joevagyok's comment I developed a solution with slightly different approach, using MutationObserver. I didn't want to create a patch as I believe there's a solution already, but saving the code here anyway just in case.

      Drupal.behaviors.maxlength = {
        attach: function(context) {
          ...
    
          // Create a function to handle the CKEditor 5 integration.
          function integrateWithCKEditor5(element, options) {
            const eid = element.dataset.ckeditor5Id;
            const ckeditorInstance = Drupal.CKEditor5Instances.get(eid);
    
            if (eid && ckeditorInstance) {
              ml.ckeditor5(ckeditorInstance, options);
            }
          }
    
          // Select the elements with the class 'maxlength' and filter them to input elements.
          const maxlengthInputs = $context.find('.maxlength').filter(':input');
    
          // Function to monitor changes in dataset.
          function observeDatasetChanges(element, callback) {
            const observer = new MutationObserver(() => {
              callback(element);
            });
    
            observer.observe(element, { attributes: true });
          }
    
          // Iterate over the input elements.
          $(once('maxlength', maxlengthInputs)).each(function () {
            const options = {};
            const $this = $(this);
            options['counterText'] = $this.attr('maxlength_js_label');
            if ($this.hasClass('maxlength_js_enforce')) {
              options['enforce'] = true;
            }
    
            $this.charCount(options);
    
            // Observe changes in dataset.
            observeDatasetChanges(this, (element) => {
              if (element.dataset.ckeditor5Id) {
                integrateWithCKEditor5(element, options);
              }
            });
    
            // Use setInterval to periodically check for CKEditor 5 instance in Drupal.CKEditor5Instances.
            const checkInterval = setInterval(() => {
              integrateWithCKEditor5(this, options);
    
              // If CKEditor 5 instance and data-ckeditor5-id are found, clear the interval.
              if (Drupal.CKEditor5Instances.has(this.dataset.ckeditor5Id)) {
                clearInterval(checkInterval);
              }
            }, 100); // Check every 100 milliseconds.
          });
    
        },
    
  • 🇧🇪Belgium joevagyok

    Thanks, I will have a look at the approach.

  • Assigned to joevagyok
  • 🇺🇸United States cedewey Denver, CO
  • 🇧🇪Belgium joevagyok

    This issue should be resolved by the removal of CKEditor 4 support where we introduce the missing dependency for CKEditor5 library.

  • Status changed to Needs review 11 months ago
  • 🇧🇪Belgium joevagyok

    Please re-check this if it can be reproduced on 3.x-dev!

  • Status changed to Postponed: needs info 9 months ago
  • 🇺🇸United States cedewey Denver, CO

    Thanks everyone for your work on this. We need someone to test this with a long, complicated form using MaxLength 3.x to determine if this is still an issue.

  • Issue was unassigned.
  • 🇺🇸United States cedewey Denver, CO
  • Status changed to Needs work 8 months ago
  • 🇧🇪Belgium StryKaizer Belgium

    Issue still exists in 3.0
    The problem is, in maxlength.js a setTimeout is called, but no delay argument is passed along.
    The default value is 0, so this timeout is not triggered at all.

    Attached is a quick fix which gives a 500ms seconds delay.

    However, using setTimout is bad practice, a better solution would be to listen to actual events firing when ckeditors are loaded, and trigger after those.

  • Assigned to joevagyok
  • 🇧🇪Belgium joevagyok

    joevagyok changed the visibility of the branch 2.1.x to hidden.

  • 🇧🇪Belgium joevagyok

    joevagyok changed the visibility of the branch 3.x to hidden.

  • 🇧🇪Belgium joevagyok

    joevagyok changed the visibility of the branch 3366798-ckeditor-fix to hidden.

  • 🇧🇪Belgium joevagyok

    joevagyok changed the visibility of the branch 3366798-failure-in-support to hidden.

  • 🇧🇪Belgium StryKaizer Belgium

    the merge request is still using the hacky setTimeout, which might still cause this issue on slow responding forms.
    A better way would be to use an event.

    Related: Trigger event when Text Editor is attached Needs work

  • 🇧🇪Belgium joevagyok

    Indeed @StryKaizer, I am looking into a robust solution as we speak. Thank you for your support on this issue!

  • Status changed to Needs review 7 months ago
  • Fix works good. setTimeout can be replaced later when related issue will be merged.

  • Status changed to RTBC 7 months ago
  • Status changed to Postponed 7 months ago
  • 🇧🇪Belgium joevagyok

    Temp fix applied for the issue, I will still leave it active until we get the related issue fixed.

Production build 0.71.5 2024