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 trueifnot
: 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.
- Basic Response Handling
- Multiple 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
}
}
}
mutation MultipleResponseHandling {
goto(url: "https://example.com") {
status
}
# Handle client errors (4xx)
if(response: {codes: [400, 401, 403, 404]}) {
screenshot {
base64
}
text(selector: "body") {
text
}
}
# Handle server errors (5xx)
if(response: {codes: [500, 502, 503]}) {
waitForTimeout(time: 5000) {
time
}
# Retry the request
goto(url: "https://example.com") {
status
}
}
# Handle successful responses (2xx)
ifnot(response: {codes: [400, 401, 403, 404, 500, 502, 503]}) {
html(selector: "main") {
html
}
}
}
Element Presence Conditions
Check if elements exist on the page and create different workflows accordingly.
- Cookie Banner Handling
- Different Login Forms
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
}
}
mutation AdaptiveLogin {
goto(url: "https://app.example.com/login") {
status
}
# Check for email/password form
if(element: {selector: "input[type='email'], input[name='email']"}) {
type(selector: "input[type='email'], input[name='email']", text: "user@example.com") {
selector
}
type(selector: "input[type='password']", text: "password123") {
selector
}
click(selector: "button[type='submit'], .login-btn") {
x
y
}
}
# Check for username/password form (different structure)
ifnot(element: {selector: "input[type='email'], input[name='email']"}) {
if(element: {selector: "input[name='username'], #username"}) {
type(selector: "input[name='username'], #username", text: "myusername") {
selector
}
type(selector: "input[type='password']", text: "password123") {
selector
}
click(selector: "button[type='submit'], .submit-btn") {
x
y
}
}
}
}
Content-Based Conditions
Make decisions based on the actual content of the page.
- Search Results Handling
- Content Variations
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")
}
}
}
mutation ContentVariations {
goto(url: "https://news-site.com") {
status
}
# Check for paywall
if(element: {selector: ".paywall, .subscription-required, [data-paywall]"}) {
# Try to find free preview content
text(selector: ".article-preview, .free-content") {
text
}
# Look for alternative free articles
mapSelector(selector: ".free-article") {
title: text(selector: ".article-title")
excerpt: text(selector: ".article-excerpt")
link: attribute(selector: "a", name: "href")
}
}
# If no paywall - get full article content
ifnot(element: {selector: ".paywall, .subscription-required, [data-paywall]"}) {
title: text(selector: "h1, .article-title") {
text
}
content: html(selector: ".article-content, .post-content") {
html
}
author: text(selector: ".author, .byline") {
text
}
publishDate: text(selector: ".publish-date, .date") {
text
}
}
}
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:
- Advanced Options - More complex BrowserQL features
- Exporting Scripts - Convert BQL to other formats
- Bot Detection - Handle complex detection scenarios