简体   繁体   中英

Selenium DeselectAll not working for HTML SELECT with multiple attribute

I am using this HTML code :

<select name="cars" multiple>
  <option value="volvo">Volvo</option>
  <option value="saab">Saab</option>
  <option value="opel">Opel</option>
  <option value="audi">Audi</option>
</select>

Manually I select some items. Then I want to deselect all of them (I am using C# but that does not matter):

var carsElement = BrowserDriver.FindElementByName("cars");
var carsSelect = new SelectElement(carsElement);
carsSelect.DeselectAll();

What happens: The first selected options stays selected, others are unselected.

Looking at the code this is what must happen, because DeselectAll() calls Click() for all selected options. You can try that in your browser. This will never unselect all options (unless you hold CTRL while clicking but that is not done by the Selenium code). So the correct way would be to change DeselectAll to press CTRL while clicking as demonstrated by How to perform Control key down in selenium webdriver?

Bottom line, I know how to fix this; my questions are: Am I missing anything? Is there an easier way? Is SelectElement not intended from HTML SELECT multiple?

您可以肯定地取消选择这些

browser.execute_script("[...document.querySelectorAll('[name=cars] option')].map(o => o.selected = false)")

Select class can handle multiple choice dropdown. It even checks if the dropdown is multiple choice when using DeselectAll() . From github

public void DeselectAll()
{
    if (!this.IsMultiple)
    {
        throw new InvalidOperationException("You may only deselect all options if multi-select is supported");
    }

    foreach (IWebElement option in this.Options)
    {
        SetSelected(option, false);
    }
}

private static void SetSelected(IWebElement option, bool select)
{
    bool isSelected = option.Selected;
    if ((!isSelected && select) || (isSelected && !select))
    {
         option.Click();
    }
}

When you click on the first select option without pressing the control key this option remains selected but all the other options are being deselected, so the click is actually not performed on the rest of the options since both isSelected and select are false in SetSelected .

The solution is either implement your own DeselectAll() as suggested in your question, or select the first option from the dropdown (which will automatically deselect all the other options) and then deselect this option using control.

Even though I did not ask for code how to set the selection state, I'll post it anyways for anybody who encountered the same problem. This is C# code. You'll need NuGet packages Selenium.WebDriver and Selenium.Support . BrowserDriver is a member variable of my helper class which contains this method.

/// <summary>
/// Sets the selection state of <paramref name="selectElement"/>. All options specified  by <paramref name="selectedOptions"/>
/// are select, all others are unselected.
/// </summary>
/// <param name="selectElement">HTML select element</param>
/// <param name="selectedOptions">options to be selected</param>
internal void SelectStateByText(OpenQA.Selenium.Support.UI.SelectElement selectElement, params string[] selectedOptions)
{
    Assert.IsNotNull(selectElement);
    Assert.IsNotNull(selectedOptions);
    CollectionAssert.IsSubsetOf(selectedOptions, selectElement.Options.Select(o => o.Text).ToArray());
    if (!selectElement.IsMultiple)
    {
        Assert.AreEqual(1, selectedOptions.Length);
        selectElement.SelectByText(selectedOptions[0]);
    }
    else
    {
        var actions = new OpenQA.Selenium.Interactions.Actions(BrowserDriver);
        actions.KeyDown(Keys.LeftControl);
        foreach (var option in selectElement.Options)
        {
            if (selectedOptions.Contains(option.Text) && !option.Selected)
            {
                actions.Click(option);
            }
            else if (option.Selected)
            {
                actions.Click(option);
            }
        }
        actions.KeyUp(Keys.LeftControl).Build().Perform();
    }
}

I have verified your usecase of using the deselect_all() method through the Selenium Python Client and it seems to work perfecto .

deselect_all()

deselect_all() method clears all selected entries. This is only valid when the SELECT supports multiple selections. throws NotImplementedError If the SELECT does not support multiple selections.


Illustration

Note : As you have mentioned in your question I have also simulated the selection of all the items manually

  • Code Block:

     from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import Select import time options = webdriver.ChromeOptions() options.add_argument("start-maximized") options.add_argument('disable-infobars') driver=webdriver.Chrome(chrome_options=options, executable_path=r'C:\\Utility\\BrowserDrivers\\chromedriver.exe') driver.get('https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_select_multiple') WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.ID,"iframeResult"))) select_cars = Select(driver.find_element_by_css_selector("select[name='cars']")) time.sleep(5) # Timeframe to Manually select all the items select_cars.deselect_all()
  • Browser Snapshot:

取消全选.gif

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM