J. Doe
J. Doe

Reputation: 183

Detect user visible elements(only in viewport) by xpath in selenium, Python

I am trying to only click on elements that would actually be visible to a user, i.e. don't click on elements outside the where I am currently scrolled to on a website.

I manage to detect all the ones that are hidden, but how to get a list of only elements a normal user would be able to see.

Upvotes: 0

Views: 3309

Answers (1)

Andrei
Andrei

Reputation: 5647

If I right understand you, you are locating a bunch of elements like this:

elements = driver.find_elements_by_xpath("//a")

it will give you a list of elements. Some of this elements are visible, some of them not. To prove it you can use this:

for element in elements:
    print(element.is_displayed()) # prints true if displayed, otherwise false

Try this code snippet:

driver = webdriver.Chrome("C:\\path\\to\\chromedriver.exe")
url = "https://stackoverflow.com/"
driver.get(url)

elements = driver.find_elements_by_xpath("//a") # finds all elements by given xPath
for element in elements: # for every element in elements list
    print(element.is_displayed()) # print 'true' if displayed, or 'false' if not

print("DONE")

Output:

False
True
False
True
True
True
True
True
True
False
False
False
False
...
DONE

NOTE: it is only an example, normally you have to wait until the page fully loads to locate all elements correctly and also get the correct state of them.

EDIT: I have found a nice solution in this question and the sample code in this case would be like this:

driver = webdriver.Chrome("C:\\path\\to\\chromedriver.exe")
url = "https://stackoverflow.com/"
driver.get(url)

time.sleep(3)
elements = driver.find_elements_by_xpath("//span")

def element_in_viewport(driver, elem):
    elem_left_bound = elem.location.get('x')
    elem_top_bound = elem.location.get('y')
    elem_width = elem.size.get('width')
    elem_height = elem.size.get('height')
    elem_right_bound = elem_left_bound + elem_width
    elem_lower_bound = elem_top_bound + elem_height

    win_upper_bound = driver.execute_script('return window.pageYOffset')
    win_left_bound = driver.execute_script('return window.pageXOffset')
    win_width = driver.execute_script('return document.documentElement.clientWidth')
    win_height = driver.execute_script('return document.documentElement.clientHeight')
    win_right_bound = win_left_bound + win_width
    win_lower_bound = win_upper_bound + win_height

    return all((win_left_bound <= elem_left_bound,
                win_right_bound >= elem_right_bound,
                win_upper_bound <= elem_top_bound,
                win_lower_bound >= elem_lower_bound)
               )

for element in elements:
    print(element_in_viewport(driver, element))

print("DONE")

Output:

True
True
True
True
True
True
True
True
True
True
True
True
True
False
True
True
True
True
False
False
False
False
False
False
False
...
DONE

In my perspective this code snippet works good.

Upvotes: 2

Related Questions