ckeditor / ckeditor5

Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.
https://ckeditor.com/ckeditor-5
Other
9.26k stars 3.67k forks source link

Support for running in shadow DOM #3891

Open Reinmar opened 7 years ago

Reinmar commented 7 years ago

Apparently, shadow DOM has its own selection (like an iframe), so when running in a shadow root, the engine must use that selection.

Welcome to hell...


Add 👍 if you're interested in support for shadow DOM in CKEditor 5.

Reinmar commented 7 years ago

https://www.w3.org/TR/shadow-dom/#ranges-and-selections

paales commented 7 years ago

Haha, thanks for creating the issue! Welcome to the wonderful world of Custom Elements and Shadow Dom. Ps, should probably also work with Shady DOM.

Reinmar commented 7 years ago

Hey, thanks for bringing this up. We always wanted to make a custom element for CKEditor (not a big deal itself) but we haven't yet worked on it for CKEditor 5 so we didn't notice that shadow DOM is a bigger problem. The fact that shadow DOM has its own selection will require some changes in the engine, but they should be possible to hide beneath the abstraction that we made.

I've just thought about one more thing – CKEditor instance is usually represented by two DOM structures – one which holds the editor itself and one which holds floating elements (e.g. balloon panels). If editor will be enclosed in a custom element and shadow root, how to create the other root which needs to be directly in the <body>? Can editor do this itself or should we require that the developer does something special (create another element for the "body root")?

I hope this isn't very stupid question, cause I haven't played with custom elements and shadow DOM myself :D.

Comandeer commented 7 years ago

If you have a control over the custom element itself, there is no issue with getting its owner document, so you have full access to the body.

However I'm not sure if it's a good solution to request access to body when everything is encapsulated into custom element and/or Shadow DOM. Such component should be totally independent and self-contained (well, it's the main use case for the whole Web Components ecosystem…). The "true WC editor" should include toolbar as a part of the whole my-editor custom element, something like: https://jsfiddle.net/Comandeer/nfwc1sud/

Reinmar commented 7 years ago

However I'm not sure if it's a good solution to request access to body when everything is encapsulated into custom element and/or Shadow DOM. Such component should be totally independent and self-contained

Unfortunately, it's a must have that some elements are directly in body. Otherwise, if editor is used in an element with a fixed size and overflow:hidden, the balloons or modals (if we'll ever have any) might render partially invisible.

fredck commented 7 years ago

I was wondering if floating panels should not endup inside yet another custom element created on the fly for that purpose and positioned within body :?

But wait, why we need to access body? I have the impression that floating elements will be simply inside the custom element itself.

Reinmar commented 7 years ago

But wait, why we need to access body? I have the impression that floating elements will be simply inside the custom element itself.

See my comment above. They cannot be in the custom element holding the editor itself (which can be wherever in the DOM structure). They must be in a special custom element (or any element in fact) which is always directly in the <body>.

I was wondering if floating panels should not endup inside yet another custom element created on the fly for that purpose and positioned within body :?

That was my idea too. I just wonder whether it should be CKEditor creating that container or a developer. For the convenience, it'd be nice if the editor did that. But is it ok for developers?

fredck commented 7 years ago

which is always directly in the <body>.

You didn't answer why, though.

Reinmar commented 7 years ago

I did :P Because they won't be rendered if the editor is in an element which has overflow:hidden and fixed size (which happens). It's the same case as an editor created in an iframe – the UI of that editor must fit in that iframe, otherwise it will be invisible.

We had multiple bug reports about this in the past.

fredck commented 7 years ago

Ouch... those creative use cases :)

I just wonder whether it should be CKEditor creating that container or a developer. For the convenience, it'd be nice if the editor did that. But is it ok for developers?

I think they would be surprised if the editor would not do that. It would be almost transparent to them, anyway.

Reinmar commented 7 years ago

Or maybe I'm wrong... https://jsfiddle.net/pp7z788w/

Strange. Cause I'm pretty sure there were problems with that. Otherwise, why would CKE4 went this way? Maybe it was some old browser. We should check it.

Reinmar commented 7 years ago

OK, now it's bad: https://jsfiddle.net/pp7z788w/1/

Comandeer commented 7 years ago

And now it's good again ;) https://jsfiddle.net/pp7z788w/2/

Maybe the use of position: fixed and positioning against visible viewport (not the whole page) could be a way to go?

Reinmar commented 7 years ago

Heh... let me guess. CKE4 was forced to put floating stuff to the body, because there was no position:fixed in IE6? :D

If there's no gotcha with the position:fixed, then perhaps it'll be fine.

BTW, @Comandeer, do you know the place in the CSS spec clarifying why position:fixed behaves differently that position:absolute in this case? :D

cc @oleq

Comandeer commented 7 years ago

BTW, @Comandeer, do you know the place in the CSS spec clarifying why position:fixed behaves differently that position:absolute in this case? :D

Of course:

Fixed positioning is similar to absolute positioning. The only difference is that for a fixed positioned box, the containing block is established by the viewport.

Heh... let me guess. CKE4 was forced to put floating stuff to the body, because there was no position:fixed in IE6? :D

Actually it's not that simple. Some mobile browsers had some issues too. But, according to Can I Use? it's safe now (unless we support also Opera Mini).

I'm not 100% sure that fixed will be always working fine, but IMO it seems like a good solution.

Reinmar commented 7 years ago

I'm not 100% sure that fixed will be always working fine, but IMO it seems like a good solution.

If we're able to easily get the position of some elements/caret relative to the true viewport (and I think that this is the default behaviour of most of the DOM methods), then using position:fixed seems to be the thing that would be the easiest to use.

BTW, there's another DOM structure which may cause issues – we need an <svg> element with icon sprite. Hopefully, it will work inside editor element just fine.

BTW2, I wonder how all that would look in case of an inline editor (with a floating toolbar) – like in this one here: http://sdk.ckeditor.com/samples/inline.html. For this case it's hard for me to imagine the custom element application, because you actually want to make an existing, normal element, an editor.

Comandeer commented 7 years ago

For this case it's hard for me to imagine the custom element application, because you actually want to make an existing, normal element, an editor.

Look into my demo ;) That normal element could be wrapped into dgui-editor element and then automatically put into slot (light DOM of custom element – its content – is distributed to the slot inside Shadow DOM).

oleq commented 7 years ago

I'm not 100% sure that fixed will be always working fine, but IMO it seems like a good solution.

Maybe yes, maybe not. The thing is that if some element residing in a dedicated container in <body> (i.e. a balloon panel or toolbar) gets position:absolute, it preserves positioning as the web page scrolls because, in fact, it is anchored to the element which is being scrolled.

If we go with position:fixed, it will mean constant re–positioning when the web page scrolls because viewport is constant and to follow the content that panel/toolbar is attached to (a link, the caret, whatever), it must be re–positioned again, and again and again. So here's the performance issue.

Still, we may put up with performance issues and more complex implementation to maintain, but the real issue I see is Safari in iOS. I researched possibilities to develop a mobile experience in CKEditor5 and the very general conclusion was more or less: "when the software keyboard kicks in Safari in iOS, position:fixed stops working". I didn't check what would happen to the overflow in such situation but it's a really big issue anyway, isn't it?

Reinmar commented 7 years ago

If we go with position:fixed, it will mean constant re–positioning when the web page scrolls because viewport is constant and to follow the content that panel/toolbar is attached to (a link, the caret, whatever), it must be re–positioned again, and again and again. So here's the performance issue.

Great point. Doing anything in DOM on scroll is an antipattern AFAIK. We could, of course, debounce it and use CSS transitions for nicer effects but we'd be still doing something on scroll, so a static positioning feels better in this case.

jay8t6 commented 6 years ago

+1

Is there a plan on getting ckeditor to run on shadow dom? We are using ckeditor in our polymer element, it works fine on shady, but not on shadow dom. It would be really great if we can get ckeditor to play nicely with shadow dom.

Reinmar commented 6 years ago

Not yet. We haven't researched it so neither we know how hard will it be nor have we seen much interest on that (I guess that this is because it's still a Blink-only feature or so at least Can I use says so).

Also, a thing which worries me is that there's a high probability that contentEditable will behave differently in different browsers' implementations of shadow DOM (once they get there). I'm afraid that for a looong time this would become an experimental feature – not really something that you can really rely on.

Anyway, the first things to figure out are:

Any help with these aspects might help speed up development of this feature.

paales commented 6 years ago

@Reinmar Just a quick mention. You've linked to the V0 spec, the actual spec to be implemented is the V1 spec: http://caniuse.com/#feat=shadowdomv1

I think a wysiwyg editor is the perfect example of a custom element that should be running in shadow DOM. Just drop in a <ck-editor/> in your html seems great. I can't answer al your questions, but in general the shadow dom API seems to be nice.

If you really want to, you can even create a custom element without shadow dom and do everything in the light (normal) DOM. That might solve some issues.

chrisekelley commented 6 years ago

@Reinmar To help illlustrate the differences between the different doms, I have created a testbed for hoisting ckeditor5 into the shadow dom (via polymer), shady dom, and normal dom here: https://github.com/chrisekelley/polymer-ckeditor5

jay8t6 commented 6 years ago

Just an FYI, there was a PR on tinymce editor repo to add shadow dom support that was merged back in January: https://github.com/tinymce/tinymce/pull/561

nazar-pc commented 6 years ago

@jay8t6 it was closed back in January without merge. And will require serious rebase in order to be mergeable again, but there is literally no interest from upstream developers despite what they publicly say.

Reinmar commented 6 years ago

Thanks for the updates! Do you know how stable and consistent is contentEditable and Selection in shadow DOM and shadydom? My biggest worry now is that investing in researching how to work with shims will be a waste of time because natively things may work differently. Our experience is that proper support contentEditable and Selection are usually omitted in various tools (e.g. Selenium ;/).

What I'd focus on first is making sure it all works in Chrome and Firefox which support Shadow DOM natively. Does that sound reasonable?

nazar-pc commented 6 years ago

I've managed to get TinyMCE fully working under both WebComponents v0 polyfill and native implementation in Chromium back in 2015. Though, I've reported one or two issues in Chromium (which I believe are fixed already) and sent a few patches to polyfills to fix various edge cases (I have quite an experience fixing various tricky Shadow DOM-related issues over last few years).

Things have changed since then, there is v1 polyfill that have significantly reduced number of abstractions. Overall I think it should be in much better shape now. The only caveat might be Safari, I have no idea what its implementation of Shadow DOM looks like.

v1 of the spec is considered stable, so Safari, Chromium and Opera should all work fine. If they are not - definitely push upstream developers to fix their stuff. Firefox and Edge are only working with polyfills in stable versions, so at least for nearest half a year you'll need to be able to get it working under polyfills too. Shouldn't be a major issue though.

jay8t6 commented 6 years ago

@nazar-pc bummer! I really thought it was merged. I am in real need for an editor that supports shadow DOM.

Comandeer commented 6 years ago

Back in December, Safari had totally different implementation of Shadow DOM than Chrome – it lacked Selection on shadow root. Because of that experimental plugin for CKEditor 4 with Shadow DOM support worked only in Chrome. However I haven't raised any issue as I couldn't find any standard stating that selection should be exposed on shadow root. Selection API specification does not mention Shadow DOM at all and in fact it's the only place, where Selection is defined. What's more, there's still open issue about implementing selection for shadow roots (targeted for v2 of WC). There's also a proposal, which describes actual behavior of browsers.

It seems that there is no sense in implementing Shadow DOM in RTEs as long as there isn't even a stable standard to base implementation on.

nelsonsilva commented 6 years ago

FYI https://github.com/GoogleChromeLabs/shadow-selection-polyfill

rjcorwin commented 6 years ago

CKEditor in Shadow DOM would be awesome. Until then, the trick is with using CKEditor with web components is to never put CKEditor in the shadow DOM, instead putting it in a slot so that the CKEditor instance is "in" the web component yet part of the parent document.

robrez commented 5 years ago

I'm woefully unfamiliar w/ ckeditor's codebase; but, FWIW, here's a little routine we've used in a custom element that uses shadow dom containing a contenteditable... this works under native shadow dom and polyfilled shady dom.

class MyContentEditable extends HTMLElement {

  _getSelection() {
    return this._findSelectionProvider().getSelection();
  }

  /**
   * return the node that supports "getSelection"
   * https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/getSelection
   */
  _findSeletionProvider() {
    if (super.getSelection) {
      return this;
    }
    var parent = this.target;
    let types = [ 
      Node.DOCUMENT_TYPE_NODE,
      Node.DOCUMENT_FRAGMENT_NODE
    ];
    while (parent) {
      if (types.indexOf(parent.nodeType) > -1 && parent.getSelection) {
        return parent;
      }
      parent = parent.parentNode || parent.host;
      //parent maybe a shadowRoot or a slot
    }
  }
}

We'd love to be able to use CKEditor in the shadows!

aadamsx commented 5 years ago

CKEditor in Shadow DOM would be awesome. Until then, the trick is with using CKEditor with web components is to never put CKEditor in the shadow DOM, instead putting it in a slot so that the CKEditor instance is "in" the web component yet part of the parent document.

@rjsteinert it would be great to see an example of this in action!

Here's an example of where it works and doesn't, maybe fork this as a start and get a simple setup going to help everyone? https://stackblitz.com/edit/lit-element-ckeditor5

rjcorwin commented 5 years ago

@aadamsx That's a nice and simple example you've provided. Here the demo of the tangy-form-editor element that uses CKEditor4 FWIW https://tangy-form-editor.glitch.me/

Reinmar commented 5 years ago

FYI – I created a master ticker for componentizing CKEditor 5: https://github.com/ckeditor/ckeditor5/issues/1483.

Also, we seem to have a solution for the body collection issue discussed at the beginning of this thread – you can read more in https://github.com/ckeditor/ckeditor5/issues/1478.

ywsang commented 4 years ago

@Reinmar Hi there! I’ve been working on creating a web component version of CKEditor5, which involves putting the editor inside of shadow DOM. I’ve made a couple changes to the source code that help increase the amount of support for running the editor in the shadow DOM. So far, these changes are related to the editor’s styles (so that the editor looks visually correct when placed inside shadow DOM) and the engine selection issue. I’d love to contribute these changes back to the repo, though they’re not complete fixes for supporting running the editor in shadow DOM (this issue). Would you be open to accepting incremental changes towards this goal that tackle more fine-grained issues?

cc: @justinfagnani

yoyo837 commented 2 years ago

Can we consider this in 2022?

ralfpatric commented 1 year ago

Any idea if this will come in 2023? 🤔

Witoso commented 1 year ago

@ralfpatric most likely not, we have not currently set it as a priority.

mancristiana commented 1 year ago

I reproduced the issue we are facing when adding CKEditor in ShadowDOM https://stackblitz.com/edit/lit-element-ckeditor5-xd7j6g?file=index.html,index.js,package.json

I would like to contribute to this issue. What would be the recommended way to setup this test case with ShadowDOM?

Reinmar commented 1 year ago

I don't want to be the party killer here but the problem with shadow DOM isn't (to our knowledge) really fixable at the moment. It's been a while since we investigated this and AFAICS we failed to share the details here, but all issues except one were rather straightforward to fix.

The one issue being: selection handling 😳🤬

There's currently no standardized API to access selection in a shadow root. If there were even different APIs, but somehow compatible, we could somehow resolve this ticket. But nope – it's 2023 and the state is as follows:

When researching this over a year ago we checked crazy hacks that we found. AFAIR, those were in line with: https://github.com/jsturgill/shadow-root-get-selection-polyfill. Unfortunately, our QA team found many instabilities of those hacks. I can share the results if someone's interested, but it really didn't look promising.

The thing is – all editor developers are struggling with this unless they are using an <iframe> to wrap the editable element. And while we could ship some shadow DOM support with those hacks, that'd be a false promise of a usable feature.

I pinged Firefox guys because I can't believe we'll see the spec for this in the next 3 years. If there will be no reaction, I think that we'll consider going with no hacks but also no Firefox support (or hacks to support Firefox, but without aiming at making it super stable). This is depressing but it may be the only choice.

bnf commented 11 months ago

@Reinmar FYI codemirror uses document.getSelection() as fallback if getSelection is not available on the shadow root, see https://github.com/codemirror/view/blob/6.18.1/src/dom.ts#L1-L12 Introduced with https://github.com/codemirror/view/commit/77e4111521519851fae8274eaefd319551df359f With this we are able to use codemirror within shadow root with working selections.

Note: Firefox does not support the newer spec that is concerned about cross shadow root selections – e.g. when you select multiple elements with multiple shadow roots – but for running an editor within one shadow root, the existing API (shadowRoot.getSelection() with fallback to document.getSelection() should suffice)

Would that be an option for you? With https://bugs.webkit.org/show_bug.cgi?id=163921 being fixed, one should be able to parse selections in the editor's shadow root for chrome, firefox and safari.

2dareis2do commented 9 months ago

With regards

There's currently no standardized API to access selection in a shadow root. If there were even different APIs, but somehow compatible, we could somehow resolve this ticket. But nope – it's 2023 and the state is as follows:

~Is it possible to use some form of polyfill? Maybe something like this:~

https://github.com/GoogleChromeLabs/shadow-selection-polyfill

Sorry i realise that others have already suggested this approach. Looks possible.

This is as far as I have got with ckeditor

https://stackblitz.com/edit/lit-element-ckeditor5-83vd6t?file=index.js

To do the same in tinymce

https://codepen.io/twodareis2do/pen/BaMxbYN

This is basically what I want to to. Notice how styles are encapsulated in the tiny-mce component. e.g. try adding bootstrap class such as btn btn-primary using the source editor and compare to button out side.

Witoso commented 8 months ago

@2dareis2do, thanks for your suggestions! Tiny under the hood is still a wrapper on an iframe, that's why the issues mentioned in this thread are not relevant to them.

jogibear9988 commented 5 months ago

As it looks with the new API from Safari, ShadowDom could now be supported. At least for me it works in my Webcomponent Designer Application with this Code: https://github.com/node-projects/web-component-designer/blob/master/packages/web-component-designer/src/elements/helper/SelectionHelper.ts

Witoso commented 5 months ago

Yes, v17 or 17.1 introduced getComposedRanges as @Reinmar stated, most likely it stabilized a bit in the latest four versions. FF is the biggest blocker for us right now, but AFAICS they haven't replied to our ping :face_exhaling:

jogibear9988 commented 5 months ago

But FF works with the document API,

hsinyi commented 5 months ago

FWIW, there are activities and patches in this Firefox ShadowDOM selection issue .

jogibear9988 commented 5 months ago

In my designer (https://node-projects.github.io/web-component-designer-demo/index.html) TextEditing in ShadowDom works with the current Firefox Version using the document.getSelection API

Witoso commented 5 months ago

I understand, and congrats on your project, but we would like to minimize the special handling around selection to a minimum. I will keep in mind to test the proposed solution, and it's also reassuring that ProseMirror handles it this way. But it will likely not happen before other projects related to installation (#15502).