Selenium WebDriver - determine if element is clickable (i.e. not obscured by dojo modal lightbox)
Asked Answered
J

6

18

I write automated scripts for testing web applications that are very heavy on ajax. For example, a modal dialog is displayed with the text "Saving..." when saving settings, while a lightbox greys out the rest of the page.

My test scripts are trying to click the next link in the test before the message disappears. It almost always works when driving Firefox, but when driving Chrome the following error is displayed:

Exception in thread "main" org.openqa.selenium.WebDriverException: Element is not clickable at point (99.5, 118.5). Other element would receive the click: <div class="dijitDialogUnderlay _underlay" dojoattachpoint="node" id="lfn10Dijit_freedom_widget_common_environment_Dialog_8_underlay" style="width: 1034px; height: 1025px; "></div> (WARNING: The server did not provide any stacktrace information)

This happens because the lightbox is obscuring the element I want to click on. I want to wait for the lightbox to disappear before attempting to click the link.

Checking for the lightbox to no longer exist is not a valid workaround because there are, at times, multiple levels of modal dialogs and lightboxes, and no easy way to distinguish between them.

Is there a way in Selenium to detect if the element is clickable (no other elements obscuring it)? A try/catch would be a workaround, but I'd prefer to do a proper check if that is possible.

Jollify answered 26/3, 2012 at 19:31 Comment(0)
D
20

Use the WebDriverWait conditions.

    WebDriverWait wait = new WebDriverWait(yourWebDriver, 5);
    wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//xpath_to_element")));

Webdriver will wait for 5 seconds for your element to be able to be clicked.

Deciliter answered 27/3, 2012 at 9:35 Comment(9)
Last time I tried it with Chrome it did not work: the "clickable" condition is satisfied despite there being other layers blocking the click.Fineable
not working with Firefox 20, selenium-firefox-driver 2.31.0. the elementToBeClickable return true when the element is blocked by a modal dialogShareeshareholder
There is nothing about Firefox 20 in 2.31's release notes. Is it works in Firefox 19?Deciliter
@PavelZorin 2.32 can't even launch firefox 20 that's why I was forced to use 2.31.0. Have you tried your solution? with what selenium driver? what version?Shareeshareholder
It strange that 2.32 can't launch your browser. According to release notes, if you need exactly the Firefox 20 you must use selenium 2.32. Look at selenium server's log. I have the 2.31 selenium and I didn't use Firefox 20 yet.Deciliter
Is there any comparable API for the ruby bindings? There is a Wait class, but there's nothing similar to ExpectedConditions.elementToBeClickable()Dentate
How come that I cannot enter Xpath and getting a syntax error?Mersey
@Fineable so it can't detect if some other layer with z -index is blocking clicks (even if the element is visible) ?Wash
I don't know why this was accepted by OP. ExpectedConditions.ElementToBeClickable checks that an element is not null, displayed, and enabled - none of which handle being overlayed by another element.Acnode
T
6

You can use the ExpectedConditions.invisibilityOfElementLocated(By by) method which waits until the element is either invisible or not present on the DOM.

WebDriverWait wait = new WebDriverWait(yourWebDriver, 10);
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.id("yourSavingModalDialogDiv")));

So, depending on how much time your modal dialog takes to go invisible or go off the DOM, webdriver will wait. The wait is for a maximum of 10 seconds.

Testee answered 14/1, 2014 at 7:46 Comment(0)
S
2

You could create a clickUntil function/method that does a WebDriver wait until the element is clickable with a timeout. It would attempt to click on the element, and throw away "Element is not clickable" error messages each time until it becomes clicked or times out.

Not sure how to write that in dojo, but that's an idea.

Sass answered 26/3, 2012 at 20:44 Comment(0)
N
0

In Scala:

  1. Standard code for waiting (visibility/invisibility)

    (new WebDriverWait(remote, 45)).until(
        ExpectedConditions.visibilityOf(remote.findElement(locator))
    )
    Thread.sleep(3000)
    
  2. More visibility in logs :

    while (remote.findElement(locator).isDisplayed) {
        println(s"isDisplayed: $ii $a : " + remote.findElement(locator).isDisplayed)
        Thread.sleep(100)
    }
    
  3. If you have asynchronous JavaScript processes use web-elements with timestamp:

    val oldtimestamp = remote.findElements(locator).get(0).getAttribute("data-time-stamp")
    
    while (oldtimestamp == remote.findElements(locator).get(0).getAttribute("data-time-stamp")) {
        println("Tstamp2:" + remote.findElements(locator).get(0).getAttribute("data-time-stamp"))
        Thread.sleep(200)
    }
    
Nika answered 23/11, 2016 at 9:53 Comment(0)
O
0

I also have same problems, but I tested many input in site. One are clickable which I tested and other - not clickable one just skipped. I made it by try() catch() Simply Code :

for(){ // for all my input
try {
    driver.findElement(By.xpath("...."
                                + "//input)["+(i+1)+"]")).click();

  ... tests...


} catch(Exception e) {
     if(e.getMessage().contains("is not clickable at point")) {

          System.out.println(driver.findElement(By.xpath(locator)).
          getAttribute("name")+" are not clicable");
     } else {
          System.err.println(e.getMessage());
     }
}

And more elegant:

 @SuppressWarnings("finally")
       public boolean tryClick(WebDriver driver,String locator, locatorMethods m) {

           boolean result = false;
           switch (m) {

           case xpath:
            try {
                driver.findElement(By.xpath(locator)).click();
                result= true;
            } catch (Exception e) {
                   if(e.getMessage().contains("is not clickable at point")) {
                       System.out.println(driver.findElement(By.xpath(locator)).getAttribute("name")+" are not clicable");
                   } else {
                       System.err.println(e.getMessage());
                   }
            } finally {
                break;
            }
        case id:
            try {   
                driver.findElement(By.id(locator)).click();
                return true;
            } catch (Exception e) {
               if(e.getMessage().contains("is not clickable at point")) {
                   System.out.println(driver.findElement(By.id(locator)).getAttribute("name")+" are not clicable");   
               } else {
                   System.err.println(e.getMessage());
               }
            } finally {
                break;
            }

          default:
              System.err.println("Unknown locator!");

        }
           return result;
  }
Oversoul answered 11/8, 2017 at 10:1 Comment(0)
S
0

Inspired by answer from sreeharimohan, I got this working for Python Selenium:

# first wait for the page loading spinner to show up, then wait for it to disappear so the actual clickable button is no longer obscured
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "page-loading-overlay")))
WebDriverWait(driver, 10).until(EC.invisibility_of_element_located((By.ID, "page-loading-overlay")))
Sewell answered 4/12, 2023 at 2:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.