Search code examples
pythonseleniumselenium-webdriverwebdriver

Selenium find Only non-hidden and non disabled items?


In python I wrote:

login_forms = driver.find_elements(by=By.TAG_NAME, value='form')

which works perfect for getting all forms in page. Then I iterate over all inputs in form like this:

form_inputs = login_forms[0].find_elements(by=By.TAG_NAME, value='input')

How can I find only items which are not hidden or disabled (those which normal user can see and tamper or right his own value inside)?


Suggested code (which didn't work for me):

login_forms = driver.find_elements(by=By.TAG_NAME, value='form')


for login_form in login_forms:
        clean_form_inputs = login_form.find_elements(By.XPATH, "//input[not @disabled and(not(contains(@class,'disabled'))) and(not(contains(.,'display: none')))]")
        if len(clean_form_inputs) < 2:
            continue
        print(str(len(clean_form_inputs)))

I'm getting:

selenium.common.exceptions.InvalidSelectorException: Message: invalid selector: Unable to locate an element with the xpath expression //[not @disabled and(not(contains(@class,'disabled'))) and(not(contains(.,'display: none')))] because of the following error: SyntaxError: Failed to execute 'evaluate' on 'Document': The string '//[not @disabled and(not(contains(@class,'disabled'))) and(not(contains(.,'display: none')))]' is not a valid XPath expression. (Session info: chrome=99.0.4844.83)


Solution

  • Go give you a correct answer we need to know how defined hidden and disabled blocks and elements.
    I can assume that disabled element will have disabled attribute or disabled class name while hidden blocks will have display: none style value or type="hidden".
    If so, we can define the locator to locate all element that are not disabled and not hidden as following:

    "//*[not(@disabled) and(not(contains(@class,'disabled'))) and(not(contains(.,'display: none'))) and(not(@type='hidden'))]"
    

    With Selenium find_elements it will look like:

    visible_enabled_elements = driver.find_elements(By.XPATH, "//*[not(@disabled) and(not(contains(@class,'disabled'))) and(not(contains(.,'display: none'))) and(not(@type='hidden'))]")
    

    But this implementation depends on how exactly disabled and hidden elements on web site you are working on since it may differ.
    UPD
    There is no need to combine By.XPATH and By.TAG_NAME since By.XPATH already includes the element tag name.
    In the example above I used //* to locate all tag names, but if you want to select input elements only for example it will be:

    visible_enabled_elements = driver.find_elements(By.XPATH, "//input[not(@disabled) and(not(contains(@class,'disabled'))) and(not(contains(.,'display: none'))) and(not(@type='hidden'))]")