Skip to main content
SeleniumDecoded

Click a Link That Opens a New Tab and Switch to It

Detect the new window handle after a click, switch to it, do work, and return to the original tab without guessing handle order.

Stable Verified against Selenium 4.48.0

Problem

A link with target="_blank" opens a new tab. Your driver is still focused on the original tab, and the handle list order is not guaranteed, so “switch to the last handle” is unreliable.

Solution

Record the handles before the click, wait for the count to grow, and switch to the handle that was not there before.

Diff the handle set
Selenium 4 Stable
String original = driver.getWindowHandle();
Set<String> before = driver.getWindowHandles();
driver.findElement(By.linkText("Terms of service")).click();
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.numberOfWindowsToBe(before.size() + 1));
String newTab = driver.getWindowHandles().stream()
.filter(h -> !before.contains(h))
.findFirst().orElseThrow();
driver.switchTo().window(newTab);
wait.until(ExpectedConditions.titleContains("Terms"));
// ... assertions in the new tab ...
driver.close(); // close the new tab
driver.switchTo().window(original); // back to where we started
original = driver.current_window_handle
before = set(driver.window_handles)
driver.find_element(By.LINK_TEXT, "Terms of service").click()
wait = WebDriverWait(driver, 10)
wait.until(EC.number_of_windows_to_be(len(before) + 1))
new_tab = (set(driver.window_handles) - before).pop()
driver.switch_to.window(new_tab)
wait.until(EC.title_contains("Terms"))
# ... assertions ...
driver.close()
driver.switch_to.window(original)
const original = await driver.getWindowHandle();
const before = new Set(await driver.getAllWindowHandles());
await driver.findElement(By.linkText('Terms of service')).click();
await driver.wait(async () => (await driver.getAllWindowHandles()).length === before.size + 1, 10000);
const newTab = (await driver.getAllWindowHandles()).find((h) => !before.has(h));
await driver.switchTo().window(newTab);
await driver.wait(until.titleContains('Terms'), 10000);
// ... assertions ...
await driver.close();
await driver.switchTo().window(original);
var original = driver.CurrentWindowHandle;
var before = driver.WindowHandles.ToHashSet();
driver.FindElement(By.LinkText("Terms of service")).Click();
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => d.WindowHandles.Count == before.Count + 1);
var newTab = driver.WindowHandles.First(h => !before.Contains(h));
driver.SwitchTo().Window(newTab);
wait.Until(d => d.Title.Contains("Terms"));
// ... assertions ...
driver.Close();
driver.SwitchTo().Window(original);

Why It Works

Handles are opaque and unordered, but the set difference before and after the click identifies the new one unambiguously. Waiting on the handle count avoids a race where the click returns before the tab exists.

Gotchas

  • After driver.close() you must switch to another handle before any other command, or you get NoSuchWindowException.
  • If the app opens the link in the same tab on some browsers (popup blockers), the count never grows; assert the URL of the current tab as a fallback.
  • To avoid the second tab entirely, read the href and driver.get() it in the same tab, if the test is about the destination rather than the link behaviour.

Learn the theory