seleniumでeachなどで作られた一覧の要素全てに対して操作をする@ruby
例えば「投稿一覧」のような画面で各投稿を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番目から取得するので問題ない。