Finding visible/hidden elements with the new :visible locator

Watir 6.0 introduces a new locator – :visible. This allows you to specify whether or not the element can be seen by the user.


Consider the following page where the first div is visible and first span element is hidden:

    <div id="visible_div">can be seen by users</div>
    <div id="hidden_div" style="display:none;">cannot be seen by users</div>

    <span id="hidden_span" style="display:none;">cannot be seen by users</span>
    <span id="visible_span">can be seen by users</span>

By default, Watir locates the first matching element regardless of visibility:
#=> "visible_div"
#=> "hidden_span"

By adding visible: true, the first matching visible element is returned:

browser.div(visible: true).id
#=> "visible_div"

browser.span(visible: true).id
#=> "visible_span"

By adding visible: false, the first matching hidden element is returned:

browser.div(visible: false).id
#=> "hidden_div"

browser.span(visible: false).id
#=> "hidden_span"

Not available for collections

In at least version 6.0.2, the :visible locator is not available for collections. Hopefully this is just an oversight and will be added in future releases – Issue 499.

browser.divs(visible: true).count
#=> C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/xpath_support.rb:5:in `escape': undefined method `include?' for true:TrueClass (NoMethodError)
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/locators/element/selector_builder/xpath.rb:48:in `equal_pair'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/locators/element/selector_builder/xpath.rb:32:in `block in attribute_expression'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/locators/element/selector_builder/xpath.rb:28:in `each'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/locators/element/selector_builder/xpath.rb:28:in `map'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/locators/element/selector_builder/xpath.rb:28:in `attribute_expression'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/locators/element/selector_builder/xpath.rb:18:in `build'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/locators/element/selector_builder.rb:122:in `build_xpath'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/locators/element/selector_builder.rb:103:in `build_wd_selector'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/locators/element/selector_builder.rb:49:in `build'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/locators/element/locator.rb:140:in `find_all_by_multiple'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/locators/element/locator.rb:60:in `locate_all'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/element_collection.rb:98:in `elements'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/element_collection.rb:84:in `to_a'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/element_collection.rb:28:in `each'
#=>   from watir-webdriver.rb:98:in `count'
#=>   from watir-webdriver.rb:98:in `<main>'

Performance impact

For the locator to work, Watir iterates through the elements to check their visibility. This can be seen in the Watir::Locators::Element::Locator. A wire call, el.displayed? is made for each potentially matching element:

elements = @query_scope.wd.find_elements(how, what)
elements = { |el| visible == el.displayed? } unless visible.nil?
elements[idx] unless elements.nil?

In other words, a simple call like browser.div(visible: true).id will need to check every single div on the page. Large number of elements, means slower execution. The following shows the increasing execution time based on the number of elements on the page:

Executed 10 Times 1 div 10 divs 50 divs 100 divs 0.966645 1.011956 0.943130 0.931430
browser.div(visible: true).id 1.107269 2.511261 8.877038 17.534622

You may want to consider:

  • Only using :visible when necessary – ie as a tie-breaker for a locator that matches both a visible and hidden element.
  • When using :visible, use other locators to limit the potential matches (wire calls).

Does not change interaction rules

Note that this is just a locator. It does not change the fact that the element is hidden and therefore cannot be interacted with.

browser.div(visible: false).click
#=> C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/elements/element.rb:528:in `rescue in wait_for_present': element located, but timed out after 30 seconds, waiting for true condition on {:visible=>false, :tag_name=>"div"} (Watir::Exception::UnknownObjectException)
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/elements/element.rb:518:in `wait_for_present'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/elements/element.rb:535:in `wait_for_enabled'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/elements/element.rb:658:in `element_call'
#=>   from C:/Ruby23/lib/ruby/gems/2.3.0/gems/watir-6.0.2/lib/watir/elements/element.rb:114:in `click'
#=>   from watir-webdriver.rb:99:in `<main>'
This entry was posted in Watir and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s