Селен: 3.141 в Java

Проблема: как сбросить коллекцию в цикле for-each?

WebElement columnid = driver.findElement(By.id("id")); 
List<WebElement> getTaskName = columnid.findElements(By.xpath("//*[contains(@class,'card-title')]")); //gets the list of first 20 elements i.e., 1-20
for(Iterator<WebElement> ite = getTaskName.iterator(); ite.hasNext();)
{
    WebElement el = ite.next();
    JavascriptExecutor jse = (JavascriptExecutor) driver; 
    jse.executeScript("arguments[0].scrollIntoView();",el); //scroll-down one element
    ite.remove(); //have added remove and modified for-each loop with collection to iterator. it doesn't throw concurrentmodifiedexception. 
    getTaskName = columnid.findElements(By.xpath("//*[contains(@class,'card-title')]")); //this gets the list of next 20 elements i.e., 2-21
}

Ожидаемый результат: я хочу сбросить getTaskName элементами от 2 до 21 и повторить итерацию каждого элемента.

Фактический результат: getTaskName всегда имеет значение 1-20, повторяется от 1 до 20 и завершается.

Пожалуйста, дайте мне знать, если что-то отсутствует или неверно в приведенном выше образце кода. Спасибо.

0
sridattas 15 Окт 2019 в 10:43
Добавляются ли элементы в DOM динамически, если они прокручиваются в поле зрения? В противном случае я не понимаю, почему вы получаете разные элементы для внутрициклового вызова columnid.findElements.
 – 
dpr
15 Окт 2019 в 10:51
Если вы хотите clear коллекцию getTaskName внутри цикла forEach, вы получите ConcurrentModificationException. В противном случае вам нужно будет сделать это с помощью iterator
 – 
Vault23
15 Окт 2019 в 11:00
Да, элементы добавляются в DOM динамически после прокрутки.
 – 
sridattas
16 Окт 2019 в 12:31
Да, и поэтому я использовал итератор, но вижу проблему.
 – 
sridattas
16 Окт 2019 в 12:31
Я использовал коллекции, чтобы заменить список, это сработало. сброс сейчас в порядке.
 – 
sridattas
6 Ноя 2019 в 15:02

2 ответа

Не уверен, что это помогает, но однажды у меня была аналогичная проблема, и я исправил ее с помощью итераций в обратном направлении.

Список заполняется числами от 0 до 4. При этом не удаляются все элементы списка:

for (int i = 0; i < arr.size (); i++) {
  arr.remove (i);
}

Output = [1, 3]

Но это работает отлично:

for (int i = arr.size () - 1; i >= 0; i--) {
  arr.remove (i);
}

Выход = []

0
Tygrahe 15 Окт 2019 в 11:41

Ваш код не работает, потому что первое выражение в операторе for (Iterator<WebElement> ite = getTaskName.iterator()) оценивается только один раз: перед началом первой итерации.

Таким образом, getTaskName = columnid.findElements(...) действительно переназначает getTaskName на новый List, но ваш цикл for по-прежнему повторяет итератор, который вы получили в первый раз. Этот итератор привязан к экземпляру List, из которого он был получен, и не будет автоматически переключать списки, если вы повторно назначите getTaskName.

Предполагая, что вы хотите сохранить свой цикл, вам нужно будет преобразовать его в цикл while и переназначить сам итератор новому экземпляру итератора, например:

WebElement columnid = driver.findElement(By.id("id")); 
List<WebElement> getTaskName = columnid.findElements(By.xpath("//*[contains(@class,'card-title')]")); //gets the list of first 20 elements i.e., 1-20
Iterator<WebElement> ite = getTaskName.iterator();
while(ite.hasNext())
{
    WebElement el = ite.next();
    JavascriptExecutor jse = (JavascriptExecutor) driver; 
    jse.executeScript("arguments[0].scrollIntoView();",el); //scroll-down one element
    ite.remove(); //have added remove and modified for-each loop with collection to iterator. it doesn't throw concurrentmodifiedexception. 
    getTaskName = columnid.findElements(By.xpath("//*[contains(@class,'card-title')]")); //this gets the list of next 20 elements i.e., 2-21
    ite = getTaskName.iterator();
}

Обратите внимание, что это кажется мне не очень чистым. Использование итератора только для получения первого элемента кажется совершенно ненужным. Вместо этого вы можете сделать это:

WebElement columnid = driver.findElement(By.id("id")); 
List<WebElement> getTaskName = columnid.findElements(By.xpath("//*[contains(@class,'card-title')]")); //gets the list of first 20 elements i.e., 1-20
while(!getTaskName.isEmpty())
{
    WebElement el = getTaskName.get(0);
    JavascriptExecutor jse = (JavascriptExecutor) driver; 
    jse.executeScript("arguments[0].scrollIntoView();",el); //scroll-down one element
    getTaskName = columnid.findElements(By.xpath("//*[contains(@class,'card-title')]")); //this gets the list of next 20 elements i.e., 2-21
}

В идеале вам, вероятно, следует настроить свой запрос XPath так, чтобы он возвращал только первый элемент в списке.


В качестве отдельной проблемы я не думаю, что то, что вы пытаетесь сделать, сработает. Насколько я помню из своего опыта работы с Selenium, удаление элемента из List, возвращаемого findElements(), НЕ приводит к удалению этого элемента из DOM страницы. Таким образом, запрос columnid.findElements() с одним и тем же запросом XPath всегда будет возвращать точно такой же список WebElement. Поэтому, если какой-либо другой скрипт на веб-странице автоматически не удаляет первый элемент DOM в списке при его прокрутке до представления, ваш цикл всегда будет зависать на первом элементе.

0
LordOfThePigs 15 Окт 2019 в 12:06
Спасибо за подробный анализ. Я попробую оба способа вместе с другими пунктами и статусом публикации здесь.
 – 
sridattas
16 Окт 2019 в 12:35
Я думаю, что оба решения не будут работать должным образом, поскольку, если вы достигли конца своего списка и не добавили / не удалили новые элементы, вы всегда будете получать одни и те же элементы в цикле. Я думаю, @sridattas, вам нужно отслеживать элементы, которые вы уже посетили, чтобы исключить их из xpath.
 – 
dpr
16 Окт 2019 в 12:37