selenium解决HTML5不能drag and drop

https://gist.github.com/florentbr/60ef7cb8d9b1ae690cafc82aad52da73

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://xxxx.xxxxl")


# 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)

目前找到的有jquery的、无需jquery的,其中基本都是不支持xpath,只有这一个既不需要jquery也不需要css selector,并且封装了延迟、附加键的方法

原始JS如下:可以看到语法还是比较老的。

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);
}

不过我目前使用pyautogui,虽然看上去有点傻...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值