seleniumでeachなどで作られた一覧の要素全てに対して操作をする
例えば「投稿一覧」のような画面で各投稿を1つずつクリックして開きたい時のパターンを想定。
操作する要素の例
こんな感じのやつを想定。多分よくあるやつ。
各post以下のlinkを1つずつ全てクリックしたい。
div class="index"
div class="post"
p hoge class="title"
a class="link"
div class="post"
p huga class="title"
a class="link"
div class="post"
p hugahoge class="title"
a class="link"
取得し直すごとにオブジェクトが変わる場合がある
Selenium::WebDriver::Elementオブジェクトのfind_elementsは要素を取得し直すごとに各種値が変わってしまう場合がある。
# 子要素に対してクリック操作をしたいループ
@driver.find_elements(:class, "index").each do |element|
element.find_element(:class, "hoge").click
end
# 1回目の取得
> @driver.find_elements(:class, "index")
[#<Selenium::WebDriver::Element:0x..f693ec4bf80c6eee id="2d75628b-0b8f-4d92-a7db-7e242210b403">,
#<Selenium::WebDriver::Element:0xbc5fe1f4062f650 id="462b6e29-ef2f-4f1f-bad4-5acb980c74e5">,
#<Selenium::WebDriver::Element:0x687a1921db97e860 id="193e8b33-669a-4381-8b74-039dcd379dd5">]
# 要素を一つクリックして新しいタブで開く
> element.find_element(:class, "hoge").click
# 2回目の取得
> @driver.find_elements(:class, "index")
=> [#<Selenium::WebDriver::Element:0x..f9a79e5201fcda17a id="08f92ae9-4ecc-4af0-b98b-94eb2bd9b843">,
#<Selenium::WebDriver::Element:0x..fd075870720e89f2a id="a2d55dd1-1fab-4918-b95e-dfde8b2da705">,
#<Selenium::WebDriver::Element:0x..fd3599185fcb5576a id="375c0957-1cfd-485f-be29-b8397d9ec2f1">]
# ループ2回目では以下のエラーが出る
Selenium::WebDriver::Error::StaleElementReferenceError: stale element reference: element is not attached to the page document
上記の場合の対処方法の例
取得するごとにオブジェクト番号が変わってしまっては雑にeachで回してクリックすることはできなくなる。なので、以下のように対処した。
current_window = @driver.window_handles.first
# 要素の総数を取得
index_elements_count = @driver.find_elements(:class, "index").count
index_elements_count.times do |time|
@driver.find_elements(:class, "index")[time].find_element(class: "link").click
@driver.switch_to.window(current_window)
end
これなら@driver.find_elements(:class, "index")の値がループごとに変わっても、配列のtime番目から取得するので問題ない。