当 draggable=true 使用 Selenium 和 C# 时,拖放不能使用 Actions

Posted

技术标签:

【中文标题】当 draggable=true 使用 Selenium 和 C# 时,拖放不能使用 Actions【英文标题】:Drag and drop not working using Actions when draggable=true using Selenium and C# 【发布时间】:2020-03-30 23:27:47 【问题描述】:

元素被正确识别,我可以看到鼠标在这两个元素之间移动,但没有发生拖放。 单击并按住时,UI 未显示任何突出显示。 也没有错误。

我尝试了在不同讨论中提出的不同解决方案,但它们都不适合我

我的代码

_actions = new Actions(Driver.WebDriver);
        var dragAndDrop = _actions.ClickAndHold(parentRow)
                                  .MoveToElement(childRow )
                                  .Release(target)
                                  .Build();
        dragAndDrop.Perform();
        Driver.Wait();

这就是我识别元素的方式

 var childList =Driver.WebDriver.FindElements(By.ClassName("itl-treeNode-title"));
     var parentRow = childList.FirstOrDefault(x => x.Text.Equals(parentSrc)).FindElement(By.XPath("following-sibling::*[1]"));
     var childRow = childList.FirstOrDefault(x => x.Text.Equals(childSrc)).FindElement(By.XPath("following-sibling::*[1]"));

相同的代码可以在我们应用程序的另一个用户界面上运行。

我现在已经像下面这样更改了我的代码,现在我遇到了过时的元素异常——因为我需要动态识别这个元素,所以我不能使用这里提到的 POM 解决方案https://www.softwaretestingmaterial.com/stale-element-reference-exception-selenium-webdriver/#How-To-Overcome-Stale-Element-Reference-Exception-in-Selenium

var childList = Driver.WebDriver.FindElements(By.ClassName("itl-treeNode-title"));
     var parent = childList.FirstOrDefault(x => x.Text.Equals(parentSrc)).FindElement(By.XPath("parent::*"));
     var parentRow = parent.FindElement(By.ClassName("itl-treenode-content-cover"));
     var child = childList.FirstOrDefault(x => x.Text.Equals(childSrc)).FindElement(By.XPath("parent::*"));
     var childRow = child.FindElement(By.ClassName("itl-treenode-content-cover"));         
     childRow.Click();
     //try
     //
     //   (new Actions(Driver.WebDriver)).DragAndDrop(childRow, parent).Perform();
     //         
     //catch (Exception ex)
     //
     //   throw new Exception("Failed to perform drag and drop ");
     //
     new Actions(Driver.WebDriver).ClickAndHold(childRow)
               .MoveToElement(parent)
               .Release(parent)
               .Build()
               .Perform();
     Driver.Wait();

例外

OpenQA.Selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document
  (Session info: chrome=77.0.3865.120)
   at OpenQA.Selenium.Remote.RemoteWebDriver.UnpackAndThrowOnError(Response errorResponse)
   at OpenQA.Selenium.Remote.RemoteWebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Remote.RemoteWebDriver.PerformActions(IList`1 actionSequenceList)
   at OpenQA.Selenium.Interactions.Actions.Perform()

【问题讨论】:

【参考方案1】:

根据您共享的 html,您想要 DragAndDrop()WebElement 包含属性 draggable=true


可拖动

draggable 是一个属性,指示是否可以使用本机浏览器行为或 HTML 拖放 API 拖动元素。 draggable 可以有以下值:

true:可以拖动元素。 false:无法拖动元素。

注意:此属性是枚举的,而不是布尔值。 truefalse 的值是强制性的,像 <img draggable> 这样的简写是被禁止的。正确的用法是<img draggable="false">

如果未设置此属性,则其默认值为auto,这意味着拖动行为是默认浏览器行为:只能拖动文本选择、图像和链接。对于其他元素,必须设置事件ondragstart 才能进行拖放操作。


原生 HTML5 拖放

Eric Bidelman 在文章Native HTML5 Drag and Drop 中提到,使对象可拖动很简单,只需在要使其可移动的元素上设置draggable=true 属性即可。举个例子:

<div id="cols">
  <div class="col" draggable="true"><header>X</header></div>
  <div class="col" draggable="true"><header>Y</header></div>
  <div class="col" draggable="true"><header>Z</header></div>
</div>

要使其他类型的内容可以拖动,您可以利用 HTML5 DnD API。但是,使用 CSS3,您可以将标记修饰为看起来像列,并添加光标给用户一个视觉指示,表明某些东西是可移动的,但大多数浏览器会创建被拖动内容的幻影图像,并且 draggable 不会做任何事情。有些浏览器,尤其是FF,在拖拽操作中会要求data be sent。

另外,Remy Sharp在文章Dragging Anything中提到:

HTML 5 规范说它应该像在相关元素的标记中添加以下属性一样简单:

draggable="true"

但是,这并不完全适用于 Safari 或 Firefox。对于 Safari,您需要向元素添加以下样式:

[draggable=true] 
  -khtml-user-drag: element;

这将在 Safari 中开始工作,并且当您拖动它时,它将使用 dataTransfer 对象设置一个默认的空值。但是,除非您手动设置一些数据,否则 Firefox 将不允许您拖动元素。


解决方案

为了解决这个问题,我们需要一个 dragstart 事件处理程序,我们将给它一些数据以供拖动:

var dragItems = document.querySelectorAll('[draggable=true]');

for (var i = 0; i < dragItems.length; i++) 
  addEvent(dragItems[i], 'dragstart', function (event) 
    // store the ID of the element, and collect it on the drop later on
    event.dataTransfer.setData('Text', this.id);
  );

在这里您可以找到drag and drop anything (source code) 的工作演示


这个用例

实时 HTML 将帮助我们以更好的方式分析问题。但是,您可以尝试以下任一链式方法作为解决方案:

使用ClickAndHold()MoveToElement()Release()

new Actions(Driver.WebDriver).ClickAndHold(parentRow).MoveToElement(childRow ).Release(target).Build().Perform();

使用DragAndDrop():

new Actions(Driver.WebDriver).DragAndDrop(parentRow, childRow).Build().Perform();

注意:诱导WebDriverWait以确保要拖动的元素可点击

【讨论】:

我已将我的新代码和异常详细信息添加到原始描述中。请您帮忙-谢谢 现在我得到一个陈旧的元素异常【参考方案2】:

点击childRow后,再次构建webelement,避免stale element引用异常,示例代码如下。

var childList = Driver.WebDriver.FindElements(By.ClassName("itl-treeNode-title"));
var parent = childList.FirstOrDefault(x => x.Text.Equals(parentSrc)).FindElement(By.XPath("parent::*"));
var parentRow = parent.FindElement(By.ClassName("itl-treenode-content-cover"));
var child = childList.FirstOrDefault(x => x.Text.Equals(childSrc)).FindElement(By.XPath("parent::*"));
var childRow = child.FindElement(By.ClassName("itl-treenode-content-cover"));    
childRow.Click();
childRow = child.FindElement(By.ClassName("itl-treenode-content-cover"));    
new Actions(Driver.WebDriver).ClickAndHold(childRow)
           .MoveToElement(parent)
           .Release(parent)
           .Build()
           .Perform();
Driver.Wait();

【讨论】:

我已将我的新代码和异常详细信息添加到原始描述中。请您帮忙-谢谢 现在我得到一个陈旧的元素异常。 @user8710571,您在哪一行得到了异常? dragAndDrop.Perform(); @user8710571,我已经更新了答案,试试看

以上是关于当 draggable=true 使用 Selenium 和 C# 时,拖放不能使用 Actions的主要内容,如果未能解决你的问题,请参考以下文章

完整日历eventElement.draggable不是函数使用`editable:true`选项时

为啥“draggable = 'true'”不能在 React 渲染组件上工作?

Draggable 为 true - 不可拖动且 DOM 选择器未触发

H5 的 Draggable 属性

draggable="true" 在 Chrome 打包应用的 <webview> 中不起作用

使用 javascript 访问 `draggable` 属性