How to switch windows and tabs in Selenium

New-tab handoffs can leave a Selenium test sending commands to the wrong browser context after an OAuth login, payment redirect, document preview, or popup creates a second tab or window. WebDriver does not follow the desktop browser focus automatically; the script must switch to the handle for the context it needs to control.

WebDriver represents every tab and window as a window handle. In Python, driver.current_window_handle returns the active handle, driver.window_handles lists the open handles, and driver.switch_to.window(handle) moves later commands to that handle. Selenium 4 can also create a tab or window with driver.switch_to.new_window(“tab”) or driver.switch_to.new_window(“window”).

Store the original handle before opening the second context, wait for the expected handle count when the application opens a tab, and return to a valid handle before continuing. Closing a tab without switching back leaves later commands aimed at a closed page and can raise NoSuchWindowException.

Steps to switch Selenium windows and tabs:

  1. Create switch_window_tab.py with a handled tab switch and return path.
    switch_window_tab.py
    from urllib.parse import quote
     
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.support.ui import WebDriverWait
     
     
    def data_page(title, heading):
        html = f"""<!doctype html>
    <html lang="en">
    <head><meta charset="utf-8"><title>{title}</title></head>
    <body><h1>{heading}</h1></body>
    </html>"""
        return "data:text/html;charset=utf-8," + quote(html)
     
     
    options = Options()
    options.add_argument("--headless=new")
    options.add_argument("--window-size=1280,720")
     
    driver = webdriver.Chrome(options=options)
     
    try:
        wait = WebDriverWait(driver, 10)
     
        driver.get(data_page("Original Context", "Original window"))
        original_handle = driver.current_window_handle
     
        print(f"original_title: {driver.title}")
        print(f"handles_before: {len(driver.window_handles)}")
     
        driver.switch_to.new_window("tab")
        new_handle = driver.current_window_handle
        driver.get(data_page("Second Context", "Second tab"))
        wait.until(EC.title_is("Second Context"))
     
        print(f"handles_after_open: {len(driver.window_handles)}")
        print(f"active_after_new_tab: {driver.title}")
     
        driver.switch_to.window(original_handle)
        print(f"active_after_return: {driver.title}")
     
        driver.switch_to.window(new_handle)
        driver.close()
        driver.switch_to.window(original_handle)
     
        print(f"handles_after_close: {len(driver.window_handles)}")
        print(f"active_final: {driver.title}")
    finally:
        driver.quit()

    If Chrome does not launch, install Selenium for Python or configure the Chrome driver first. Use window instead of tab in new_window() when the browser context must open as a separate window.
    Related: How to install Selenium WebDriver for Python
    Related: How to configure ChromeDriver for Selenium

  2. Run the script.
    $ python3 switch_window_tab.py
    original_title: Original Context
    handles_before: 1
    handles_after_open: 2
    active_after_new_tab: Second Context
    active_after_return: Original Context
    handles_after_close: 1
    active_final: Original Context

    handles_after_open should be 2 while the second tab is open, and active_after_return should show the original title after switch_to.window(original_handle).

  3. Use the handle-difference pattern when the application opens the tab itself.
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
     
     
    original_handle = driver.current_window_handle
    driver.find_element(By.LINK_TEXT, "Open report").click()
    wait.until(EC.number_of_windows_to_be(2))
     
    new_handle = (set(driver.window_handles) - {original_handle}).pop()
    driver.switch_to.window(new_handle)

    Store the original handle before the click. Do not rely on a fixed index when another popup, extension page, or authentication window can appear first.

  4. Remove the temporary smoke-test script after moving the handle logic into the project test.
    $ rm switch_window_tab.py