Locate by classes in a specific order


In HTML, the class attribute is a space-separated list of classes for the element. The order of classes does not matter when applying style sheets. For example, in the following page, all 3 div elements would look the same even though their class value is different.

    <div class="a b">1</div> 
    <div class="b a">2</div>
    <div class="a b">3</div>

For many years, Watir’s :class locator has been order dependent when using multiple classes:

# Only matches the exact order "a" and then "b"
browser.divs(class: 'a b').map(&:text)
#=> ["1", "3"]

# Only matches the exact order "b" and then "a"
browser.divs(class: 'b a').map(&:text)
#=> ["2"]

Watir 6.5 introduced the ability to be order independent. This is done by providing an Array of classes:

browser.divs(class: ['a', 'b']).map(&:text)
#=> ["1", "2", "3"]

browser.divs(class: ['b', 'a']).map(&:text)
#=> ["1", "2", "3"]

For backwards compatibility, the decision was to leave the existing order dependent behaviour for String values as-is. However, a deprecation warning will be generated:

browser.divs(class: 'a b').map(&:text)
#=> 2017-08-16 21:50:17 WARN Watir [DEPRECATION] 
#=>   Using the :class locator to locate multiple classes with 
#=>   a String value is deprecated. Use Array instead.

Hopefully no one has to deal with a class order requirement, but if you do, what can you do to prevent the deprecation warning?


The new handling for Array values still builds the XPath the same as when a String value is used. The only difference is that the Array value will “and” the items together. With only a single item in the Array, the behaviour is the same as using a String. As well, the Array is not checked for items with spaces – ie is not subject to the deprecation warning.

This means that the order dependent classes can simply be passed as one of the Array elements:

browser.divs(class: ['a b']).map(&:text)
#=> ["1", "3"]
browser.divs(class: ['b a']).map(&:text)
#=> ["2"]

The XPath generated for the deprecated approach and this workaround only differ by an unimportant set of brackets:

# Using browser.divs(class: 'a b')
{"using":"xpath","value":".//div[contains(concat(' ', @class, ' '), ' a b ')]"}

# Using browser.divs(class: ['a b'])
{"using":"xpath","value":".//div[(contains(concat(' ', @class, ' '), ' a b '))]"}
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:

WordPress.com Logo

You are commenting using your WordPress.com 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