0

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.

JeffC
  • 22,180
  • 5
  • 32
  • 55
J. Doe
  • 183
  • 2
  • 9

1 Answers1

2

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.

Andrei Suvorkov
  • 5,559
  • 5
  • 22
  • 48
  • Thanks a lot for the above, the problem is that it still shows elements which a user would not be able to see, as it is outside the display area on the screen, i.e. I would have to scroll down further to see this. – J. Doe Jul 07 '18 at 14:08
  • Ah I see what you need. But why do you need this? the user is also able to scroll the page. – Andrei Suvorkov Jul 07 '18 at 14:10
  • Yeah the problem is that if I want to scrap a page looking like a user, then I can't press elements which the user shouldnt be able to see. Which the website can detect as it can detect how far down I scrolled using javascript – J. Doe Jul 07 '18 at 14:20
  • @J.Doe I think I have found a solution for your problem, please check the updated answer. – Andrei Suvorkov Jul 07 '18 at 14:34