Selenium WebDriver can't click <option> element inside <datalist>

Viewed 1598

I am writing automated tests for an angular/clarity application and I want to first
type some text into an input field and afterwards select an option from a datalist.

UI and HTML

Setting the text to the input field works fine but when attempting to click on the
option element I get the following exception:

Caused by: org.openqa.selenium.JavascriptException: javascript error: option element is not in a select

I have already tried clicking it with selenium and javascript. Below is the code for both.

@Override
public void click(String xpath, MouseButton button, ClickPosition position)
{
    LOG.info("Clicking on element '{}' with Mouse Button '{}' on Position '{}'!", xpath, button, position);

    final WebElement element = find(xpath);

    //in case we have a standard click (left on center) we spare out the whole calculation and just use standard click
    if (button == MouseButton.LEFT && position == ClickPosition.CENTER)
    {
        element.click();
    }
    else
    {
        final Dimension size = element.getSize();

        // opposite to the javadoc, the Actions#moveToElement() method is relative to the center and not to the top left corner with selenium api version 3.14.0
        // REMARK: there is an issue currently filed so may be changed back at some point
        final Point relativePoint = position.calculateRelativePointFromCenter(size.width - 1, size.height - 1);

        performActions(a -> {
            try
            {
                a.moveToElement(element, relativePoint.x, relativePoint.y);
                switch (button)
                {
                    case LEFT:
                        a.click();
                        break;
                    case RIGHT:
                        a.contextClick();
                        break;
                    case MIDDLE:
                    default:
                        throw new UnsupportedOperationException("Unexpected click button: " + button);
                }
            }
            catch (final WebDriverException e)
            {
                if (e.getMessage().contains("Element is not clickable"))
                {
                    throw new ElementNotClickableException("Unable to click on element with XPath '" + xpath + "'",
                        e);
                }
            }
        });
    }
}

@Override
public void javascriptClick(String xpath)
{
    LOG.info("Triggering click event on element '{}' via javascript!", xpath);
    parent.executeJavascript("arguments[0].click();", find(xpath));
}

System info: host: 'XXX', ip: 'XXX', os.name: 'Windows 10', os.arch: 'amd64', os.version: '10.0', java.version: '1.8.0_191'

Driver info: org.openqa.selenium.chrome.ChromeDriver Capabilities {browserName: chrome, browserVersion: 84.0.4147.125, chrome: {chromedriverVersion: 84.0.4147.30}

Does anyone have a clue about whats wrong here?

1 Answers

That's a good question. Datalist and options seem to be simple yet selenium does not like them. I read through the w3c standards for datalists and options - they're designed for auto-complete support rather than selection.

The html living spec says:

The datalist element represents a set of option elements that represent predefined options for other controls. In the rendering, the datalist element represents nothing and it, along with its children, should be hidden

And for the input field the spec says:

When the user selects a suggestion, the input element's value must be set to the selected suggestion's value, as if the user had written that value themself.

So really, this datalist is just populating the input with the value...

I was able to recreate your error with a very simple piece of html:

<html>
    <body>
        <input id="input" list="datalist">
            <datalist class="c" id="datalist">
                <option value="AUTO00001TEST - AutoTestRequestPart" class="ng-star-inserted"></option>
            </datalist>
        </input
    </body>
</html>

Exactly as you said - a normal selenium click throws a js error:

Javascript error: option element is not in a select

You also...

  • cannot convert it to a Select object type as that doesn't accept datalist tags
  • cannot click the datalist directly as it throws an ElementNotIteractable
  • cannot use actions to moveto and click - you get "Failed to execute 'elementsFromPoint' on 'Document'"
  • cannot use action to keys down + keys enter - just doesn't do it
  • cannot use a JS executor to click - does nothing

So - what's left, is to recreate what the spec says the dropdown does.

  1. Type your partial text in the input.
  2. Get your option object (confirming the popup exists)
  3. get the value attribute
  4. clear and send keys to the input

This little bit of code worked for me for entering the full value:

    var input = driver.findElement(By.id("input"));
    input.sendKeys("Auto");
   
    String optionValue = driver.findElement(By.xpath("//option[contains(@value,'TEST')]")).getAttribute("value");
    input.clear();
    input.sendKeys(optionValue);

I get it's not ideal, but it's a work around and it's a start.

You'll also see I added some simple IDs to simplify my code. You'll need to update those to your identifiers


If anyone else has any ideas and things to try this is everything else i tried and didn't work with my sample page:

                   //JS error in question
        driver.findElement(By.xpath("//option[contains(@value,'TEST')]")).click();

            //can only create from select
        var select = new Select(driver.findElement(By.id("datalist")));
        select.selectByIndex(0); // only 1 option with specific text

            //element not interactable
        driver.findElement(By.id("datalist")).click();

            //This works!
        String optionValue = driver.findElement(By.xpath("//option[contains(@value,'TEST')]")).getAttribute("value");
        input.clear();
        input.sendKeys(optionValue);
        //potentially press enter if required.

            //Failed to execute 'elementsFromPoint' on 'Document': The provided double value is non-finite.
        var options =  driver.findElement(By.xpath("//option[contains(@value,'TEST')]"));
        var actions = new Actions(driver);
        actions.moveToElement(options).click().build().perform();

            //does nothing!
        var options =  driver.findElement(By.xpath("//option[contains(@value,'TEST')]"));
        var actions = new Actions(driver);
        actions.click(input).sendKeys(Keys.ARROW_DOWN).sendKeys(Keys.ENTER).build().perform();

            //does nothing - doesn't click
        var options =  driver.findElement(By.xpath("//option[contains(@value,'TEST')]"));
        JavascriptExecutor js = (JavascriptExecutor)driver;
        js.executeScript("arguments[0].click();",options);

(same variables are repeated so use one at a time)

Related