Problem/Motivation

In #2994696: Render embedded media items in CKEditor, we added the ability for media items embedded in formatted text to be previewable in CKEditor. The previews are fully rendered media entities. The problem, though, is that if those media entities have attached asset libraries, those libraries are NOT included as part of the rendered preview.

This is totally intentional, for reasons that can be summed up as "overwhelming, unjustified complexity". As Wim Leers stated in #2994696-214: Render embedded media items in CKEditor:

We specifically do not want to load asset libraries associated with a rendered entity because A) that would also load JS, and we don't want previews to become interactive, they're just previews, B) a rendered media entity's hypothetical attached CSS (because there isn't any in Drupal core) would be written with the assumption that basic theme CSS is already present, which is not true in CKEditor iframe instances.

[...] Media entities rendered by Drupal don't have asset libraries attached by default. I explained above why we don't want this. I can expand on it more if you like. Loading attached CSS and JS — problematic as it is for the reasons I've outlined already — would require us to use the AJAX system. This would mean that requests would use the back-end theme instead of the front-end theme, thus loading both the wrong template (that of the admin theme) and the wrong asset libraries (those of the admin theme). It would also make the responses uncacheable (AJAX responses are uncacheable). If you want the nitty gritty background, see #2844822: The preview in CKEditor does not use the same Twig template as the one on the front end (default theme). Specifically, see #2844822-41: The preview in CKEditor does not use the same Twig template as the one on the front end (default theme), where I anticipated this would come up 🙉. Entity Embed prior to 1.0 was using the AJAX system. One early user with a super advanced use case needed it. They acknowledged it would not work for everyone always. Many people ran into the problem of the wrong Twig template being used. By now 23% of all Drupal 8 Entity Embed installs is using 8.x-1.0 where no AJAX request is used, and hence no CSS or JS is loaded. There have been no complaints. (I just triaged the Entity Embed issue queue again to make sure.)

We agreed to open this issue as a place to further discuss this, if it's requested by enough people.

Proposed resolution

TBD

Remaining tasks

TBD

User interface changes

TBD

API changes

TBD

Data model changes

TBD

Release notes snippet

TBD

Comments

phenaproxima created an issue. See original summary.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

dave reid’s picture

I am needing this. I am adding an embeddable render element widget, that lets the user pick which render element to use. The attached CSS and JS is dependent on what the user picks, and I cannot determine this ahead of time, aside from including *all possible CSS*. I am using the Embed module to run the returned preview HTML but the attached CSS and JS is being attached to the root document head, and not the head inside the CKEditor iframe instance.

maximpodorov’s picture

I also need the ability to render media entity on the client side using attached JS and drupalSettings.

maximpodorov’s picture

I have solved the problem of inability to preview JS-based media entities by invoking an additional AJAX request to /embed/preview/rich_text (embed module) - the response is processed in the usual Drupal way.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

johnnybgoode’s picture

I could also use this, I need to embed an entity that relies on CSS and JS from a library for proper display. I agree 100% that embedded entities should not be interactive, but their attached libraries should load so that they can be displayed correctly without workarounds.

@maximpodorov can you give more details on how that additional AJAX request is invoked to work around the current problem?

maximpodorov’s picture

Something like this:

  $(window).on('ckeditor:unlockSnapshot', function (e, ckeditor) {
    if (!ckeditor || !ckeditor.document) {
      return;
    }
    var elements = ckeditor.document.$.getElementsByClassName('your-class');
    for (var i = 0; i < elements.length; i++) {
      var element = elements[i];
      var text = element.getAttribute('data-text');
      var wrapper = element.parentElement;
      wrapper.removeChild(element);
      wrapper.style.display = 'block';

      Drupal.ajax({
        element: wrapper,
        url: Drupal.url('embed/preview/rich_text?value=' + text),
        progress: {type: 'none'},
        event: 'entity_embed_dummy_event'
      }).execute();
    }
  });
phenaproxima’s picture

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

scotwith1t’s picture

This is also critical for our project. The editors need to see exactly what the rendered embedded entity will look like when saved/published while they are editing; not including the js and css attached in the template for the view mode completely changes the way the embedded entity behaves in WYSIWYG and makes us have to create some hacks or alternate (superfluous and less effective "previews") view modes to approximate the behavior in the back end theme/CKeditor. We may eventually come up with a contributable solution for this, but for now wanted to at least voice support for somehow including attached assets without being disruptive or overly complex.

seanb’s picture

Just had a quick chat with phenaproxima about this. Allowing libraries to be attached to CkEditor is apparently not an easy thing to do (see the IS). As a workaround though, it might already help if the media templates can somehow determine they are being used as a "preview", and the attached libraries will not work.

We can discuss how the template can determine that it is being used in CkEditor, and if the Media entity itself should be aware of that (through something like a isPreviewing / setPreviewContext method). Not sure if the workaround should be implemented in this issue or if we should create a new one for it, but to be honest I think providing some variable/context to the media template is the only way we can get anything close to an improvement for this in the short term.

Any thoughts?

bkosborne’s picture

I'm a big proponent of letting media entities know they're in a "preview" state. +1 to that approach.

It's just too tricky to get some media entities rendering correctly in the editor. I'd rather just know they're in that state and render some kind of placeholder instead.

Have to make sure it's not cached though.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

karlshea’s picture

It is actually insane that there's literally no way to tell if the media item is being rendered inside CKEditor.

This does not work:

function mymodule_support_preprocess_media(&$variables) {
  if (\Drupal::service('router.admin_context')->isAdminRoute()) {
    // Even inside the editor, the media item isn't in an admin route.
  }

  if (\Drupal::routeMatch()->getRouteName() === 'media.filter.preview') {
    // It's only a preview when you first embed it, but on edit it's just part of the content!
  }
}

This is the ridiculous thing I had to do in order to allow clicking on a media item so you can adjust display mode etc:

function mymodule_support_preprocess_page(&$variables) {
  if (\Drupal::service('router.admin_context')->isAdminRoute()) {
    $variables['#attached']['library'][] = 'mymodule_support/ckeditor5_fixes';
  }
}
document.body.addEventListener('click', (e) => {
  // Prevent opening colorbox images in the current tab while in the editor.
  if (e.target.parentElement.classList.contains('colorbox')) {
    e.preventDefault();
  }
});

I can't imagine what other media embeds will need to go through to suppress behavior.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

mably’s picture

In our special use case we add to load some JS after a custom web component was displayed as a media.

We wrote a Javascript MutationObserver to detect the addition to the page of our custom element and load the related javascript library.

Here is an quick and dirty piece of code for those interested:

jQuery(document).ready(function() {
  // Function to remove specific attribute
  function initImageComparisonSlider(mutationsList, observer) {
    mutationsList.forEach(mutation => {
      // Check if nodes are added
      if (mutation.type === 'childList') {
        // Iterate through added nodes
        mutation.addedNodes.forEach(node => {
          // Check if the node is an element of type DIV
          if ((node.nodeType === 1) && (node.tagName === "DIV")) {
            var article = node.children[0];
            // Check if the node is an element of type ARTICLE
            if (article.tagName === "ARTICLE") {
              var ics = article.children[1];
              // Check if the node is an element of type IMG-COMPARISON-SLIDER
              if (ics.tagName === "IMG-COMPARISON-SLIDER") {
                once('ics-js-css', 'body').forEach(function (element) {
                  let scriptEle = document.createElement("script");
                  scriptEle.setAttribute("src", "https://cdn.jsdelivr.net/npm/img-comparison-slider@8/dist/index.js");
                  scriptEle.setAttribute("type", "text/javascript");
                  scriptEle.setAttribute("async", "async");
                  document.body.appendChild(scriptEle);
                });
              }
            }
          }
        });
      }
    });
  }
  
  // Create a new MutationObserver
  const observer = new MutationObserver(initImageComparisonSlider);
  
  // Define the target node to observe
  const targetNode = document.body;
  
  // Configuration of the observer
  const config = { childList: true, subtree: true };
  
  // Start observing the target node for configured mutations
  observer.observe(targetNode, config);
});

We used Asset Injector for our test, script was activated only on node/*/edit pages.

s_leu’s picture

+1 for this feature. It's a WYSIWYG editor after all, so at least CSS should be rendered when previewing.

loze’s picture

I also need this. +1

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.