A lazy, driver-independent element query.
Role locators accept these options:
:name(String.t/0) - Restrict the role locator by accessible name.:exact(boolean/0) - Require an exact, case-sensitive accessible-name match.
Text, label, placeholder, alternative-text, and title locators accept:
:exact(boolean/0) - Require an exact, case-sensitive match.
Test-id locators accept:
:attribute(String.t/0) - Attribute used as the test-id source. The default value is"data-testid".
filter/2 accepts:
:visible(boolean/0) - Filter by structural visibility in Static/Live and rendered visibility in Playwright.:exact(boolean/0) - Match the whole normalized text, case-sensitively, for has_text and has_not_text.:has(struct of typeFluffy.Locator) - Keep elements containing a match for this locator.:has_text(String.t/0) - Keep elements containing this normalized text.:has_not(struct of typeFluffy.Locator) - Keep elements containing no match for this locator.:has_not_text(String.t/0) - Keep elements that do not contain this normalized text.
Summary
Functions
Matches only elements matched by both locators, preserving the left locator's order.
Converts an iframe element query to a lazy query of its content frame.
Creates a lazy frame scope. Frame traversal requires the Playwright driver.
Creates a nested frame scope relative to an element or frame query.
Matches elements in either locator, once each in document order, retaining action strictness.
Types
@type exact_option() :: {:exact, boolean()}
@type scope() :: t() | Fluffy.FrameLocator.t()
@type t() :: %Fluffy.Locator{operations: [term()]}
@type test_id_option() :: {:attribute, binary()}
Functions
Matches only elements matched by both locators, preserving the left locator's order.
The right locator resolves from the same query root, not within each left match.
Inside filter/2's :has or :has_not, that root is the filter candidate.
by_css("#email") |> and_(by_label("Email", exact: true))With Playwright, the right locator may be relative to the left locator's frame or use the same frame prefix. Playwright validates frame compatibility when the locator resolves; intersections across different frames are unsupported.
frame = frame_locator("#checkout")
by_css(frame, "input") |> and_(by_label(frame, "Email"))Like other locators, intersections are lazy and retain normal action strictness.
@spec by_alt_text(scope(), String.t(), [exact_option()]) :: t()
@spec by_label(scope(), String.t(), [exact_option()]) :: t()
@spec by_placeholder(scope(), String.t(), [exact_option()]) :: t()
@spec by_role(scope(), atom(), [role_option()]) :: t()
@spec by_test_id(scope(), String.t(), [test_id_option()]) :: t()
@spec by_text(scope(), String.t(), [exact_option()]) :: t()
@spec by_title(scope(), String.t(), [exact_option()]) :: t()
@spec content_frame(t()) :: Fluffy.FrameLocator.t()
Converts an iframe element query to a lazy query of its content frame.
@spec filter(t(), [filter_option()]) :: t()
@spec frame_locator(String.t()) :: Fluffy.FrameLocator.t()
Creates a lazy frame scope. Frame traversal requires the Playwright driver.
@spec frame_locator(scope(), String.t()) :: Fluffy.FrameLocator.t()
Creates a nested frame scope relative to an element or frame query.
Matches elements in either locator, once each in document order, retaining action strictness.