I am trying to use learn Capybara for a scraping task I have. I have heretofore only used it for testing. There are a million things I want to learn, but at the very basic part of it I want to know how to find a certain element that is a sibling and comes after another element that I am able to find?
Take a page like this:
<body>
<h3>Name1</h3>
<table>
...
</table>
<h3>Name2</h3>
<table>
...
</table>
<h3>Name3</h3>
<table>
...
</table>
</body>
I want to return the <table>
element that comes after the <h3>
element having text Name2.
I know how to loop through elements with all
, and I know how to use first
instead of find
, but I don't know how to "Find the first element X following specific element Y".
In CSS you could use a sibling selector. These allow you to select sibling elements; or those at the same nesting level and with the same parent element. There are two types of sibling selectors:
It's usually ideal to avoid matching by text whenever possible. (This makes your specs easier to write and also means that textual changes are less likely to break your specs.) In that ideal world your 'h3' elements might have IDs on them and we could just:
find('h3#name2+table')
However, in your example they don't have IDs so let's connect a couple of queries to scope to what we want.
find('h3', text: 'Name2').find('+table')
First we found the correct 'h3' element (using text matching) and then with that query as a basis we request the sibling 'table' element.
You may also note that if you used the general sibling selector '~' you would get an ambiguous element error; Capybara found all the 'table' elements rather than just the adjacent one.
Sometimes XPath is actually easier to use if you're really forced to do textual element selection. So you could instead:
find(:xpath, "//h3[contains(text(),'Name2')]/following-sibling::table")
More difficult to read but does the same thing. First find an 'h3' with text 'Name2' and then select it's sibling 'table' element.
sibling
and ancestor
finders (~>=2.15.0)[Updated 2018/09]
As @trueunlessfalse commented out, the original answer using sibling
finder will return amgibuous match error
if there are multiple matches. So, please consider to use xpath in that case..
Following code using xpath will return what OP wanted
find('h3', text: 'Name2').first(:xpath, './following-sibling::table')
Following code will return what OP wanted => ambiguous match error
find('h3', text: 'Name2').sibling('table')
You can check the detail here. https://www.rubydoc.info/github/jnicklas/capybara/Capybara/Node/Finders:sibling
Using
find('h3#name2+table')
didnt work for me. I needed to add :css
then it worked
find(:css, 'h3#name2+table')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With