Skip to main content

Conditional Logic in BQL

if and ifnot are BrowserQL mutations that execute nested operations based on a condition. if runs its body when the condition matches; ifnot runs its body when it does not. Neither can be nested inside the other. Use sequential conditionals with compound selectors for multi-branch logic.

warning

Conditions are evaluated immediately. if and ifnot do not wait for a selector to appear or a response to arrive. If the element isn't already on the page when the condition runs, it evaluates as not present. Use waitForSelector or waitForResponse before your conditional if timing matters.

Arguments

ArgumentTypeAvailable onDescription
selectorStringif, ifnotTriggers if the CSS selector matches an element on the page.
visibleBooleanif onlyWhen using selector, requires the element to be visible in the viewport. Defaults to false.
responseResponseInputif, ifnotTriggers based on HTTP response status codes or URL pattern.
requestRequestInputif, ifnotTriggers based on a matching HTTP request.

ResponseInput fields:

  • statuses: [Int]: one or more HTTP status codes to match
  • url: String: glob-style URL pattern to match the response URL

RequestInput fields:

  • method: Method: HTTP verb to match (GET, POST, PUT, DELETE, PATCH, and others)
  • url: String: glob-style URL pattern to match the request URL

If a condition is not met, the branch returns null and execution continues. A skipped condition is not an error.

For the full schema, see the API reference.

Element Presence Conditions

Check if a selector is present on the page and run different operations depending on the result. Because if and ifnot are evaluated independently and in sequence, you can chain them to cover multiple branches in a single mutation. A common pattern is checking for a CAPTCHA before proceeding: if one is found, solve it and continue; if not, extract content directly:

mutation HandleCaptcha {
goto(url: "https://www.google.com/recaptcha/api2/demo") {
status
}

# No CAPTCHA - extract content directly
ifnot(selector: ".g-recaptcha") {
html {
html
}
}

# CAPTCHA detected - solve it before proceeding
if(selector: ".g-recaptcha") {
solve {
time
found
solved
}
click(selector: "#recaptcha-demo-submit") {
time
}
waitForNavigation {
time
}
html {
html
}
}
}

Visible Elements

By default, selector matches any element in the DOM, including those hidden with CSS. Add visible: true to if to require the element to also be rendered and visible in the viewport. This is useful for elements like modals, error messages, or menus that exist in the DOM before they are shown:

mutation VisibleElementCheck {
goto(url: "https://practicetestautomation.com/practice-test-login/") {
status
}

# Only fires if #error is in the DOM and visible — not just present but hidden
if(selector: "#error", visible: true) {
text(selector: "#error") {
text
}
}
}

Response Code Conditions

One of the most common uses is handling different HTTP response codes.

mutation ResponseHandling {
goto(url: "https://quotes.toscrape.com/doesntexist") {
status
}

# 4xx means access was denied or the resource wasn't found — capture the text and redirect
ifError4xx: if(response: {statuses: [400, 401, 403, 404]}) {
text {
text
}
goto(url: "https://quotes.toscrape.com") {
status
}
}

# Successful response — extract the main content
ifnot(response: {statuses: [400, 401, 403, 404, 500, 502, 503]}) {
html(selector: ".col-md-8") {
html
}
}
}

Request Conditions

request triggers based on HTTP requests the page has made. Use url to match a glob-style URL pattern, method to match an HTTP verb, or both together:

mutation RequestCondition {
goto(url: "https://jsonplaceholder.typicode.com/posts/1") {
status
}

# Fires if a GET request to the posts endpoint was made
if(request: {method: GET, url: "**/posts/**"}) {
text(selector: "body") {
text
}
}

# No matching request detected — take a screenshot for debugging
ifnot(request: {method: GET, url: "**/posts/**"}) {
screenshot {
base64
}
}
}

Next Steps