Skip to content

Instantly share code, notes, and snippets.

@druska
Created August 26, 2015 03:56
Show Gist options
  • Save druska/624501b7209a74040175 to your computer and use it in GitHub Desktop.
Save druska/624501b7209a74040175 to your computer and use it in GitHub Desktop.
Create the `simulateDragDrop` function which can be used to simulate clicking and dragging one DOM Node onto another
function simulateDragDrop(sourceNode, destinationNode) {
var EVENT_TYPES = {
DRAG_END: 'dragend',
DRAG_START: 'dragstart',
DROP: 'drop'
}
function createCustomEvent(type) {
var event = new CustomEvent("CustomEvent")
event.initCustomEvent(type, true, true, null)
event.dataTransfer = {
data: {
},
setData: function(type, val) {
this.data[type] = val
},
getData: function(type) {
return this.data[type]
}
}
return event
}
function dispatchEvent(node, type, event) {
if (node.dispatchEvent) {
return node.dispatchEvent(event)
}
if (node.fireEvent) {
return node.fireEvent("on" + type, event)
}
}
var event = createCustomEvent(EVENT_TYPES.DRAG_START)
dispatchEvent(sourceNode, EVENT_TYPES.DRAG_START, event)
var dropEvent = createCustomEvent(EVENT_TYPES.DROP)
dropEvent.dataTransfer = event.dataTransfer
dispatchEvent(destinationNode, EVENT_TYPES.DROP, dropEvent)
var dragEndEvent = createCustomEvent(EVENT_TYPES.DRAG_END)
dragEndEvent.dataTransfer = event.dataTransfer
dispatchEvent(sourceNode, EVENT_TYPES.DRAG_END, dragEndEvent)
}
@kkrishan
Copy link

Solved above issue by changing . Now working fine in chrome for ruby for angular drag drop lists

    setData: function(type,val) {
        this.data[type] = val
        this.types[0] = type
    }

@mike123vn
Copy link

it does not work with dragular:
http://valor-software.com/ng2-dragula/

     p_Login.openUrl("http://valor-software.com/ng2-dragula/");
            let ele1=element(by.xpath("//example-app//div[contains(text(),'You can move')]"));
            let ele2=element(by.xpath("//example-app//div[contains(text(),'This is the default use case')]/.."));
            browser.driver.executeScript(dragAndDropFn, ele1.getWebElement(), ele2.getWebElement());

@leesaxby
Copy link

leesaxby commented Jul 27, 2017

As of angular-drag-and-drop-lists version >= 2.0.0 the above work around no longer seems to work as is, due to the directive using a custom MIME type.

Iv'e forked the gist and updated it, allowing drag and drop of html elements in protractor tests.
https://gist.github.com/leesaxby/fabc59c82569a225f8d833b5924e23c6

Basic app to test the fix.
https://github.com/leesaxby/protractor-drag-drop/blob/master/README.md

@jcsmit17
Copy link

jcsmit17 commented Sep 26, 2017

@druska What is the licensing on this code?

@sergsol
Copy link

sergsol commented Aug 20, 2018

Can someone give me an example how to use it in Python script please

@ElliotPalmer-94
Copy link

Thank you, the solution above works fine in Protractor

@sridattasp
Copy link

Amazing! Thank you for this script, I finally have a test passing. I adapted it slightly to reflect the exact events I was using in my drag/drop component (I also needed 'dragover').

Can you share the dragover event changes done in javascript?

@lpnam0201
Copy link

lpnam0201 commented Jan 29, 2020

Brilliant solution. Thanks a lot.
I was simulating dropping files onto Facebook's chat window and adding files: [] property to event.dataTransfer with arbitrary File() objects actually upload these files.

@vitobotta
Copy link

Hi! I am trying to use this with Sortable.js but nothing happens. No errors and the drag and drop doesn't happen. Is this supposed to work with current browsers?

@david-stefan
Copy link

Hi! I am trying to use this with Sortable.js but nothing happens. No errors and the drag and drop doesn't happen. Is this supposed to work with current browsers?

Same here. Doesn't seem to work with Sortable.js.

@hdanske
Copy link

hdanske commented Apr 3, 2020

Great. Works fine with selenium + java

@EugeneShchur
Copy link

This is amazing! Works perfectly with Selenium JavascriptExecutor =)

@nehasuman24
Copy link

nehasuman24 commented Aug 12, 2020

Can anybody please help me to know how can we drag and drop by offset using Javascript executor in Python?

@knurum
Copy link

knurum commented Mar 15, 2021

how to drag element to offset?

@rjurado01
Copy link

Thanks !!

@Praveer-grover
Copy link

If someone is doing this in 2021, and is having trouble with data transfer object (in the above example dataTransfer object is a very trimmed down version of the actual object in 2021, so some of the application implementation could reject this object) then the following would work. Just replaced the Custom event with DragEvent and used the actual DataTransfer object instead of the dummy one.

function simulateDragDrop(sourceNode, destinationNode) {
    var EVENT_TYPES = {
        DRAG_END: 'dragend',
        DRAG_START: 'dragstart',
        DROP: 'drop'
    }

    function createCustomEvent(type, dataTransfer) {
        var event = new DragEvent(type,  {dataTransfer});
        return event
    }

    function dispatchEvent(node, type, event) {
        if (node.dispatchEvent) {
            return node.dispatchEvent(event)
        }
        if (node.fireEvent) {
            return node.fireEvent("on" + type, event)
        }
    }

    var event = createCustomEvent(EVENT_TYPES.DRAG_START, new DataTransfer())
    dispatchEvent(sourceNode, EVENT_TYPES.DRAG_START, event)

    var dropEvent = createCustomEvent(EVENT_TYPES.DROP, event.dataTransfer)
    dispatchEvent(destinationNode, EVENT_TYPES.DROP, dropEvent)

    var dragEndEvent = createCustomEvent(EVENT_TYPES.DRAG_END, event.dataTransfer)
    dispatchEvent(sourceNode, EVENT_TYPES.DRAG_END, dragEndEvent)
}

@csbalazs-inst
Copy link

I was using Selenide and Kotlin. Also had to add a few extra props for my page to the transfer event and a few extra events:
It's both usable with SelenideElements or css selectors if you keep the commented line in the script:
The script:

function simulateDragDrop(sourceNode, destinationNode) {
    var EVENT_TYPES = {
        MOUSE_DOWN: 'mousedown',
        MOUSE_UP: 'mouseup',
        DRAG: 'drag',
        DRAG_END: 'dragend',
        DRAG_OVER: 'dragover',
        DRAG_START: 'dragstart',
        DROP: 'drop'
    }

    function createCustomEvent(type) {
        var event = new CustomEvent("CustomEvent")
        event.initCustomEvent(type, true, true, null)
        event.dataTransfer = {
            dropEffect: 'move',
            effectAllowed: 'all',
            files :[],
            items: [],
            types: [],
            data: {
            },
            setData: function(type, val) {
                this.data[type] = val
                this.items.push(val);
                this.types.push(type);
            },
            getData: function(type) {
                return this.data[type]
            }
        }
        return event
    }

    function dispatchEvent(node, type, event) {
        if (node.dispatchEvent) {
            return node.dispatchEvent(event)
        }
        if (node.fireEvent) {
            return node.fireEvent("on" + type, event)
        }
    }


    var mouse_down = createCustomEvent(EVENT_TYPES.MOUSE_DOWN)
    dispatchEvent(sourceNode, EVENT_TYPES.MOUSE_DOWN, mouse_down)

    var event = createCustomEvent(EVENT_TYPES.DRAG_START)
    dispatchEvent(sourceNode, EVENT_TYPES.DRAG_START, event)

    var drag = createCustomEvent(EVENT_TYPES.DRAG)
    dispatchEvent(sourceNode, EVENT_TYPES.DRAG, drag)

    drag_over = createCustomEvent(EVENT_TYPES.DRAG_OVER)
    drag_over.dataTransfer = event.dataTransfer
    dispatchEvent(destinationNode, EVENT_TYPES.DRAG_OVER, drag_over)

    var dropEvent = createCustomEvent(EVENT_TYPES.DROP)
    dropEvent.dataTransfer = event.dataTransfer
    dispatchEvent(destinationNode, EVENT_TYPES.DROP, dropEvent)

    var dragEndEvent = createCustomEvent(EVENT_TYPES.DRAG_END)
    dragEndEvent.dataTransfer = event.dataTransfer
    dispatchEvent(sourceNode, EVENT_TYPES.DRAG_END, dragEndEvent)

    var mouse_up = createCustomEvent(EVENT_TYPES.MOUSE_UP)
    dispatchEvent(sourceNode, EVENT_TYPES.MOUSE_UP, mouse_up)
    return 1
}
// simulateDragDrop(document.querySelector(arguments[0]),document.querySelector(arguments[1]))
simulateDragDrop(arguments[0],arguments[1])

usage:

 val file = File("src/test/resources/js_drag_and_drop.js")
 val js = file.readText()
 Selenide.executeJavaScript<String>(js, loginIdCard, uniqueIdDropField)

@Wooqo
Copy link

Wooqo commented Feb 11, 2022

@csbalazs-inst Thank you very much!!!

@csbalazs-inst
Copy link

@mufasalg0 you can use the script above, save it in your repo and read the file into a String object.
I don't know exactly what stack you're using but selenium or most drivers have options for executing javascript directly.
look for "selenium javascript executor" for example.

@nekdan
Copy link

nekdan commented Jan 9, 2023

@Praveer-grover
Hi, is it possible to upgrade this script and move the element to the given coordinates?

@mcgows
Copy link

mcgows commented Mar 2, 2023

If someone is doing this in 2021, and is having trouble with data transfer object (in the above example dataTransfer object is a very trimmed down version of the actual object in 2021, so some of the application implementation could reject this object) then the following would work. Just replaced the Custom event with DragEvent and used the actual DataTransfer object instead of the dummy one.

function simulateDragDrop(sourceNode, destinationNode) {
    var EVENT_TYPES = {
        DRAG_END: 'dragend',
        DRAG_START: 'dragstart',
        DROP: 'drop'
    }

    function createCustomEvent(type, dataTransfer) {
        var event = new DragEvent(type,  {dataTransfer});
        return event
    }

    function dispatchEvent(node, type, event) {
        if (node.dispatchEvent) {
            return node.dispatchEvent(event)
        }
        if (node.fireEvent) {
            return node.fireEvent("on" + type, event)
        }
    }

    var event = createCustomEvent(EVENT_TYPES.DRAG_START, new DataTransfer())
    dispatchEvent(sourceNode, EVENT_TYPES.DRAG_START, event)

    var dropEvent = createCustomEvent(EVENT_TYPES.DROP, event.dataTransfer)
    dispatchEvent(destinationNode, EVENT_TYPES.DROP, dropEvent)

    var dragEndEvent = createCustomEvent(EVENT_TYPES.DRAG_END, event.dataTransfer)
    dispatchEvent(sourceNode, EVENT_TYPES.DRAG_END, dragEndEvent)
}

@Praveer-grover this changes in this script are not moving elements for me, is it possible that you can confirm this method is still functional?

@Tasmaniandevil1979
Copy link

DragEvent

function simulateDragDrop(sourceNode, destinationNode) {
    var EVENT_TYPES = {
        DRAG_END: 'dragend',
        DRAG_START: 'dragstart',
        DROP: 'drop'
    }

    function createCustomEvent(type, dataTransfer) {
        dataTransfer.data = {};
        dataTransfer.setData = function(type,val){this.data[type] = val};
        dataTransfer.getData = function(type){return this.data[type]};
        dataTransfer.dropEffect = 'move';
        dataTransfer.effectAllowed = 'move';
        dataTransfer.types = [];
        dataTransfer.items = {};
        dataTransfer.files = {};
        var event = new DragEvent(
            type,  
            {
                bubbles: true,
                cancelable: true,
                dataTransfer: dataTransfer
            });
        return event
    }

    function dispatchEvent(node, type, event) {
        if (node.dispatchEvent) {
            return node.dispatchEvent(event)
        }
        if (node.fireEvent) {
            return node.fireEvent("on" + type, event)
        }
    }

    var event = createCustomEvent(EVENT_TYPES.DRAG_START, new DataTransfer())
    dispatchEvent(sourceNode, EVENT_TYPES.DRAG_START, event)

    var dropEvent = createCustomEvent(EVENT_TYPES.DROP, event.dataTransfer)
    dispatchEvent(destinationNode, EVENT_TYPES.DROP, dropEvent)

    var dragEndEvent = createCustomEvent(EVENT_TYPES.DRAG_END, event.dataTransfer)
    dispatchEvent(sourceNode, EVENT_TYPES.DRAG_END, dragEndEvent)
}

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