Skip to main content
Version: 10.3

Automate dynamic web elements

Most of sites are dynamic and change the page content by JS scripts while a user interacts with a page.

import com.workfusion.bot.service.SecureEntryDTO;
import com.workfusion.odf2.compiler.BotTask;
import com.workfusion.odf2.core.cdi.Requires;
import com.workfusion.odf2.core.task.AdHocTask;
import com.workfusion.odf2.core.task.TaskInput;
import com.workfusion.odf2.core.task.output.TaskRunnerOutput;
import com.workfusion.odf2.core.webharvest.rpa.RpaDriver;
import com.workfusion.odf2.core.webharvest.rpa.RpaFactory;
import com.workfusion.odf2.core.webharvest.rpa.RpaRunner;
import com.workfusion.odf2.service.ControlTowerServicesModule;
import com.workfusion.odf2.service.vault.SecretsVaultService;
import com.workfusion.rpa.driver.Driver;
import com.workfusion.rpa.helpers.RPA;
import com.workfusion.rpa.helpers.UiElement;
import com.workfusion.rpa.helpers.UiElementCollection;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.Select;
import org.slf4j.Logger;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.workfusion.rpa.helpers.RPA.*;
import static com.workfusion.rpa.helpers.UiSelectors.byImage;
import static com.workfusion.rpa.helpers.UiSelectors.byText;

@BotTask(requireRpa = true)
@Requires({ControlTowerServicesModule.class})
public class AutomateWebElements implements AdHocTask {

private final RpaRunner rpaRunner;
private final Logger logger;
private final SecretsVaultService secretsVault;

@Inject
public AutomateWebElements(RpaFactory rpaFactory, Logger logger, SecretsVaultService secretsVault){
this.rpaRunner = rpaFactory
.builder(RpaDriver.UNIVERSAL)
.closeOnCompletion(true)
.build();
this.logger = logger;
this.secretsVault = secretsVault;
}

@Override
public TaskRunnerOutput run(TaskInput taskInput) {

rpaRunner.execute(driver->{
this.automateDynamicWebElements(driver);
});

return taskInput.asResult()
.withColumn("example_bot_task_output", "completed_successfully");
}

private void automateDynamicWebElements(Driver driver){
RPA.timeouts(100 * 1000);
driver.switchDriver("chrome");
RPA.openLinkInNewWindow("https://www.alibaba.com");
$(By.xpath("//input[@class='search-bar-input']")).val("sword");
$(By.xpath("//button[text()='Search']")).click();

for(int i = 1 ; i<=5 ; i++) {
UiElement elem = $(By.xpath("//a[contains(@class,'pages-next')]")); //Switching to next 5 pages
elem.click();
RPA.sleep(5 * 1000);
}
}
}

In the example above, you access http://alibaba.com, enter a product name, and try to swap the page in the loop. If you run this Bot Task, it finishes with the exception: "unknown error: Element <a href="javascript:void(0)" class="next" data-role="next">...</a> is not clickable at point (1320, 994). Other element would receive the click..."

The exception occurs as the bot produces actions like a human. It moves the cursor to the element and sends a signal that the mouse button is pressed. This variant works in most cases, but not for this site. While the mouse cursor moves to the Next Page button, the site loads more elements, and the button changes its coordinates, so a click is sent to another UI element.

There are two main variants how to avoid errors: by using the hover() method or JavaScriptExecutor.

Click by JS

You can write a JS script that presses the button. That method is preferable since the button is pressed without scrolling and moving the cursor.

import com.workfusion.bot.service.SecureEntryDTO;
import com.workfusion.odf2.compiler.BotTask;
import com.workfusion.odf2.core.cdi.Requires;
import com.workfusion.odf2.core.task.AdHocTask;
import com.workfusion.odf2.core.task.TaskInput;
import com.workfusion.odf2.core.task.output.TaskRunnerOutput;
import com.workfusion.odf2.core.webharvest.rpa.RpaDriver;
import com.workfusion.odf2.core.webharvest.rpa.RpaFactory;
import com.workfusion.odf2.core.webharvest.rpa.RpaRunner;
import com.workfusion.odf2.service.ControlTowerServicesModule;
import com.workfusion.odf2.service.vault.SecretsVaultService;
import com.workfusion.rpa.driver.Driver;
import com.workfusion.rpa.helpers.RPA;
import com.workfusion.rpa.helpers.UiElement;
import com.workfusion.rpa.helpers.UiElementCollection;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.Select;
import org.slf4j.Logger;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.workfusion.rpa.helpers.RPA.*;
import static com.workfusion.rpa.helpers.UiSelectors.byImage;
import static com.workfusion.rpa.helpers.UiSelectors.byText;

@BotTask(requireRpa = true)
@Requires({ControlTowerServicesModule.class})
public class AutomateWebElements implements AdHocTask {

private final RpaRunner rpaRunner;
private final Logger logger;
private final SecretsVaultService secretsVault;

@Inject
public AutomateWebElements(RpaFactory rpaFactory, Logger logger, SecretsVaultService secretsVault){
this.rpaRunner = rpaFactory
.builder(RpaDriver.UNIVERSAL)
.closeOnCompletion(true)
.build();
this.logger = logger;
this.secretsVault = secretsVault;
}

@Override
public TaskRunnerOutput run(TaskInput taskInput) {

rpaRunner.execute(driver->{
this.automateDynamicWebElementsByJS(driver);
});

return taskInput.asResult()
.withColumn("example_bot_task_output", "completed_successfully");
}

private void automateDynamicWebElementsByJS(Driver driver){
RPA.timeouts(600 * 1000);
driver.switchDriver("chrome");
RPA.openLinkInNewWindow("https://www.alibaba.com");
$(By.xpath("//input[@class='search-bar-input']")).val("sword");
$(By.xpath("//button[text()='Search']")).click();

for(int i = 1 ; i<=5 ; i++) {
UiElement element = $(By.xpath("//a[contains(@class,'pages-next')]"));
driver.executeScript("arguments[0].click();", element); //Switching to next 5 pages using JS
RPA.sleep(5 * 1000);
}
}
}

Use hover() method

You can use the hover() method for loading extra elements before you click the button.

Move the cursor to the element before clicking:

    UiElement elem = $(By.xpath("//a[contains(@class,'pages-next')]")).hover();
elem.click();

However, it doesn't help as the bot needs more time to update its cash. Add sleep or wait to the code:

    UiElement elem = $(By.xpath("//a[contains(@class,'pages-next')]")).hover();
sleep(1000);
elem.click();

It still generates an exception: "stale element reference: element is not attached to the page document."

The error occurs because the page was changed, but the bot tries to access the element presented on the previous page and needs some time to reevaluate XPath. Add another sleep.

import com.workfusion.bot.service.SecureEntryDTO;
import com.workfusion.odf2.compiler.BotTask;
import com.workfusion.odf2.core.cdi.Requires;
import com.workfusion.odf2.core.task.AdHocTask;
import com.workfusion.odf2.core.task.TaskInput;
import com.workfusion.odf2.core.task.output.TaskRunnerOutput;
import com.workfusion.odf2.core.webharvest.rpa.RpaDriver;
import com.workfusion.odf2.core.webharvest.rpa.RpaFactory;
import com.workfusion.odf2.core.webharvest.rpa.RpaRunner;
import com.workfusion.odf2.service.ControlTowerServicesModule;
import com.workfusion.odf2.service.vault.SecretsVaultService;
import com.workfusion.rpa.driver.Driver;
import com.workfusion.rpa.helpers.RPA;
import com.workfusion.rpa.helpers.UiElement;
import com.workfusion.rpa.helpers.UiElementCollection;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.Select;
import org.slf4j.Logger;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.workfusion.rpa.helpers.RPA.*;
import static com.workfusion.rpa.helpers.UiSelectors.byImage;
import static com.workfusion.rpa.helpers.UiSelectors.byText;

@BotTask(requireRpa = true)
@Requires({ControlTowerServicesModule.class})
public class AutomateWebElements implements AdHocTask {

private final RpaRunner rpaRunner;
private final Logger logger;
private final SecretsVaultService secretsVault;

@Inject
public AutomateWebElements(RpaFactory rpaFactory, Logger logger, SecretsVaultService secretsVault){
this.rpaRunner = rpaFactory
.builder(RpaDriver.UNIVERSAL)
.closeOnCompletion(true)
.build();
this.logger = logger;
this.secretsVault = secretsVault;
}

@Override
public TaskRunnerOutput run(TaskInput taskInput) {

rpaRunner.execute(driver->{
this.automateDynamicWebElementsByHover(driver);
});

return taskInput.asResult()
.withColumn("example_bot_task_output", "completed_successfully");
}

private void automateDynamicWebElementsByHover(Driver driver){
RPA.timeouts(600 * 1000);
driver.switchDriver("chrome");
RPA.openLinkInNewWindow("https://www.alibaba.com");
$(By.xpath("//input[@class='search-bar-input']")).val("sword");
$(By.xpath("//button[text()='Search']")).click();

for(int i = 1 ; i<=5 ; i++) {
UiElement element = $(By.xpath("//a[contains(@class,'pages-next')]"));
driver.executeScript("arguments[0].scrollIntoView(true);", element); //Switching to next 5 pages by scrolling to next button element
element.hover();
RPA.sleep(5 * 1000);
element.click();
RPA.sleep(2 * 1000);
}
}
}