Skip to content

Instantly share code, notes, and snippets.

@florentbr
Last active March 9, 2023 14:48
Show Gist options
  • Save florentbr/60ef7cb8d9b1ae690cafc82aad52da73 to your computer and use it in GitHub Desktop.
Save florentbr/60ef7cb8d9b1ae690cafc82aad52da73 to your computer and use it in GitHub Desktop.
Selenium - HTML5 drag and drop
from selenium import webdriver
import time
# JavaScript: HTML5 Drag and drop script
# param1 (WebElement): Source element to drag
# param2 (WebElement): Optional - target element for the drop
# param3 (int): Optional - Drop offset x relative to the target if any or source element
# param4 (int): Optional - Drop offset y relative to the target if any or source element
# param4 (int): Optional - Delay in milliseconds (default = 1ms) for dragging and dropping
# param5 (string): Optional - Key pressed (alt or ctrl or shilf)
JS_DRAG_AND_DROP = "var t=arguments,e=t[0],n=t[1],i=t[2]||0,o=t[3]||0,r=t[4]||1,a=t[5]||'',s='alt'===a||'\ue00a'===a,l='ctrl'===a||'\ue009'===a,c='shift'===a||'\ue008'===a,u=e.ownerDocument,f=e.getBoundingClientRect(),g=n?n.getBoundingClientRect():f,p=f.left+f.width/2,d=f.top+f.height/2,h=g.left+(i||g.width/2),m=g.top+(o||g.height/2),v=u.elementFromPoint(p,d),y=u.elementFromPoint(h,m);if(!v||!y){var E=new Error('source or target element is not interactable');throw E.code=15,E}var _={constructor:DataTransfer,effectAllowed:null,dropEffect:null,types:[],files:Object.setPrototypeOf([],null),_items:Object.setPrototypeOf([],{add:function(t,e){this[this.length]={_data:''+t,kind:'string',type:e,getAsFile:function(){},getAsString:function(t){t(this._data)}},_.types.push(e)},remove:function(t){Array.prototype.splice.call(this,65535&t,1),_.types.splice(65535&t,1)},clear:function(t,e){this.length=0,_.types.length=0}}),setData:function(t,e){this.clearData(t),this._items.add(e,t)},getData:function(t){for(var e=this._items.length;e--&&this._items[e].type!==t;);return e>=0?this._items[e]._data:null},clearData:function(t){for(var e=this._items.length;e--&&this._items[e].type!==t;);this._items.remove(e)},setDragImage:function(t){}};function w(t,e,n,i){for(var o=0;o<e.length;++o){var r=u.createEvent('MouseEvent');r.initMouseEvent(e[o],!0,!0,u.defaultView,0,0,0,p,d,l,s,c,!1,0,null),t.dispatchEvent(r)}i&&setTimeout(i,n)}function D(t,e,n,i){var o=u.createEvent('DragEvent');o.initMouseEvent(e,!0,!0,u.defaultView,0,0,0,p,d,l,s,c,!1,0,null),Object.setPrototypeOf(o,null),o.dataTransfer=_,Object.setPrototypeOf(o,DragEvent.prototype),t.dispatchEvent(o),i&&setTimeout(i,n)}'items'in DataTransfer.prototype&&(_.items=_._items),w(v,['pointerdown','mousedown'],1,function(){for(var t=v;t&&!t.draggable;)t=t.parentElement;if(t&&t.contains(v)){var e=y.getBoundingClientRect();D(v,'dragstart',r,function(){var t=y.getBoundingClientRect();p=t.left+h-e.left,d=t.top+m-e.top,D(y,'dragenter',1,function(){D(y,'dragover',r,function(){D(u.elementFromPoint(p,d),'drop',1,function(){D(v,'dragend',1,function(){w(u.elementFromPoint(p,d),['mouseup','pointerup'])})})})})})}})"
def drag_and_drop(driver, source, target=None, offsetX=0, offsetY=0, delay=25, key=None) :
driver.execute_script(JS_DRAG_AND_DROP, source, target, offsetX, offsetY, delay, key)
time.sleep(delay * 2 / 1000)
driver = webdriver.Chrome()
driver.get("http://react-dnd.github.io/react-dnd/examples-dustbin-copy-or-move.html")
# drag and drop Glass
source = driver.find_element_by_xpath("//*[not(./*)][normalize-space()='Glass']")
target = driver.find_element_by_xpath("//*[text()[contains(.,'Works with any drop effect')]]")
drag_and_drop(driver, source, target)
# drag and drop Banana with alt key pressed
source = driver.find_element_by_xpath("//*[not(./*)][normalize-space()='Banana']")
target = driver.find_element_by_xpath("//*[text()[contains(.,'Works with copy drop effect')]]")
drag_and_drop(driver, source, target, key='alt')
# drag and drop Paper by offset
source = driver.find_element_by_xpath("//*[not(./*)][normalize-space()='Paper']")
drag_and_drop(driver, source, offsetX=250, offsetY=-100)
var args = arguments,
elemSrc = args[0],
elemDst = args[1],
offsetX = args[2] || 0,
offsetY = args[3] || 0,
delay = args[4] || 1,
key = args[5] || '',
alt = key === 'alt' || key === '\uE00A',
ctrl = key === 'ctrl' || key === '\uE009',
shift = key === 'shift' || key === '\uE008',
doc = elemSrc.ownerDocument,
box1 = elemSrc.getBoundingClientRect(),
box2 = elemDst ? elemDst.getBoundingClientRect() : box1,
x = box1.left + (box1.width / 2),
y = box1.top + (box1.height / 2),
x2 = box2.left + (offsetX ? offsetX : box2.width / 2),
y2 = box2.top + (offsetY ? offsetY : box2.height / 2),
source = doc.elementFromPoint(x, y),
target = doc.elementFromPoint(x2, y2);
if (!source || !target) {
var ex = new Error('source or target element is not interactable');
ex.code = 15;
throw ex;
}
var dataTransfer = {
constructor : DataTransfer,
effectAllowed : null,
dropEffect : null,
types : [ ],
files : Object.setPrototypeOf([], null),
_items : Object.setPrototypeOf([], {
add : function add(data, type) {
this[this.length] = {
_data : '' + data,
kind : 'string',
type : type,
getAsFile : function () { },
getAsString : function (callback) { callback(this._data) }
};
dataTransfer.types.push(type);
},
remove : function remove(i) {
Array.prototype.splice.call(this, i & 65535, 1);
dataTransfer.types.splice(i & 65535, 1);
},
clear : function clear(data, type) {
this.length = 0;
dataTransfer.types.length = 0;
}
}),
setData : function setData(format, data) {
this.clearData(format);
this._items.add(data, format);
},
getData : function getData(format) {
for (var i = this._items.length; i-- && this._items[i].type !== format;);
return i >= 0 ? this._items[i]._data : null;
},
clearData : function clearData(format) {
for (var i = this._items.length; i-- && this._items[i].type !== format;);
this._items.remove(i);
},
setDragImage : function setDragImage(format) { }
};
if ('items' in DataTransfer.prototype)
dataTransfer.items = dataTransfer._items;
emit_mouse(source, [ 'pointerdown', 'mousedown' ], 1, function () {
var elem = source;
while (elem && !elem.draggable)
elem = elem.parentElement;
if (!elem || !elem.contains(source))
return;
var box2 = target.getBoundingClientRect();
emit_drag(source, 'dragstart', delay, function () {
var box3 = target.getBoundingClientRect();
x = box3.left + x2 - box2.left;
y = box3.top + y2 - box2.top;
emit_drag(target, 'dragenter', 1, function () {
emit_drag(target, 'dragover', delay, function () {
emit_drag(doc.elementFromPoint(x, y), 'drop', 1, function () {
emit_drag(source, 'dragend', 1, function () {
emit_mouse(doc.elementFromPoint(x, y), [ 'mouseup', 'pointerup' ]);
})})})})})});
function emit_mouse(element, types, delay, callback) {
for (var i=0; i < types.length; ++i) {
var event = doc.createEvent('MouseEvent');
event.initMouseEvent(types[i], true, true, doc.defaultView, 0, 0, 0, x, y, ctrl, alt, shift, false, 0, null);
element.dispatchEvent(event);
}
callback && setTimeout(callback, delay);
}
function emit_drag(element, type, delay, callback) {
var event = doc.createEvent('DragEvent');
event.initMouseEvent(type, true, true, doc.defaultView, 0, 0, 0, x, y, ctrl, alt, shift, false, 0, null);
Object.setPrototypeOf(event, null);
event.dataTransfer = dataTransfer;
Object.setPrototypeOf(event, DragEvent.prototype);
element.dispatchEvent(event);
callback && setTimeout(callback, delay);
}
var t=arguments,e=t[0],n=t[1],i=t[2]||0,o=t[3]||0,r=t[4]||1,a=t[5]||'',s='alt'===a||'\ue00a'===a,l='ctrl'===a||'\ue009'===a,c='shift'===a||'\ue008'===a,u=e.ownerDocument,f=e.getBoundingClientRect(),g=n?n.getBoundingClientRect():f,p=f.left+f.width/2,d=f.top+f.height/2,h=g.left+(i||g.width/2),m=g.top+(o||g.height/2),v=u.elementFromPoint(p,d),y=u.elementFromPoint(h,m);if(!v||!y){var E=new Error('source or target element is not interactable');throw E.code=15,E}var _={constructor:DataTransfer,effectAllowed:null,dropEffect:null,types:[],files:Object.setPrototypeOf([],null),_items:Object.setPrototypeOf([],{add:function(t,e){this[this.length]={_data:''+t,kind:'string',type:e,getAsFile:function(){},getAsString:function(t){t(this._data)}},_.types.push(e)},remove:function(t){Array.prototype.splice.call(this,65535&t,1),_.types.splice(65535&t,1)},clear:function(t,e){this.length=0,_.types.length=0}}),setData:function(t,e){this.clearData(t),this._items.add(e,t)},getData:function(t){for(var e=this._items.length;e--&&this._items[e].type!==t;);return e>=0?this._items[e]._data:null},clearData:function(t){for(var e=this._items.length;e--&&this._items[e].type!==t;);this._items.remove(e)},setDragImage:function(t){}};function w(t,e,n,i){for(var o=0;o<e.length;++o){var r=u.createEvent('MouseEvent');r.initMouseEvent(e[o],!0,!0,u.defaultView,0,0,0,p,d,l,s,c,!1,0,null),t.dispatchEvent(r)}i&&setTimeout(i,n)}function D(t,e,n,i){var o=u.createEvent('DragEvent');o.initMouseEvent(e,!0,!0,u.defaultView,0,0,0,p,d,l,s,c,!1,0,null),Object.setPrototypeOf(o,null),o.dataTransfer=_,Object.setPrototypeOf(o,DragEvent.prototype),t.dispatchEvent(o),i&&setTimeout(i,n)}'items'in DataTransfer.prototype&&(_.items=_._items),w(v,['pointerdown','mousedown'],1,function(){for(var t=v;t&&!t.draggable;)t=t.parentElement;if(t&&t.contains(v)){var e=y.getBoundingClientRect();D(v,'dragstart',r,function(){var t=y.getBoundingClientRect();p=t.left+h-e.left,d=t.top+m-e.top,D(y,'dragenter',1,function(){D(y,'dragover',r,function(){D(u.elementFromPoint(p,d),'drop',1,function(){D(v,'dragend',1,function(){w(u.elementFromPoint(p,d),['mouseup','pointerup'])})})})})})}})
@as3379
Copy link

as3379 commented Sep 20, 2019

Hi I'm currently working on an application which is developed in HTML5, so automating the drag and drops functionality has been a nightmare. I found your work to be really helpful. When implemented the code in my local test framework, I'm facing this error "TypeError: Object of type WalkthroughClient is not JSON serializable"

I have not include the drag_drop.js in the project folder. Is it because of this ?? If not please give me steps to implement

@gouthamdatla
Copy link

gouthamdatla commented Sep 25, 2019

"drag-drop.min.js"
This worked for me....I'm trying to drag and drop elements in my application from past three days....your awsome :)
Im using Protractor Type script.
Implimentation:

const DragAndDrop = "function h(a,b,c,d){var k=l.createEvent('DragEvent');k.initMouseEvent(b,!0,!0,l.defaultView,0,0,0,m,n,w,x,y,!1,0,null);Object.setPrototypeOf(k,null);k.dataTransfer=g;Object.setPrototypeOf(k,DragEvent.prototype);a.dispatchEvent(k);setTimeout(d,c)}var a=arguments,c=a[0],d=a[1],q=a[2]||0,r=a[3]||0,t=a[4]||1;a=a[5]||'';var x='alt'===a||'\ue00a'===a,w='ctrl'===a||'\ue009'===a,y='shift'===a||'\ue008'===a,l=c.ownerDocument;a=c.getBoundingClientRect();var e=d?d.getBoundingClientRect():a,m=a.left+a.width/2,n=a.top+a.height/2,u=e.left+(q?q:e.width/2),v=e.top+(r?r:e.height/2),p=l.elementFromPoint(m,n),f=l.elementFromPoint(u,v);for(d=p;d&&!d.draggable;)d=d.parentElement;if(!d||!c.contains(p))throw c=Error('source element is not interactable/draggable'),c.code=15,c;if(!f)throw c=Error('target element is not interactable'),c.code=15,c;var g={constructor:DataTransfer,effectAllowed:null,dropEffect:null,types:[],files:Object.setPrototypeOf([],null),_items:Object.setPrototypeOf([],{add:function(a,b){this[this.length]={_data:''+_data,kind:'string',type:b,getAsFile:function(){},getAsString:function(a){a(this._data)}};g.types.push(b)},remove:function(a){Array.prototype.splice.call(this,a&65535,1);g.types.splice(a&65535,1)},clear:function(a,b){this.length=0;g.types.length=0}}),setData:function(a,b){this.clearData(a);this._items.add(b,a)},getData:function(a){for(var b=this._items.length;b--&&this._items[b].type!==a;);return 0<=b?this._items[b]._data:null},clearData:function(a){for(var b=this._items.length;b--&&this._items[b].type!==a;);this._items.remove(b)},setDragImage:function(a){}};'items'in DataTransfer.prototype&&(g.items=g._items);e=f.getBoundingClientRect();h(p,'dragstart',t,function(){var a=f.getBoundingClientRect();m=a.left+u-e.left;n=a.top+v-e.top;h(f,'dragenter',1,function(){h(f,'dragover',t,function(){f=l.elementFromPoint(m,n);h(f,'drop',1,function(){h(p,'dragend',1,function(){})})})})})";

let dragElement : ElementFinder = element(by.xpath("your element xpath))
let dropElement: ElementFinder = element(by.xpath("your element xpath"))
await browser.executeScript(DragAndDrop, dragElement ,dropElement);

Note: need to do few changes to make code look good

@gouthamdatla
Copy link

Implimentation of "drag-drop.min.js" in Protractor typescript framework:

const DND = "function h(a,b,c,d){var k=l.createEvent('DragEvent');k.initMouseEvent(b,!0,!0,l.defaultView,0,0,0,m,n,w,x,y,!1,0,null);Object.setPrototypeOf(k,null);k.dataTransfer=g;Object.setPrototypeOf(k,DragEvent.prototype);a.dispatchEvent(k);setTimeout(d,c)}var a=arguments,c=a[0],d=a[1],q=a[2]||0,r=a[3]||0,t=a[4]||1;a=a[5]||'';var x='alt'===a||'\ue00a'===a,w='ctrl'===a||'\ue009'===a,y='shift'===a||'\ue008'===a,l=c.ownerDocument;a=c.getBoundingClientRect();var e=d?d.getBoundingClientRect():a,m=a.left+a.width/2,n=a.top+a.height/2,u=e.left+(q?q:e.width/2),v=e.top+(r?r:e.height/2),p=l.elementFromPoint(m,n),f=l.elementFromPoint(u,v);for(d=p;d&&!d.draggable;)d=d.parentElement;if(!d||!c.contains(p))throw c=Error('source element is not interactable/draggable'),c.code=15,c;if(!f)throw c=Error('target element is not interactable'),c.code=15,c;var g={constructor:DataTransfer,effectAllowed:null,dropEffect:null,types:[],files:Object.setPrototypeOf([],null),_items:Object.setPrototypeOf([],{add:function(a,b){this[this.length]={_data:''+_data,kind:'string',type:b,getAsFile:function(){},getAsString:function(a){a(this._data)}};g.types.push(b)},remove:function(a){Array.prototype.splice.call(this,a&65535,1);g.types.splice(a&65535,1)},clear:function(a,b){this.length=0;g.types.length=0}}),setData:function(a,b){this.clearData(a);this._items.add(b,a)},getData:function(a){for(var b=this._items.length;b--&&this._items[b].type!==a;);return 0<=b?this._items[b]._data:null},clearData:function(a){for(var b=this._items.length;b--&&this._items[b].type!==a;);this._items.remove(b)},setDragImage:function(a){}};'items'in DataTransfer.prototype&&(g.items=g._items);e=f.getBoundingClientRect();h(p,'dragstart',t,function(){var a=f.getBoundingClientRect();m=a.left+u-e.left;n=a.top+v-e.top;h(f,'dragenter',1,function(){h(f,'dragover',t,function(){f=l.elementFromPoint(m,n);h(f,'drop',1,function(){h(p,'dragend',1,function(){})})})})})";

await browser.executeScript(DND , dragingElement, droppingElement);

Note: Need to do changes to make your code look good.

@gouthamdatla
Copy link

Hi I'm currently working on an application which is developed in HTML5, so automating the drag and drops functionality has been a nightmare. I found your work to be really helpful. When implemented the code in my local test framework, I'm facing this error "TypeError: Object of type WalkthroughClient is not JSON serializable"

I have not include the drag_drop.js in the project folder. Is it because of this ?? If not please give me steps to implement

Hope below implementation steps by me helps you...:) happy coding

@manikumarnune123
Copy link

Were anyone able to drag and drop in this site with above piece od code?

https://app.appsmith.com/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment