Skip to main content
 首页 » 编程设计

c#之在滚动 SPA 时,父 div 向上移动太远后,WebDriver 将子 div 报告为 "Not Visible"

2024年02月13日30mfrbuaa

有问题的网页是一个 SPA,我可以将其描述为一个大的 div,里面有几个 div“板”,我们通过在父 div 上应用 webkit 转换将其滚动到 View 中。

不幸的是,一旦滚动太远,WebDriver 返回的元素将被“禁用”——WebElement 属性“Displayed”为 false,Text 属性为空,我无法对该元素执行任何点击/操作。然而,它当时在页面上是可见的并且功能完美。

在调查问题时,我创建了一个小型测试网页以查看在什么情况下会发生这种情况。

Link to JSFiddle sample page to show functionality

注意我们没有在这个网站中使用 IFrame,JSFiddle 只是为了演示功能。

<html><head> 
<script type="text/javascript" src="jquery-1.8.3.js"></script> 
<style type="text/css"> 
    .board-nav { 
        font-size: 12px; 
        color: #1b1a1a; 
        margin-right: 10px; 
        vertical-align: middle; 
    } 
 
    .scroller{ 
        -webkit-transition: -webkit-transform 0ms; transition: -webkit-transform 0ms;  
        -webkit-transform-origin: 0px 0px;  
        -webkit-transform: translate(0px, 0px) scale(1) translateZ(0px); 
        transition-property: transform;  
        transform-origin: 0px 0px 0px;  
        transform: translate(0px, 0px) scale(1) translateZ(0px); 
        height: 4000px; 
    } 
</style> 
<script type="text/javascript"> 
 var boardPositions = [0, -878, -1748, -2598, -3468]; 
var $scroller = null; 
function changeBoard(event) { 
    var newValue = 'translate(0px, ' + boardPositions[event.target.id] + 'px) scale(1) translateZ(0px)'; 
     
    $('.scroller').css('-webkit-transform',newValue); 
    $('.scroller').css('transform', newValue); 
} 
 
$(document).ready( function(){ 
    var $boards = $('.board-nav');  
    $boards.on('click', changeBoard); 
}); 
 
</script> 
</head><body> 
<div> 
    <div class="board-nav" id="0">Info</div> 
    <div class="board-nav" id="1">Board1</div> 
    <div class="board-nav" id="2">Board2</div> 
    <div class="board-nav" id="3">Board3</div> 
    <div class="board-nav" id="4">Board4</div> 
</div> 
<div id="content" style="width:1612px; height:864px; position:absolute; left:58px;         overflow:hidden"> 
    <div class="scroller"> 
        <ul class="vertical"> 
            <li class="v" id="li0"> 
                <div class="target"><span>Info</span></div> 
            </li> 
        <li class="v" id="li1"> 
            <div class="target"><span>Board1</span></div> 
        </li> 
        <li class="v" id="li2"> 
            <div class="target"><span>Board2</span></div> 
        </li> 
        <li class="v" id="li3"> 
            <div class="target"><span>Board3</span></div> 
        </li> 
        <li class="v" id="li4"> 
            <div class="target"><span>Board4</span></div> 
        </li> 
    </ul> 
    </div> 
</div> 
</body></html> 

似乎只要父 div Y 位置变得小于 -2000 左右,所有 子元素就会以这种方式“禁用”。在此之前(例如 -1900 左右),返回的元素包含所有有效信息。我可以从 Board 3 切换回 Board 2,然后所有元素再次正确返回。返回板 3 - 所有板都“禁用”。所以这似乎是浏览器 WebDriver 的限制。

WebDriver C# 测试代码:

 var driver = new FirefoxDriver(); 
 driver.Manage().Window.Maximize(); 
 driver.Navigate().GoToUrl("testpage.html"); 
 
 var elements = driver.FindElements(By.ClassName("v")); 
 return elements[3].FindElement(By.TagName("span")).Text; 

Firefox 处理得最好 - 它至少在放弃之前显示 Info/Board1/Board2。 Chrome 不处理任何板,为所有板返回相同的 Displayed = false,空白文本。

所以我的问题是 - 有什么办法可以解决这个问题吗?我可以测试其他板上的文本是否正确,同时显示 Board1/Info,但无法与最后 2 个板交互。

回答:

刚刚通过向父 div“滚动”元素添加高度属性解决了我的问题 - 我的猜测是 WebDriver 无法确定父 div 的高度,因此假设超出某个位置它不再可见。

我已经用解决方案更新了上面的代码和 JSFiddle

请您参考如下方法:

我想这会发生在 Firefox 和 IE 上,但不会发生在 Chrome 上?如果是这样,那么我们遇到了类似的问题。我怀疑 Chrome 不关心 WebDriver W3C 标准并以稍微不同的方式确定可见性。

如果这个答案与您的问题无关,我希望它仍然对那些通过搜索引擎找到它的人有所帮助。

我面临的问题是 Selenium 返回的元素是不可见的,但真正的最终用户可以毫无问题地查看和执行操作。这里最好的解决方案是更改站点的源代码,以使 Selenium 的 FirefoxDriver 和 InternetExplorerDriver 满意。由于这与元素的位置和重叠有关,这似乎很难重现和修复。

但是,除此之外,这里有一些解决方法,通过 Selenium 进行的黑客攻击可能会有所帮助。

  • 对那些不可见的元素使用element.GetAttribute("textContent")而不是element.Text
 return elements[3].FindElement(By.TagName("span")).GetAttribute("textContent"); 
  • 使用 Actions 点击或 JavaScript 点击,而不是普通点击。例如,如果您多次需要此方法,您可能希望为 IWebElement 或 IWebDriver 创建扩展方法。
// example of JavaScriptClick extension method for IWebDriver 
// use as driver.JavaScriptClick(element); 
// or you can make it IWebElement's extension and pass in IWebDriver 
// or just create a normal method and pass in both driver and element 
public static void JavaScriptClick(this IWebDriver driver, IWebElement element) { 
    IJavaScriptExecutor executor = driver as IJavaScriptExecutor; 
    executor.ExecuteScript("arguments[0].click();", element); 
} 
 
public static void ActionsClick(this IWebDriver driver, IWebElement element) { 
    new Actions(driver).Click(element).Perform(); 
} 

相关阅读: