Recommended Free Tools
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
In Selenium 3, find a DOM element by passing a locator from By to findElement; use findElements when you want every match or need to handle zero matches without an exception. For example, use By.id('username') for a stable unique ID or By.css('input[name="password"]') for a CSS selector. These locator patterns are useful for maintaining an existing PhantomJS 2.1.1 suite, but PhantomJS support is legacy: Selenium removed native PhantomJS support because its WebDriver implementation was no longer actively developed. For new test suites, use a maintained headless Chrome or Firefox driver; the Selenium locator concepts carry over.
Find one element or a collection
The essential distinction is whether you expect one match or want to inspect zero or more matches:
findElement(locator)returns the first matching element. If nothing matches, Selenium raises a no-such-element error.findElements(locator)returns a collection of matching elements. If nothing matches, it returns an empty collection.
In JavaScript Selenium 3, construct a locator with By and pass it to the driver. This example opens a page, finds form controls and a set of results, then closes the browser even if an operation fails:
const {Builder, By} = require('selenium-webdriver');
(async function () {
const driver = await new Builder().forBrowser('phantomjs').build();
try {
await driver.get('https://example.test/login');
const username = await driver.findElement(By.id('username'));
const password = await driver.findElement(By.css('input[name="password"]'));
const results = await driver.findElements(By.css('.result'));
await username.sendKeys('alice');
await password.sendKeys('secret');
console.log(`Found ${results.length} result elements`);
} finally {
await driver.quit();
}
})();
Replace the example page and selectors with the application under test. The example assumes the page has loaded the controls by the time the searches run; if it renders them asynchronously, use an explicit wait as described below. The phantomjs browser target is a legacy Selenium JavaScript integration, not a recommendation for a new project.
#1 Best Overall
Choose a locator that will survive page changes
Prefer a selector that identifies the intended element directly and expresses as little accidental page structure as possible. A compact, readable locator is generally easier to debug and maintain than a long traversal through nested elements.
ID: first choice when it is unique and stable
Use By.id('username') when the page has a stable, unique id. IDs are direct and readable, and Selenium guidance prefers unique IDs when available. Do not rely on an ID that changes on each page load or is duplicated in the rendered DOM.
CSS selector: practical fallback
When no suitable ID exists, a concise CSS selector is usually the next choice. Examples include By.css('form input[name="email"]'), By.css('#checkout button.submit'), and By.css('[data-testid="save"]'). Scope a selector to a meaningful form or container if a page has repeated controls, rather than selecting the first matching element across the whole document.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Name: target a stable form attribute
Use By.name('email') when the element’s name attribute is stable. This can be clear for form fields, though a page may contain multiple controls with the same name; use findElements or a scoped CSS selector if that is intentional.
Rank #2
Class name: pass one class token
By.className('information') finds elements carrying that class. The traditional class-name strategy accepts one class token, not a compound string such as 'form control'. To require multiple classes or combine a class with an attribute, use CSS, for example By.css('.form.control').
Link text and partial link text: anchor wording
By.linkText('Sign in') matches an anchor by its link text; By.partialLinkText('Sign') matches part of that text. These strategies are for links, not arbitrary buttons. Exact link text can be affected by wording changes, localization or whitespace, so prefer a stable ID or CSS attribute when the visible wording is not a reliable identifier.
Tag name: useful for collecting element types
By.tagName('button') can collect all buttons or help inspect a known container’s children. A tag alone often matches many elements, so scope it or use findElements when multiplicity is expected.
XPath: use for relationships or conditions CSS cannot express
By.xpath('//form//input[@name="email"]') identifies an input with a particular name inside a form. XPath can express relationships and conditions flexibly, but complex expressions are typically harder to read and maintain. Use it when the relationship matters and a compact CSS selector cannot express it cleanly.
Rank #3
Python syntax in Selenium 3 environments that still expose PhantomJS
Python uses the same locator strategies with uppercase constants from By:
from selenium import webdriver
from selenium.webdriver.common.by import By
# In Selenium 3 environments that still expose the PhantomJS binding:
driver = webdriver.PhantomJS(executable_path='/path/to/phantomjs')
try:
driver.get('https://example.test/login')
username = driver.find_element(By.ID, 'username')
password = driver.find_element(By.CSS_SELECTOR, 'input[name="password"]')
results = driver.find_elements(By.CSS_SELECTOR, '.result')
print(len(results))
finally:
driver.quit()
This is legacy maintenance syntax, not a setup recommendation for a new Python test environment. Selenium deprecated PhantomJS in Selenium 3.8.1, and native PhantomJS support was later removed. If this binding is unavailable in the Selenium version installed in your environment, use a maintained browser driver instead of assuming the old constructor remains supported. The locator idea remains the same: pass By.ID, By.CSS_SELECTOR or another locator strategy to find_element or find_elements.
Keep an existing PhantomJS 2.1.1 driver running
PhantomJS 2.1.1 is a headless browser release built on Qt 5.5-based WebKit. Its embedded GhostDriver can expose a WebDriver endpoint from the command line:
phantomjs --webdriver=PORT
PhantomJS documents the default endpoint as 127.0.0.1:8910. Replace PORT with the port you intend to use if configuring a specific endpoint. Selenium’s legacy forBrowser('phantomjs') integration may manage the browser process for you, as in the JavaScript example; running GhostDriver directly is relevant when maintaining a setup that connects to its WebDriver endpoint. Do not treat either route as evidence that PhantomJS is maintained or compatible with current Selenium releases.
Rank #4
When to migrate instead
For a new suite, choose a maintained headless Chrome or Firefox driver. Selenium’s project histories record the PhantomJS deprecation and removal because its WebDriver implementation was no longer actively developed; Selenium recommended headless Chrome or Firefox instead. Migration effort is usually concentrated in browser setup and browser-specific behavior, not the locator vocabulary: By, findElement/find_element, and findElements/find_elements carry over conceptually. Check your selected driver and Selenium binding’s current compatibility details before changing the environment.
Diagnose “element not found”
A no-such-element error means the locator returned no match at the time Selenium searched. Work through the likely causes in this order:
- Check the rendered DOM and locator. Confirm the page actually contains the intended element and that the ID, attribute, text or tag in the locator matches its rendered value. A source template is not necessarily the same as the live DOM after scripts run.
- Wait for asynchronous content. A successful navigation does not guarantee that an application has finished inserting a control. Use the explicit-wait facility for your language binding to wait for the specific element or condition, rather than adding an arbitrary delay everywhere. Keep the wait bounded so a genuinely missing element fails instead of hanging the test.
- Check for an iframe. Elements inside an iframe are not found from the top-level document. Switch the driver to the relevant frame before searching, then return to the default content when done. A correct selector against the wrong document context still finds nothing.
- Reduce ambiguity and scope the search. If the page has repeated controls, search within a stable form or container. Confirm the locator matches the element you mean rather than merely the first item with a common class or tag.
- Choose the right lookup method. Use
findElementswhen zero results are a valid outcome, such as checking whether optional content exists. An empty collection is not an exception. UsefindElementwhen one matching element is required and absence should fail the operation.
Found does not mean interactable
Finding an element and interacting with it are separate checks. An element hidden with CSS may exist in the DOM and be returned by a locator, yet not be visible or usable for typing or clicking. If the search succeeds but an interaction fails, inspect visibility and the element’s state rather than making the locator broader. Also verify that the page has not replaced the element after you found it; a stored element reference can become stale when the DOM changes.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteOr skip the browser setup
If your goal is a visual capture rather than locating or interacting with DOM elements, ScreenshotNeo can return a screenshot with one GET request. It does not replace Selenium for finding elements, filling forms or validating application behavior.
Best Value
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.test/login -o shot.webp
See the ScreenshotNeo documentation for API options. Cookie banners, newsletter popups and chat widgets are removed before the shot; each of those steps can be turned off. Bot checks, blank pages, failed loads, timeouts and cache hits cost nothing, and the response includes page-verdict and billing headers. An MCP server offers take_screenshot, get_page_info and capture_pdf tools for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Sign up for ScreenshotNeo’s free plan to try 1,000 screenshots a month without a card.
Frequently asked questions
Does a headless browser change how Selenium locators work?
No. Headless describes how the browser runs, not a separate locator language. Selenium still searches the document using the same locator strategies; the browser’s support status and behavior are separate considerations.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Can I use a Selenium locator to find an element in a screenshot?
No. Selenium locators search the browser’s DOM, not pixels in an image. A screenshot is useful for visual inspection, while DOM-based automation needs a WebDriver session and a locator.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

