Skip to main content

Conditional Logic

One of BrowserQL's most powerful features is its ability to create dynamic, condition-based workflows using if and ifnot mutations. This allows you to build smart automation that adapts to different scenarios, handles errors gracefully, and creates branching logic based on page conditions.

Understanding Conditional Logic

Conditional logic in BrowserQL allows you to:

  • Handle different page states (loaded vs. error)
  • Respond to varying content (elements present vs. absent)
  • Create fallback behaviors (try method A, fallback to method B)
  • Build adaptive scraping (different selectors for different page layouts)

Basic Conditional Structure

The basic pattern for conditional logic in BrowserQL uses two key mutations:

  • if: Executes nested mutations when a condition is true
  • ifnot: Executes nested mutations when a condition is false
mutation ConditionalExample {
goto(url: "https://example.com") {
status
}

if(condition) {
// Do A - when condition is true
}

ifnot(condition) {
// Do B - when condition is false
}
}

Response Code Conditions

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

mutation ResponseHandling {
goto(url: "https://protected-site.com") {
status
}

# If we get a 403 Forbidden - handle bot detection
if(response: {codes: 403}) {
verify(type: cloudflare) {
solved
}

# Try navigation again after verification
goto(url: "https://protected-site.com") {
status
}
}

# If we get a successful response - proceed normally
ifnot(response: {codes: 403}) {
title {
title
}

text(selector: "h1") {
text
}
}
}

Element Presence Conditions

Check if elements exist on the page and create different workflows accordingly.

mutation CookieBannerHandling {
goto(url: "https://example.com") {
status
}

# If cookie banner exists - accept cookies
if(element: {selector: "#cookie-banner, .cookie-consent, [data-testid='cookie-banner']"}) {
click(selector: "#cookie-banner .accept, .cookie-consent .accept") {
x
y
}

waitForTimeout(time: 1000) {
time
}
}

# Whether banner existed or not, continue with main content
text(selector: "h1") {
text
}

html(selector: ".main-content") {
html
}
}

Content-Based Conditions

Make decisions based on the actual content of the page.

mutation SearchResultsHandling {
goto(url: "https://example-store.com") {
status
}

# Perform search
type(selector: ".search-input", text: "wireless headphones") {
selector
}

click(selector: ".search-button") {
x
y
}

# Wait for results to load
waitForTimeout(time: 2000) {
time
}

# If results found - extract product data
if(element: {selector: ".product-item, .search-result-item"}) {
mapSelector(selector: ".product-item") {
name: text(selector: ".product-name")
price: text(selector: ".product-price")
rating: text(selector: ".product-rating")
imageUrl: attribute(selector: ".product-image img", name: "src")
}
}

# If no results found - try alternative search
ifnot(element: {selector: ".product-item, .search-result-item"}) {
# Clear search and try broader term
click(selector: ".search-clear, .clear-button") {
x
y
}

type(selector: ".search-input", text: "headphones") {
selector
}

click(selector: ".search-button") {
x
y
}

waitForTimeout(time: 2000) {
time
}

# Extract whatever results we get
mapSelector(selector: ".product-item, .item") {
name: text(selector: ".product-name, .item-title")
price: text(selector: ".product-price, .price")
}
}
}

Advanced Conditional Patterns

Nested Conditions

You can nest conditional logic for complex decision trees:

mutation NestedConditions {
goto(url: "https://e-commerce-site.com/product/123") {
status
}

# First level: Check if product exists
if(element: {selector: ".product-details"}) {

# Second level: Check if product is in stock
if(element: {selector: ".in-stock, .available"}) {

# Third level: Check for discount
if(element: {selector: ".discount, .sale-price"}) {
# Product exists, in stock, and on sale
productData: mapSelector(selector: ".product-details") {
name: text(selector: ".product-name")
originalPrice: text(selector: ".original-price")
salePrice: text(selector: ".sale-price")
discount: text(selector: ".discount-percent")
}
}

ifnot(element: {selector: ".discount, .sale-price"}) {
# Product exists, in stock, regular price
productData: mapSelector(selector: ".product-details") {
name: text(selector: ".product-name")
price: text(selector: ".price")
availability: text(selector: ".stock-status")
}
}
}

ifnot(element: {selector: ".in-stock, .available"}) {
# Product exists but out of stock
productData: mapSelector(selector: ".product-details") {
name: text(selector: ".product-name")
status: text(selector: ".out-of-stock, .unavailable")
restockDate: text(selector: ".restock-date")
}
}
}

ifnot(element: {selector: ".product-details"}) {
# Product doesn't exist - capture error info
errorInfo: text(selector: ".error-message, .not-found") {
text
}
}
}

Fallback Strategies

Create robust automation with multiple fallback options:

mutation FallbackStrategies {
goto(url: "https://dynamic-site.com") {
status
}

# Try primary selector
if(element: {selector: ".primary-selector"}) {
primaryData: text(selector: ".primary-selector") {
text
}
}

ifnot(element: {selector: ".primary-selector"}) {
# Try secondary selector
if(element: {selector: ".secondary-selector"}) {
secondaryData: text(selector: ".secondary-selector") {
text
}
}

ifnot(element: {selector: ".secondary-selector"}) {
# Try tertiary selector
if(element: {selector: ".tertiary-selector"}) {
tertiaryData: text(selector: ".tertiary-selector") {
text
}
}

ifnot(element: {selector: ".tertiary-selector"}) {
# Last resort - get any text content
fallbackData: text(selector: "body") {
text
}
}
}
}
}

Best Practices for Conditional Logic

1. Plan Your Decision Tree

Before writing conditional logic, map out all possible scenarios:

  • What conditions might you encounter?
  • What should happen in each case?
  • What are your fallback strategies?

2. Use Descriptive Aliases

Make your conditional logic readable by using meaningful aliases:

# Good - descriptive aliases
handleCookieBanner: if(element: {selector: ".cookie-banner"}) {
acceptCookies: click(selector: ".accept-cookies") { x y }
}

extractProductData: ifnot(element: {selector: ".out-of-stock"}) {
productInfo: mapSelector(selector: ".product") {
name: text(selector: ".title")
price: text(selector: ".price")
}
}

3. Handle Edge Cases

Always consider what happens when multiple conditions could be true or when none are true:

mutation RobustHandling {
# ... navigation code ...

# Handle multiple possible states
if(element: {selector: ".loading"}) {
waitForTimeout(time: 5000) { time }
}

if(element: {selector: ".error"}) {
errorMessage: text(selector: ".error") { text }
}

if(element: {selector: ".success"}) {
successData: html(selector: ".content") { html }
}

# Always have a catch-all
ifnot(element: {selector: ".loading, .error, .success"}) {
fallbackScreenshot: screenshot { base64 }
}
}

4. Test All Branches

Ensure you test both the if and ifnot branches of your logic to make sure they work as expected.

Common Use Cases

Conditional logic in BrowserQL is particularly useful for:

  • Error handling and recovery
  • Adapting to different page layouts
  • Handling dynamic content loading
  • Creating robust web scraping scripts
  • Building adaptive automation workflows
  • Managing different user interface states

Next Steps

Now that you understand conditional logic, explore these related topics: