返回

Chrome 133升级后Selenium元素不可交互问题解决

java

Chrome 133 升级后 Selenium "元素不可交互" (ElementNotInteractableException) 问题解决

最近,把 Chrome 浏览器升级到 133 版本后,使用 Selenium 和 Java 进行自动化测试时,发现一些之前正常的测试用例突然报错了,错误信息是"Element Not Interactable"。具体来说,是在处理菜单悬停 (hover) 和输入框自动补全 (type-ahead) 的元素时出现问题。 之前在 Chrome 132 版本下, 这些测试用例都是正常运行的。

问题原因分析

Chrome 浏览器版本更新可能导致 WebDriver 的行为发生变化, 或者网页的前端代码有细微调整。具体到本次遇到的"元素不可交互"错误,可能的原因有以下几个方面:

  1. 元素定位问题: 升级后,网页的 DOM 结构或元素属性(例如 ID、class)可能发生了变化,导致原有的定位方式失效。 元素确实找到了,但是不可操作。
  2. 元素可见性/可交互性问题: 元素虽然存在于 DOM 中,但可能因为各种原因(比如还未加载完成、被其他元素遮挡、CSS 样式设置为不可见等)处于不可交互状态。
  3. WebDriver 同步问题: Chrome更新了, 老的WebDriver 可能有一些兼容性问题,尤其是在元素状态变化(例如悬停后菜单的出现与消失)的处理上可能存在偏差。
  4. 浏览器/驱动 BUG: 不排除 Chrome 133 本身或者对应版本的 ChromeDriver 存在某些未知的 BUG, 特别是一些 edge cases, 会影响到特定类型的元素交互。

解决方案

针对上述可能的原因,我尝试并总结了以下几种解决办法:

1. 检查并更新元素定位

  • 原理: 确保 Selenium 定位到的确实是目标元素,而且是可交互的那个。

  • 操作步骤:

    1. 使用 Chrome 开发者工具 (F12) 重新检查目标元素。
    2. 观察元素的属性 (ID, class, XPath, CSS Selector 等) 是否有变化。
    3. 如果有变化, 更新 Selenium 代码中对应的元素定位。
    4. 优先使用更稳定、更唯一的属性进行定位 (例如 ID > class > XPath > CSS Selector)。
    5. 如果元素在 iframe 中,需要先切换到对应的 iframe。
  • 代码示例:

    // 旧的定位方式 (可能失效)
    // WebElement element = driver.findElement(By.id("old-id"));
    
    //新的ID
      WebElement element = driver.findElement(By.id("menu-item"));
    
    // 如果是 XPath, 尝试使用更稳定的表达式, 避免使用绝对路径
    // WebElement element = driver.findElement(By.xpath("//div[@class='menu']/ul/li[2]")); // 不推荐
      WebElement element = driver.findElement(By.xpath("//li[@data-menu-id='item-123']"));  //推荐
    
     // 如果元素在 iframe 里
     //driver.switchTo().frame("iframe-name");
    // WebElement element = driver.findElement(By.id("element-in-iframe"));
    // driver.switchTo().defaultContent(); // 切换回主文档
    
    

2. 使用显式等待 (Explicit Waits)

  • 原理: 等待元素满足特定条件 (可见、可点击等) 后再进行操作,避免因为元素未加载完成或状态未就绪导致的错误。

  • 操作步骤:

    1. 导入 WebDriverWaitExpectedConditions 类。
    2. 创建 WebDriverWait 对象,设置超时时间。
    3. 使用 until() 方法,结合 ExpectedConditions 中的条件进行等待。
  • 代码示例:

    import org.openqa.selenium.By;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.support.ui.ExpectedConditions;
    import org.openqa.selenium.support.ui.WebDriverWait;
    import java.time.Duration;
    
    // ...
      WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); // 10秒超时
    
    // 等待元素可见并且可点击
     WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("myElement")));
     element.click();
      // 等到元素可交互(能被找到、能看到、能被点击)
       wait.until(ExpectedConditions.presenceOfElementLocated(By.id("typeahead-input"))); //首先等到能被定位
     WebElement typeAheadInput = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("typeahead-input")));//其次等到元素可见
    typeAheadInput = wait.until(ExpectedConditions.elementToBeClickable(By.id("typeahead-input")));//最后再等等, 保证可以点击
    
    // 悬停操作
    //  Actions actions = new Actions(driver);
    //  WebElement menu = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("main-menu")));
    // actions.moveToElement(menu).perform();
      //WebElement submenu = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("submenu-item")));
     // submenu.click();
    

3. 使用 JavaScriptExecutor

  • 原理: 通过执行 JavaScript 代码直接操作元素,绕过 Selenium 的某些限制。有些情况下, 元素明明在页面中, 但Selenium 的标准点击事件被阻止了,或者元素实际能接受点击但是报告了 “不可交互” ,此时可以用JavaScript.

  • 操作步骤:

    1. WebDriver 对象强制转换为 JavascriptExecutor
    2. 使用 executeScript() 方法执行 JavaScript 代码。
  • 代码示例:

    import org.openqa.selenium.JavascriptExecutor;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.By;
    // ...
    JavascriptExecutor js = (JavascriptExecutor) driver;
    
    // 直接点击元素
    WebElement element = driver.findElement(By.id("myElement"));
      js.executeScript("arguments[0].click();", element);
     // 模拟鼠标悬停
    // WebElement menu = driver.findElement(By.id("main-menu"));
    //js.executeScript("arguments[0].dispatchEvent(new MouseEvent('mouseover', {bubbles: true}));", menu);
      // 向输入框输入文本
    // WebElement input = driver.findElement(By.id("typeahead-input"));
    //  js.executeScript("arguments[0].value = 'some text';", input);
    
    
  • 安全提示 : 虽然可以直接使用JavaScript操作,但应尽量保持 Selenium 代码和JS代码的一致性,并关注其可能产生的负面影响,确保理解JS代码所做事情的全貌。

4. 检查 WebDriver 版本

  • 原理: 确保使用的 ChromeDriver 与 Chrome 浏览器版本兼容。如果不兼容可能各种奇葩问题.

  • 操作步骤:

    1. 查看 Chrome 浏览器的版本号 (chrome://version)。
    2. 下载对应版本的 ChromeDriver (下载地址: 可以在搜索引擎里搜索"ChromeDriver download")。
    3. 更新 Selenium 项目中 ChromeDriver 的路径。
  • 说明:
    每个版本的 Chrome 通常需要特定版本的ChromeDriver 支持。

5. 处理元素遮挡

  • 原理 : 如果其他元素挡住了需要交互的目标元素,导致点击事件等操作无法达到目标元素。 可以让那个遮挡的元素消失, 或者把被遮挡的元素滚动到视野内再做交互.

  • 代码示例 :

 import org.openqa.selenium.JavascriptExecutor;
 import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.By;

 // 假设遮挡元素为 overlay
  WebElement overlay = driver.findElement(By.id("overlay"));
 JavascriptExecutor js = (JavascriptExecutor) driver;
  // 将遮挡元素隐藏(display: none)
 js.executeScript("arguments[0].style.display = 'none';", overlay);

// ... 之后再进行后续元素的点击操作
 //或者, 将被遮挡的元素滚动到可见区域
WebElement targetElement = driver.findElement(By.id("myElement"));

//滚动到元素与视口顶部对齐
  js.executeScript("arguments[0].scrollIntoView(true);", targetElement);
   // 确保有足够的时间,元素已经完全出现了,可以再加上一点点额外的延时
try {
  Thread.sleep(500); // 稍微等个0.5秒, 根据网络和加载情况灵活设置
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复打断状态. 如果不是必须继续下去, 直接 throw 也行.
}
   targetElement.click();

6. 进阶处理(针对特定情况)

  • 处理动态加载的内容: 如果菜单或自动补全列表是动态加载的,可能需要使用 WebDriverWait 等待特定元素出现或消失。

  • 处理 shadow DOM: 如果元素位于 shadow DOM 中,需要先获取 shadow root,再在 shadow root 中查找元素。

  • 使用 Actions 类进行复杂交互: 如果简单的 click()sendKeys() 不起作用,可以尝试使用 Actions 类模拟更复杂的鼠标和键盘操作(例如双击、右键、组合键等)。

    import org.openqa.selenium.interactions.Actions;
    
     //...
     WebElement element = driver.findElement(By.id("myElement"));
      Actions actions = new Actions(driver);
     // actions.moveToElement(element).click().perform(); // 移动到元素并点击, 把多个动作串起来.
     actions.doubleClick(element).perform();//双击
      //actions.contextClick(element).perform(); //右键
    
    

7. 更新Selenium版本

旧的Selenium库也许不支持新浏览器的某些新特性。 保持Selenium客户端库为最新通常也是好习惯

代码示例(Maven 项目):

pom.xml 中更新 selenium-java 的版本。

 <dependency>
       <groupId>org.seleniumhq.selenium</groupId>
   <artifactId>selenium-java</artifactId>
        <version>4.x.x</version> <!---更新成最新的稳定版本-->
</dependency>

重点小结

总的来说,解决“Element Not Interactable”错误需要根据具体情况进行排查和尝试, 没有万能的解决方案, 灵活运用上面的多种技巧结合起来大概率可以搞定。 重点是理解每种方法的原理,选择适合当前场景的解决方案,并根据实际情况进行调整。 如果经过上面的操作,问题依然无法解决了, 可以尝试临时回退到132版本的浏览器。