-
Notifications
You must be signed in to change notification settings - Fork 0
Paths
Paths are important because they let you tell the browser how to find an element on the page, such as a Next Link.
To help you visualize the elements you are making paths for (and to help go along with the examples), it's recommended you use your browser's DevTools to see the rendered HTML and elements on the page. You can also follow along by using the Element Picker.
There are three different path types supported. The following list also includes the label abbreviations sometimes used (in parenthesis).
- Selector (SE)
- XPath (XP)
- JS Paths (JS)
Both path types accomplish the same thing and help you tell the browser how to find and select an element on the page, but each has its own syntax and features.
Selectors (or more formerly, CSS Selectors) are the language used in stylesheets (.css
files) and were intended to be used by developers to help "select" (or find) elements to style on the page. For that reason, they're generally more popular and accessible.
XPath is a powerful query language that offers more features and functions, but is harder to master. For example, with XPath, you can select an element based on its textContent (something you can't do with Selectors), like: "select the anchor that has the text 'next page' inside of it."
If you're a complete beginner, it's recommended to start with Selectors as XPath queries can become quite verbose and complex.
Here are some examples that may help you write your own CSS Selector and XPath expressions. These examples use single quote '
characters, but you could also replace them with double quote "
characters.
This isn't important to understand for beginners, but you will notice that the XPath expressions listed here are prefixed with a //
. For example, instead of a[@id='foo']
, it's written as //a[@id='foo']
. The reason for this is due to the element most likely not being the root node (which is typically html
on most web pages you visit). The //
basically tells XPath that the element isn't a direct descendant and it needs to search for it. Otherwise, you'd need to write a "full" path to the element starting from the root node, like: /html/body/div/a[@id='foo']
.
This path selects an a
(anchor) element with the id of foo
:
- Selector:
a#foo
- XPath:
//a[@id='foo']
orid('foo')
This path selects an a
element with the class name of foo
:
- Selector:
a.foo
- XPath:
//a[@class='foo']
This path selects an a
element that contains the class foo
(amongst other class names). Note that you could replace class
with a different attribute or property.
- XPath:
//a[contains(@class, 'foo')]
This path selects an a
element that contains the exact text content, foo
:
- XPath:
//a[.='foo']
This path selects an a
element that contains the substring text content, foo
somewhere in it. This is a more relaxed constraint of the equals version, as this will match elements that might contain leading/trailing spaces, new lines, or other text:
- XPath:
//a[contains(.,'foo')]
;
This path selects a span
's next sibling, an a
element. Selecting the next sibling is something you may commonly need to use when writing a path for a next link. For example, on many websites, the current page number (say you're on Page 1
) is the only thing unique about the path to the next link. So we use the fact that it's a span
(and not an a
like Page 2
and the other page numbers) and select its following sibling a
, the next page link.
- Selector:
span.current + a
- XPath:
//span[@class='current']/following-sibling::a
Consider a situation where you have a series of a
elements in a paginated list that have no unique attribute designating the next link other than the next link always being the last element in the subtree. This can be achieved by using XPath's last()
function or CSS Selector's :last-child
or :last-of-type
pseudo classes:
- XPath:
//div[@class='pagination-list']/a[last()]
- Selector:
div.pagination-list > a:last-child
This is more of an advanced topic, but you may need to write a path that targets a parent element that contains a child element. CSS Selectors can only target the child itself (not the parent), so we need to leverage XPath for this.
For example, consider a list of paginated links like 1 2 3 4
. This is a list (ul
), with each list item (li
) containing the link (a
). The HTML looks like this:
<ul class="pagination">
<li>
<a class="current-page">1</a>
</li>
<li>
<a>2</a>
</li>
<li>
<a>3</a>
</li>
<li>
<a>4</a>
</li>
</ul>
We want to target the li
parent that has the a
child with the class current-page
. In this case, that would be the first li
(Page 1). To do this, we can write our XPath like so:
- XPath:
//li[./a[@class='current-page']]
Taking this a step further, to make this more useful, let's say we want to write a path that goes to the next li
's a
, so that it targets the next page. In other words, if the current page is Page 1, then it will give us Page 2. This can be done like so:
- XPath:
//li[./a[@class='current-page']]/following-sibling::li[1]/a
Finally, what if the a
is not a direct child of the li
(instead, the li
has a div
that contains the a
)? Simply add two backslashes before the a
instead of one like so:
- XPath:
//li[.//a[@class='current-page']]
This is the opposite of the above and selects a parent li
that doesn't contain a a
child with the class of current-page
.
- XPath:
//li[not(.//a[@class='current-page'])]
This is not common, but you may run into a situation where you want to append certain elements, but not others (say, an ad or a repetitive element like a footer that is part of the Page Element Path). For example, consider a situation where you want to append the following content
div, but only the divs inside of it that don't have the class ad
:
<div id="content">
<div class="ad"></div>
<div>...</div>
<div>...</div>
<div>...</div>
</div>
With XPath, you can do this using the not()
function. Here's an example:
- XPath:
//id('content')/*[not(self::div[contains(@class, 'ad')])]
XPath can return multiple elements that match. For some actions like Click Element, the app's algorithm always internally picks the last match it finds. If you want it to pick the first one instead, you may need to add a [1]
at the end of the expression. Here's an example:
//div[@class='current-page-number']/following-sibling::div[1]
A note about SVG Elements and XPath: When dealing with SVG Elements, you'll need to be careful in how you write paths for them. The reason is because they belong to a different namespace URI. So, to select an SVG, instead of writing //svg
, write //*[name()='svg']
.
JS (JavaScript) Paths can let you write paths that can find their way through shadow roots and same-origin iframes. You'll rarely need to write a JS Path, but you may run into a situation where you might need them for a site or two.
JS Paths should always start with document.querySelector
and be written as a chained set of context Selectors. XPath is not supported. (This is consistent with how your browser's DevTools generates its own JS Paths.)
For example, consider a website that stores its next link inside a shadow root. Your JS Path could therefore look something like this:
document.querySelector("#shadow").shadowRoot.querySelector("#next-link")
If the site is encapsulating its content inside iframes (of the same origin of the site), your JS Path could be written like this:
document.querySelector("#iframe").contentDocument.querySelector("#next-link")
And in the extremely rare case that it's using multiple shadow roots or iframes, or even a mixture of the two, your JS Path could be written as a complex chain of selectors like this:
document.querySelector("#shadow").shadowRoot.querySelector("#iframe").contentDocument.querySelector("#next-link")
The Element Picker can also generate JS Paths, and will do so automatically if you happen to click on an element that is inside an iframe or shadow root.
In short, use JS Paths when you need to select elements that are in a different context than the top-level document, like an iframe or shadow root.
My goal in writing this section was to have it serve as a quick-start and reference guide. If you'd like to learn more, there are several guides and tutorials on the web. Just search for "CSS Selector Tutorial" or "XPath Tutorial" and I'm sure you'll find a great guide!