Closed NathanaelA closed 4 years ago
Hi @NathanaelA, thank you for the comment. Sorry, my English is poor. I couldn't understand what you want well. So, what is "PD"?
Sorry, PD
= Plain Draggable
. :grinning:
Here is the issue; I was trying to put a class that uses Plain Draggable to do drag and drop with a group of elements. This worked great as a standalone project.
Once I put it inside a the WebComponent (customElements.define
) and tried to use the ShadowDom ( this.attachShadow({mode: 'open'});
) PlainDraggable failed with an error about a bad element.
This error was caused by this check: (element.compareDocumentPosition(document) & Node.DOCUMENT_POSITION_DISCONNECTED)
is TRUE when the draggable items are inside the ShadowDOM. Your code expects this to be false statement.
It is called from here for the initial failure: https://github.com/anseki/plain-draggable/blob/master/src/plain-draggable.js#L1561 which calls the code above here: https://github.com/anseki/plain-draggable/blob/master/src/plain-draggable.js#L338
By removing this part of the check and making this function look like this:
function isElement(element) {
return !!(element &&
element.nodeType === Node.ELEMENT_NODE &&
// element instanceof HTMLElement &&
typeof element.getBoundingClientRect === 'function');
}
PluginDraggable will work inside a shadowDOM.
Ah... I heard the PD that means PlainDraggable for the first time. :laughing:
Maybe you misunderstood some points.
The shadow DOM is used for encapsulation typically. That divides DOM tree.
And, the Node.DOCUMENT_POSITION_DISCONNECTED
means elements are not in the same tree.
PlainDraggable requires the elements that are in same tree, otherwise it may not work.
Therefore, that checking and that error are correct behavior.
You can use PlainDraggable in divided document (e.g. frame) for encapsulation.
For example:
customElements.define('test-shadow',
class extends HTMLElement {
constructor() {
super();
this.appendChild(this.window = document.createElement('iframe'));
this.document = this.window.contentDocument;
this.targetElement = this.document.createElement('div');
this.document.body.appendChild(this.targetElement);
}
connectedCallback() {
console.log("Connected");
console.log('this.targetElement.compareDocumentPosition(this.document): ' + this.targetElement.compareDocumentPosition(this.document));
console.log('this.targetElement.compareDocumentPosition(document): ' + this.targetElement.compareDocumentPosition(document));
}
}
);
I understand the reason for the check. :grinning: And you are correct, the ShadowDom is technically a partially
different tree than the document
root object.
However, everything I am doing is 100% encapsulated inside the shadowdom. So all elements that are being used, dragged, created, are part of the exact same tree. So this check will break in this use case.
I have no issue deleting the check in my copy; which is what I'm doing right now. But I figured you might want to be aware of it -- as their are two solutions I can think of that would allow your check to work with only some minor changes to the code base.
Then instead of comparing document
to the element you compare the shadowRoot
element to the element. This allows you to detect if all the objects passed in are still part of the proper root element. If the flag/shadowroot isn't passed in; you use the document
object as normal.
This code is fairly trivial.
function findCompareElementShadow
// Find shadowRoot if it exists...
let this._compareElement = document;
let parent = this._parentElement; // _parentElement is the very root
let hasShadow = false;
while (parent != null) {
if (parent instanceof ShadowRoot) { hasShadow = true; break; }
parent = parent.parentNode;
}
if (hasShadow) { this._compareDocument = parent; }
Then you can use _compareDocument
inside the isElement
function and the check should still work properly in all use-cases. :grinning:
Btw, Using a iframe will complicate thing.... So I'd rather just delete the check in my copy of PluginDraggable as I already did, if this is not something that you want to change/fix. :grinning:
Thank you for the suggestion.
However, that should not be implemented because PlainDraggable can work in shadow DOM by divided document as I said. I think the option is unnecessary.
Even if you don't like that, many popular libraries (e.g. YouTube movie, Facebook Like button, Twitter Tweet button, etc.) are using <iframe>
element to add a web component safely. PlainDraggable also work fine in shadow DOM by general technique.
Generally speaking, a library should not consider about the outside of own world. That is, the document that runs the library should be prepared by you, not the library.
The example I indicated may help you.
Unfortunately using a iframe
would be one of the worst solutions in my case. Unlike those examples that typically have a single simple iframe added. My dom for the component is fairly complex and dynamically generated. Since their are still major performance issues with iframe ( https://jsperf.com/iframe-performance-overhead ), and in my case this widget could potentially have dozens' of these components on the page. That would be unacceptable speed hit while dynamically generating the massive amounts of items attached to an iframe.
As I stated before, I have no issue removing the check in my copy of the library that I will distribute with the rest of the code for my library. :grinning: I just figured you might want to be aware of this issue and fix it with around 10 or so lines of code. If you want a PR -- I'd be willing to send you a PR which detects the shadowdom and uses it in the compare step so that you code is as safe as it was before -- and works inside the shadow dom properly. :grinning:
As everyone knows, the shadow DOM is awesome new technology but it is not perfect yet. Then, many popular libraries are using <iframe>
element to add a web component safely for now.
Anyway, as I said, PlainDraggable requires the elements that are in same tree (i.e. current document). This means that PlainDraggable is not designed for shadow DOM. Therefore it checks that. This is of course correct behavior. Since your suggestion doesn't consider mechanism of the library at all, the removing the checking may break other many apps even if it is good for your app luckily. Please understand that PlainDraggable has to be changed more to support shadow DOM, not only the removing the checking. Your suggestion breaks some functions. That means that you open the gate by force before it is ready.
You'd better use another library rather than PlainDraggable that is unsuited for your app. Or, of course you can fork the library and use the special version for your app only. However, of course it is not supported, and I don't recommend that.
Thank you for the suggestion. :smile:
As everyone knows, the shadow DOM is awesome new technology but it is not perfect yet.
@anseki - You seem to be a little misinformed on Shadow Dom; it has been around for many years, and is part of the formal html spec. They did make some changes from Shadow Dom v0 and Shadow Dom v1.0 -- but even the current v1.0 shipped 2 years ago in chrome and safari and a over year ago in Firefox. It also has a fully working polyfill for any browser that doesn't support it naively . Basically v1 has been supported in pretty much all major browsers for well over a year now on desktop and mobile. And v0 for something like 6 years now.
Then, many popular libraries are using
Actually again, you seem to be mis-informed -- anyone actually using the webcomponent specs is using https://www.polymer-project.org/ or one of the several other https://www.webcomponents.org/ libraries or even native code. They don't use iframes. I can't think of ANY actual webcomponent that use iframes; iframes create completely different issues and as such that is why shadow dom was created for webcomponents to solve all these issues rather than using iframes. Now some webcomponent don't use shadow dom, and just attach to the normal dom. So shadow dom isn't required. However, If the browser supports webcomponents it supports shadow dom, so most complex webcomponents will use the shadow dom by default. :grinning:
Now as for the issues can you help me understand what issues you think would occur in this situation? You are 100% I don't know all the ins and outs of PlainDraggable, so it is possible I am missing something. :grinning:
Dom Tree: (-> Means child of)
Document -> Head/ -> Body -> Div ->
Thank you for the explanation about shadow DOM. :smile:
I couldn't understand it completely but of course I know that shadow DOM is awesome new technology as I said, and supporting by web browsers is progressing even though many popular libraries are using <iframe>
element to add a web component safely for now.
Anyway, you'd better use another library rather than PlainDraggable that is unsuited for your app.
Thank you for the suggestion. :smile:
You are correct, this is at least one issue in the Shadow Dom, it seems like location calculations are off by probably where I assume the shadow dom stops the calculation.
If you have time; do you think you can point out any where else I might have issues with PD in a shadow dom? Does PD walk the dom from Document to the draggable element to get coordinates; or does it walk backwards up the document element?
If I fix the issues with PD inside a shadow dom, would you accept/like a PR.
Please note, PD is actually the best library for what I'm doing; so I'd rather fix it than switch to another one... :grinning:
Sorry, my English is poor. What is the difference between "PD" and "PlainDraggable" that you are calling both? Do you mean that you are writing new library or something named "PD" that uses "PlainDraggable"? And, do you mean that you have issues about shadow DOM? Or, do you mean that you want to know how to use PlainDraggable in shadow DOM by divided document?
No problem. No difference between them PD === PlainDraggable
; just the initials. :grinning:
I am trying to understand what changes you believe would be needed to make PD work inside of shadow dom, because I'm want to attempt to do them. :grinning: And if I get it working would you accept a Pull Request for the changes...
Uh... I see, but please unify the name to call. Only you call it three names "PD", "Plain Draggable" and "PlainDraggable" randomly... Please call "PlainDraggable" it to avoid confusion.
So, if you really want, you can fork the library and use the special version to avoid the error by force as I said. Or, you can deceive the library to avoid the error by force without changing the library.
No reply came, then I close this abandoned issue. Maybe you already understood that you can deceive the library to avoid the error without changing the library. Or you noticed that PlainDraggable is unsuited for your app.
@anseki -- This can stay closed.. 1st. What is the technique to "deceive the library"? As far as I know I have to change the library to make it work...
2nd. If I create a PR that makes this work in the Shadow DOM would you merge it, or is this something you have no interest in the Library supporting?
For example: https://jsfiddle.net/wdrns2bk/
You should understand that PlainDraggable can work in shadow DOM without changing the library and <iframe>
.
As I said, of course it is not supported, and I don't recommend that.
If you attach the render tree of elements to a shadow dom that you are trying to use PD with; the following line fails: https://github.com/anseki/plain-draggable/blob/master/src/plain-draggable.js#L338 as apparently anything inside the shadow dom is considered disconnected:
Test code:
You will see the
_container
which is NOT attached to the shadow dom returns 10. You will see the_shadowContainer
which is attached to the shadow dom return 35 or 37 depending on the browser...Deleting this single check seems to allow it to fully work inside the shadow dom...