> ## Documentation Index
> Fetch the complete documentation index at: https://docs.uplink.build/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript Execution

> Execute JavaScript code in the page context

Execute JavaScript in the browser context and inject user scripts that run on every page load.

## Methods

### `page.evaluate()`

Executes JavaScript in the page context and returns the result.

```typescript theme={null}
page.evaluate(func: Function | string, ...params: unknown[]): Promise<unknown>
```

**Parameters:**

* `func`: Function or string of JavaScript code to execute
* `params` (optional): Parameters to pass to the function

**Returns:** `Promise<unknown>` - Result of the JavaScript execution

**Examples:**

```typescript theme={null}
// Execute a function
const title = await page.evaluate(() => document.title)
console.log('Page title:', title)

// With parameters
const result = await page.evaluate((a, b) => a + b, 5, 10)
console.log('Result:', result)  // 15

// Execute a string
const html = await page.evaluate('document.body.innerHTML')

// Return complex objects
const data = await page.evaluate(() => {
  return {
    title: document.title,
    url: window.location.href,
    links: Array.from(document.querySelectorAll('a')).map(a => a.href)
  }
})
```

<Tip>
  `evaluate()` runs in the page context, so it has access to the DOM, `window`, `document`, and any JavaScript libraries loaded on the page.
</Tip>

### `page.addUserScript()`

Adds a user script that runs on every page load.

```typescript theme={null}
page.addUserScript(script: string): Promise<void>
```

**Parameters:**

* `script`: JavaScript code to execute on every page load

**Returns:** `Promise<void>`

**Example:**

```typescript theme={null}
await page.addUserScript(`
  console.log('User script loaded on:', window.location.href);

  // Add custom API
  window.customAPI = {
    version: '1.0',
    getData: () => ({ foo: 'bar' })
  };

  // Modify page behavior
  document.addEventListener('DOMContentLoaded', () => {
    console.log('DOM loaded, custom script initialized');
  });
`)

await page.goto('https://example.com')

// Access custom API
const data = await page.evaluate(() => window.customAPI.getData())
console.log(data)  // { foo: 'bar' }
```

## Complete examples

### Extract data from page

```typescript theme={null}
await page.goto('https://example.com/products')

const products = await page.evaluate(() => {
  return Array.from(document.querySelectorAll('.product')).map(el => {
    const name = el.querySelector('.product-name')?.textContent || ''
    const price = el.querySelector('.product-price')?.textContent || ''
    const image = el.querySelector('img')?.src || ''

    return {
      name: name.trim(),
      price: price.trim(),
      image
    }
  })
})

console.log(`Found ${products.length} products:`)
console.log(products)
```

### Check page state

```typescript theme={null}
const pageInfo = await page.evaluate(() => {
  return {
    title: document.title,
    url: window.location.href,
    readyState: document.readyState,
    scrollHeight: document.body.scrollHeight,
    hasJQuery: typeof window.jQuery !== 'undefined',
    hasReact: typeof window.React !== 'undefined'
  }
})

console.log('Page info:', pageInfo)
```

### Manipulate DOM

```typescript theme={null}
await page.goto('https://example.com')

// Modify the page
await page.evaluate(() => {
  // Change background color
  document.body.style.backgroundColor = '#f0f0f0'

  // Hide ads
  document.querySelectorAll('.ad, .advertisement').forEach(ad => {
    ad.style.display = 'none'
  })

  // Add custom content
  const banner = document.createElement('div')
  banner.textContent = 'Custom Banner'
  banner.style.cssText = 'position: fixed; top: 0; width: 100%; background: blue; color: white; padding: 10px; text-align: center; z-index: 9999;'
  document.body.insertBefore(banner, document.body.firstChild)
})

// Take a screenshot of modified page
const screenshot = await page.screenshot()
```

### Call page functions

```typescript theme={null}
await page.goto('https://example.com/app')

// Call a function defined on the page
const result = await page.evaluate(() => {
  // Assuming the page has a global function `fetchData()`
  if (typeof window.fetchData === 'function') {
    return window.fetchData()
  }
  return null
})

console.log('Page data:', result)
```

### Scroll and interact

```typescript theme={null}
await page.goto('https://example.com/long-page')

// Scroll to bottom
await page.evaluate(() => {
  window.scrollTo(0, document.body.scrollHeight)
})

// Wait for lazy-loaded content
await new Promise(resolve => setTimeout(resolve, 2000))

// Check scroll position
const scrollPos = await page.evaluate(() => window.scrollY)
console.log('Scrolled to:', scrollPos)

// Scroll to specific element
await page.evaluate(() => {
  document.querySelector('#target-section')?.scrollIntoView()
})
```

### Inject libraries

```typescript theme={null}
await page.goto('https://example.com')

// Inject jQuery if not present
const hasJQuery = await page.evaluate(() => typeof window.jQuery !== 'undefined')

if (!hasJQuery) {
  await page.addUserScript(`
    const script = document.createElement('script');
    script.src = 'https://code.jquery.com/jquery-3.6.0.min.js';
    document.head.appendChild(script);
  `)

  // Wait for jQuery to load
  await page.waitForFunction(() => typeof window.jQuery !== 'undefined')
  console.log('jQuery injected')
}

// Now use jQuery
const count = await page.evaluate(() => {
  return window.$('p').length
})
console.log(`Found ${count} paragraphs using jQuery`)
```

### Monitor page activity

```typescript theme={null}
await page.addUserScript(`
  window.__pageActivity = {
    clicks: 0,
    xhr: 0,
    errors: []
  };

  document.addEventListener('click', () => {
    window.__pageActivity.clicks++;
  });

  const originalFetch = window.fetch;
  window.fetch = function(...args) {
    window.__pageActivity.xhr++;
    return originalFetch.apply(this, args);
  };

  window.addEventListener('error', (e) => {
    window.__pageActivity.errors.push(e.message);
  });
`)

await page.goto('https://example.com')

// Do some automation
await page.click('#button1')
await page.click('#button2')

// Check activity
const activity = await page.evaluate(() => window.__pageActivity)
console.log('Page activity:', activity)
// { clicks: 2, xhr: 3, errors: [] }
```

### Custom form validation

```typescript theme={null}
await page.addUserScript(`
  window.customValidation = {
    validateEmail: (email) => {
      return /^[^@]+@[^@]+\\.[^@]+$/.test(email);
    },
    validatePhone: (phone) => {
      return /^\\d{10}$/.test(phone.replace(/\\D/g, ''));
    }
  };
`)

await page.goto('https://example.com/form')

// Use custom validation
const emailValid = await page.evaluate(() => {
  const email = document.querySelector('#email').value
  return window.customValidation.validateEmail(email)
})

if (!emailValid) {
  console.error('Invalid email')
}
```

## Best practices

<AccordionGroup>
  <Accordion title="Serialize return values" icon="brackets-curly">
    `evaluate()` can only return serializable values (no functions, DOM nodes, etc.):

    ```typescript theme={null}
    // Good
    const text = await page.evaluate(() => {
      return document.querySelector('h1').textContent
    })

    // Bad - can't return DOM nodes
    const element = await page.evaluate(() => {
      return document.querySelector('h1')  // Error!
    })
    ```
  </Accordion>

  <Accordion title="Handle errors in evaluate" icon="triangle-exclamation">
    Errors in `evaluate()` will be thrown in your code:

    ```typescript theme={null}
    try {
      const result = await page.evaluate(() => {
        return document.querySelector('.missing').textContent
      })
    } catch (error) {
      console.error('Error in page context:', error)
    }
    ```
  </Accordion>

  <Accordion title="Use parameters for dynamic values" icon="arrow-right-to-bracket">
    Pass values as parameters rather than using string concatenation:

    ```typescript theme={null}
    // Good
    const result = await page.evaluate((selector) => {
      return document.querySelector(selector)?.textContent
    }, ['#title'])

    // Avoid
    const selector = '#title'
    const result = await page.evaluate(`
      document.querySelector('${selector}')?.textContent
    `)
    ```
  </Accordion>

  <Accordion title="User scripts run early" icon="clock">
    User scripts run before page scripts, so you can set up monitoring or modify behavior:

    ```typescript theme={null}
    await page.addUserScript(`
      // Runs before page JavaScript
      window.addEventListener('load', () => {
        console.log('Page loaded!');
      });
    `)
    ```
  </Accordion>
</AccordionGroup>

## Related

<CardGroup cols={2}>
  <Card title="Waiting" icon="hourglass" href="/api-reference/page/waiting">
    Wait for JavaScript conditions
  </Card>

  <Card title="Interaction" icon="hand-pointer" href="/api-reference/page/interaction">
    Interact with page elements
  </Card>

  <Card title="Events" icon="bell" href="/api-reference/page/events">
    Monitor page events
  </Card>

  <Card title="Page overview" icon="window-maximize" href="/api-reference/page/overview">
    Back to Page API overview
  </Card>
</CardGroup>
