Open z2oh opened 6 years ago
Wow, thanks for this, would be very cool to support ShadowDOM 🤘 Haven't used ShadowDOM yet so unfortunately can only offer moral support.
Thanks for working on ShadowDOM support!
I'm currently rather of no help because of less free time for OSS and no detailed ShadowDOM knowledge either.
What I can offer though is a discussion about an API to make it easy to hook into those parts that need customization.
tryFindDraggableTarget(event:TouchEvent):Element | undefined
for custom implementations of selecting an element for the drag operation given a touch event
dragImageSetup(dragElement:Element):Element
to aid custom workarounds for fixing #109, #111
https://github.com/timruffles/ios-html5-drag-drop-shim/blob/b485129b36fb875a072743df1a1d641b4183c34d/src/index.ts#L1303-L1306
userSelectionFromViewportCoordinates(x:number, y:number):Element
to select the element the user is currently hovering over while dragging.
That API seems like it will cover the bases, so that's definitely a good start.
I've stuck on how to implement userSelectionFromViewportCoordinates(x:number, y:number):Element
with ShadowDOM all day. I'm worried we might have to wait for the browsers to catch up for this one.
I investigated trying to fire an event at an x,y coordinate and then use the same event.path
method that I used earlier, but for security reasons the browser won't let you do this (otherwise one could write code to click buttons in an iframe for example). I got a hacky solution working in chrome by parsing the DOM down from the top up where the coordinates are, but that won't work in Safari (which is what I really need to support).
I'll keep trying to think of tricks, but it may be better to just wait until ShadowDOM support is more widespread (which hopefully won't be too much longer).
API additions released as of v2.3.0-rc.0
Let me know if the API's provided need tweaking.
thanks @reppners we'll check!
I've shadow dom support workin on my system.
This is my workin Code:
function tryFindDraggableTarget(event) {
var cp = event.composedPath();
for (let o of cp) {
var el = o;
do {
if (el.draggable === false) {
continue;
}
if (el.getAttribute && el.getAttribute("draggable") === "true") {
return el;
}
} while ((el = el.parentNode) && el !== document.body);
}
}
function elementFromPoint(x, y) {
for (let o of this._path ) {
if (o.elementFromPoint) {
let el = o.elementFromPoint(x, y);
if (el) {
while (el.shadowRoot) {
el = el.shadowRoot.elementFromPoint(x, y);
}
return el;
}
}
}
}
function dragStartConditionOverride(event) {
this._path = event.composedPath();
return true;
}
MobileDragDrop.polyfill({ tryFindDraggableTarget: tryFindDraggableTarget, elementFromPoint: elementFromPoint, dragStartConditionOverride: dragStartConditionOverride});
this works for me on my site on safari on ios
@z2oh can you check this out? lets bump this to the top of our (internal) priority list
@jogibear9988 Nice! Looks very elegant.
Please let me know if any utility functions may make sense for the polyfill to export so they can be reused in those custom implementations. The body of tryFindDraggableTarget
seems to be reused as is, so that might make sense to export as a utility function, e.g. isElementDraggable()
.
I'm trying to wrap my head around why you're transporting the composedPath()
from the initial drag start event to use it in your elementFromPoint()
. Can you elaborate?
at first I tried to use hand over the lastEvent to the elementFromPoint, but when I call composedPath() there, I get an empty array!
seems I could only call it in the function with was raised from the event.
i've tested yesterday only on a iPad (where it worked). today I tested a galaxy tab and iPhone, both do not work. I will look whats different here
I've to correct myself. iPhone & galaxy tab work. I tested an old version
@jogibear9988 Thanks for the explanation!
The issue with your particular implementation is that elementFromPoint
is invoked based on the last touchmove
event. Coordinates will almost always be outside the boundaries of the element that started the drag operation, when composedPath
is invoked and cached in _path
.
If the whole application consists of custom elements it will work because composedPath
will contain all the elements that have to be considered in elementFromPoint
even when coordinates will be outside of the boundaries of the starting element.
But if only a few components are custom elements eventually not sharing a common root element than looping only through the elements that are part of composedPath
when the drag operation starts might lead to silent failure.
Would this naive implementation for elementFromPoint
work?
let el = document.elementFromPoint(x, y);
if (el) {
// walk down custom component shadowRoots'
while (el.shadowRoot) {
let customEl = el.shadowRoot.elementFromPoint(x, y);
// I'm a ShadowDom noob, can the element returned ever be the custom element itself?
if(customEl === null || customEl === el) {
break;
}
el = customEl;
}
return el;
}
Custom-element support should be a first class citizen of this polyfill but until browser support is solid and the implementation is battle-tested it makes sense to provide the needed custom implementations in a separate module similar as to how the scroll-behaviour
module exports the function that enables automatic scrolling support when hovering at the edge of a scrollable container.
That being said I'm happy to accept PRs adding such a module to maintain and iterate on the implementation for custom-element support. Once browser support/the implementation has matured and is proven solid it can be added to the polyfill's default implementation.
@reppners I can only say, my code works in my application (with uses webcomponents everywhere!)
should I test your code, or what should I do?
If it's no trouble for you to test it - I'd be happy to know if it works this way, too.
Ultimately this repo needs a demo page that makes use of web components but I'm too short in time to work on this atm.
@reppners seems to work... sorry for the long delay :-(
would you include this? maybe create a setting to enable use of composedPath for shadowDom?
@reppners any news to this?
@timruffles could you merge this?
I'm facing the same issue when using web components. Neither this polyfill nor https://github.com/Bernardo-Castilho/dragdroptouch/issues/25 is working when using ShadowDOM.
@timruffles Would love to see this feature merged 😎
I used a little bit updates version of my polyfill in my project:
// see https://github.com/timruffles/mobile-drag-drop/issues/115
function tryFindDraggableTarget(event) {
const cp = event.composedPath();
for (const o of cp) {
let el = o;
do {
if (el.draggable === false) {
continue;
}
if (el.getAttribute && el.getAttribute('draggable') === 'true') {
return el;
}
} while ((el = el.parentNode) && el !== document.body);
}
}
function elementFromPoint(x, y) {
let el = document.elementFromPoint(x, y);
if (el) {
// walk down custom component shadowRoots'
while (el.shadowRoot) {
let customEl = el.shadowRoot.elementFromPoint(x, y);
// I'm a ShadowDom noob, can the element returned ever be the custom element itself?
if (customEl === null || customEl === el) {
break;
}
el = customEl;
}
return el;
}
}
MobileDragDrop.polyfill({ tryFindDraggableTarget: tryFindDraggableTarget, elementFromPoint: elementFromPoint });
it works here: https://node-projects.github.io/web-component-designer-demo/index.html
what does not work is drag drop from jquery-fancytree, see issue here: https://github.com/mar10/fancytree/issues/1088
@timruffles can this fix be merged?
Sorry I haven't been actively doing much frontend these days. I've not worked with the ShadowDOM APIs, for instance.
If someone makes a PR that everyone here is happy with I'd be happy to merge it.
If someone makes a PR that everyone here is happy with I'd be happy to merge it.
@timruffles i've tested @jogibear9988 's PR and it fixed the issue for me
@jogibear9988 @danziv @timruffles Took care of merging the PR and cutting a release as v3.0.0-beta.0
.
thanks @reppners! checked the new version out - all working on my end, also in shadowDom.
I have started working on adding ShadowDOM support to this shim (which we are using for our Polymer 2 application).
At first, nothing was working at all. ShadowDOM introduces event retargeting, so events that fire up through Shadow Roots have their target changed to be the parent of the highest shadow root. This introduces a problem for the
tryFindDraggableTarget
function:https://github.com/timruffles/ios-html5-drag-drop-shim/blob/0f2c6426d2618fadd1e9dfa4816fa0384b0291a0/src/index.ts#L188-L213
as
event.target
no longer refers to the actual target of the event. I rewrote this method (in JS) as follows:Success! I can now start drag operations successfully. Next up is dropping, which has proved to be more difficult. The code responsible for finding the drop target is here:
https://github.com/timruffles/ios-html5-drag-drop-shim/blob/0f2c6426d2618fadd1e9dfa4816fa0384b0291a0/src/index.ts#L722
document.elementFromPoint
suffers a similar problem as before and returns only the highest shadow root rather than the actual element at the location. I found some documentation for DocumentOrShadowRoot.elementFromPoint(), but support forDocumentOrShadowRoot
seems limited at best. This is where I got stuck. Does anyone have any ideas on how to move forward from here?