or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mddom-apis.mdindex.mdjsdom-class.mdresources.mdtroubleshooting.mdvirtual-console.md
tile.json

index.mddocs/

0

# jsdom

1

2

jsdom is a pure-JavaScript implementation of many web standards, notably the WHATWG DOM and HTML Standards, for use with Node.js. It provides a complete browser-like environment for testing and scraping web applications without requiring an actual browser.

3

4

## Quick Start

5

6

> **Best Practice:** Always wrap jsdom usage in try-finally to ensure cleanup, even on errors.

7

8

### Parse HTML and Extract Data

9

10

```javascript

11

const { JSDOM } = require("jsdom");

12

13

const html = '<div class="item"><h2>Title</h2><p>Description</p></div>';

14

const dom = new JSDOM(html);

15

16

try {

17

const { document } = dom.window;

18

19

const title = document.querySelector("h2")?.textContent || "";

20

const description = document.querySelector("p")?.textContent || "";

21

22

console.log({ title, description }); // { title: 'Title', description: 'Description' }

23

} finally {

24

dom.window.close(); // Always cleanup

25

}

26

```

27

28

### Setup for Testing

29

30

```javascript

31

const { JSDOM } = require("jsdom");

32

33

function setupDOM(html) {

34

const dom = new JSDOM(html || '<!DOCTYPE html><body></body>', {

35

url: "https://example.org/",

36

referrer: "https://google.com/",

37

includeNodeLocations: true

38

});

39

global.window = dom.window;

40

global.document = dom.window.document;

41

global.navigator = dom.window.navigator;

42

return dom;

43

}

44

45

function teardownDOM(dom) {

46

if (dom && dom.window) dom.window.close();

47

delete global.window;

48

delete global.document;

49

delete global.navigator;

50

}

51

52

const dom = setupDOM('<div id="test">Hello</div>');

53

try {

54

const element = document.getElementById("test");

55

console.log(element.textContent); // "Hello"

56

} finally {

57

teardownDOM(dom);

58

}

59

```

60

61

### Scrape a Web Page (with retry)

62

63

```javascript

64

const { JSDOM } = require("jsdom");

65

66

async function scrapePage(url, maxRetries = 3) {

67

let lastError;

68

69

for (let attempt = 1; attempt <= maxRetries; attempt++) {

70

let dom = null;

71

try {

72

dom = await JSDOM.fromURL(url, {

73

url, // Important: set URL for resource loading

74

resources: "usable",

75

runScripts: "dangerously",

76

pretendToBeVisual: true

77

});

78

79

const { document } = dom.window;

80

const result = {

81

title: document.title,

82

links: Array.from(document.querySelectorAll("a[href]")).map(a => a.href),

83

html: dom.serialize()

84

};

85

86

dom.window.close(); // Cleanup on success

87

return result;

88

} catch (error) {

89

lastError = error;

90

if (dom) dom.window.close(); // Cleanup on error

91

92

if (attempt < maxRetries) {

93

await new Promise(r => setTimeout(r, 1000 * attempt));

94

}

95

}

96

}

97

98

throw new Error(`Failed to scrape ${url} after ${maxRetries} attempts: ${lastError?.message}`);

99

}

100

101

scrapePage("https://example.com")

102

.then(data => console.log(data))

103

.catch(error => console.error(error));

104

```

105

106

## Quick Reference

107

108

| Task | Code |

109

|------|------|

110

| Create DOM | `const dom = new JSDOM('<p>Hello</p>');` |

111

| Access document | `const { document } = dom.window;` |

112

| Parse fragment | `const frag = JSDOM.fragment('<li>Item</li>');` |

113

| Load from URL | `const dom = await JSDOM.fromURL(url);` |

114

| Load from file | `const dom = await JSDOM.fromFile('page.html');` |

115

| Serialize | `dom.serialize()` |

116

| Cleanup | `dom.window.close();` |

117

118

## Decision Tree: Which Option to Use?

119

120

```

121

Need to parse HTML?

122

├─ Simple extraction? → JSDOM.fragment() (fastest)

123

├─ Need full document? → new JSDOM(html)

124

└─ Need external resources? → new JSDOM(html, { resources: "usable" })

125

126

Need to execute scripts?

127

├─ Trusted content? → { runScripts: "dangerously" }

128

└─ Untrusted content? → { runScripts: "outside-only" }

129

130

Need to test code?

131

├─ Setup before each test → setupDOM()

132

├─ Global access needed → global.window = dom.window

133

└─ Always cleanup → dom.window.close() in finally

134

135

Need to scrape?

136

├─ Single page → JSDOM.fromURL()

137

├─ Multiple pages → Shared CookieJar

138

└─ Need resilience → Retry logic with cleanup

139

```

140

141

## Common Patterns

142

143

### Execute Scripts Safely

144

145

```javascript

146

const dom = new JSDOM(`<!DOCTYPE html><body></body>`, {

147

url: "https://example.org/",

148

runScripts: "outside-only"

149

});

150

151

try {

152

dom.window.eval(`document.body.innerHTML = '<p>Added</p>';`);

153

console.log(dom.window.document.body.innerHTML);

154

} catch (error) {

155

console.error("Script execution failed:", error);

156

} finally {

157

dom.window.close();

158

}

159

```

160

161

### Capture Console Output

162

163

```javascript

164

const { JSDOM, VirtualConsole } = require("jsdom");

165

166

const virtualConsole = new VirtualConsole();

167

virtualConsole.on("log", (msg) => console.log("Page:", msg));

168

virtualConsole.on("error", (msg) => console.error("Page error:", msg));

169

virtualConsole.on("jsdomError", (err) => console.error("jsdom error:", err.message));

170

171

const dom = new JSDOM(`<script>console.log("Hello");</script>`, {

172

runScripts: "dangerously",

173

virtualConsole

174

});

175

```

176

177

### Manage Cookies

178

179

```javascript

180

const { JSDOM, CookieJar } = require("jsdom");

181

182

const cookieJar = new CookieJar();

183

184

const dom1 = new JSDOM(``, { url: "https://example.com/", cookieJar });

185

dom1.window.document.cookie = "session=abc123; path=/";

186

187

const dom2 = new JSDOM(``, { url: "https://example.com/", cookieJar });

188

console.log(dom2.window.document.cookie); // "session=abc123"

189

```

190

191

### Integration: Jest Testing

192

193

```javascript

194

const { JSDOM } = require("jsdom");

195

196

let dom;

197

198

beforeEach(() => {

199

dom = new JSDOM(`<!DOCTYPE html><body id="root"></body>`, {

200

url: "https://example.org/",

201

includeNodeLocations: true

202

});

203

204

global.window = dom.window;

205

global.document = dom.window.document;

206

global.navigator = dom.window.navigator;

207

});

208

209

afterEach(() => {

210

if (dom) dom.window.close();

211

delete global.window;

212

delete global.document;

213

delete global.navigator;

214

});

215

216

test("should manipulate DOM", () => {

217

const root = document.getElementById("root");

218

root.innerHTML = "<p>Test</p>";

219

220

expect(root.querySelector("p").textContent).toBe("Test");

221

});

222

```

223

224

### Integration: Mocha/Chai

225

226

```javascript

227

const { JSDOM } = require("jsdom");

228

const { expect } = require("chai");

229

230

describe("DOM Operations", () => {

231

let dom;

232

233

beforeEach(() => {

234

dom = new JSDOM(`<div id="app"></div>`, {

235

url: "https://example.org/"

236

});

237

global.window = dom.window;

238

global.document = dom.window.document;

239

});

240

241

afterEach(() => {

242

if (dom) dom.window.close();

243

delete global.window;

244

delete global.document;

245

});

246

247

it("should find elements", () => {

248

const app = document.getElementById("app");

249

expect(app).to.not.be.null;

250

});

251

});

252

```

253

254

### Integration: Vitest

255

256

```javascript

257

import { describe, it, expect, beforeEach, afterEach } from "vitest";

258

import { JSDOM } from "jsdom";

259

260

describe("jsdom integration", () => {

261

let dom;

262

263

beforeEach(() => {

264

dom = new JSDOM(`<!DOCTYPE html><body><h1>Test</h1></body>`, {

265

url: "https://example.org/"

266

});

267

global.window = dom.window;

268

global.document = dom.window.document;

269

});

270

271

afterEach(() => {

272

dom?.window.close();

273

delete global.window;

274

delete global.document;

275

});

276

277

it("should parse HTML", () => {

278

const h1 = document.querySelector("h1");

279

expect(h1.textContent).toBe("Test");

280

});

281

});

282

```

283

284

## Core APIs

285

286

```javascript { .api }

287

/**

288

* Create a new jsdom instance

289

* @param input - HTML string or binary data (optional)

290

* @param options - Configuration options

291

*/

292

class JSDOM {

293

constructor(input?: string | Buffer | ArrayBuffer | TypedArray, options?: JSDOMOptions);

294

readonly window: Window;

295

readonly virtualConsole: VirtualConsole;

296

readonly cookieJar: CookieJar;

297

serialize(): string;

298

nodeLocation(node: Node): LocationInfo | undefined;

299

getInternalVMContext(): object;

300

reconfigure(settings: { windowTop?: any, url?: string }): void;

301

302

static fromURL(url: string, options?: JSDOMOptions): Promise<JSDOM>;

303

static fromFile(filename: string, options?: JSDOMOptions): Promise<JSDOM>;

304

static fragment(string?: string): DocumentFragment;

305

}

306

307

interface JSDOMOptions {

308

url?: string; // Document URL (default: "about:blank")

309

referrer?: string; // Document referrer

310

contentType?: string; // Content type (default: "text/html")

311

includeNodeLocations?: boolean; // Track source locations

312

storageQuota?: number; // localStorage quota (default: 5000000)

313

runScripts?: "dangerously" | "outside-only"; // Script execution mode

314

resources?: "usable" | ResourceLoader; // Resource loading

315

pretendToBeVisual?: boolean; // Enable animation APIs

316

virtualConsole?: VirtualConsole; // Console output handler

317

cookieJar?: CookieJar; // Cookie storage

318

beforeParse?: (window: Window) => void; // Pre-parse callback

319

}

320

```

321

322

## Best Practices

323

324

1. **Always close**: `dom.window.close()` in finally block prevents memory leaks

325

2. **Set proper URL**: Enables relative URLs and cookies

326

3. **Use `runScripts: "outside-only"`**: Safe script execution

327

4. **Handle errors**: Wrap in try-catch for production code

328

5. **Cleanup globals**: Remove test globals after tests

329

6. **Use fragments**: For simple parsing without full document overhead

330

7. **Share CookieJar**: For maintaining sessions across requests

331

8. **Set resources**: Only when needed to avoid unnecessary network requests

332

9. **Enable location tracking**: Use `includeNodeLocations: true` for debugging

333

10. **Monitor console**: Use VirtualConsole to capture and handle page errors

334

335

## Package Information

336

337

- **Package Name**: jsdom

338

- **Package Type**: npm

339

- **Language**: JavaScript (CommonJS)

340

- **Installation**: `npm install jsdom`

341

- **Node.js Requirement**: v20 or newer

342

- **Repository**: https://github.com/jsdom/jsdom

343

344

## Architecture

345

346

jsdom is built around several key components:

347

348

- **JSDOM Class**: Main interface for creating and managing DOM instances

349

- **Window Object**: Browser-like global environment with complete web APIs

350

- **Virtual Console**: Captures console output and jsdom errors for external handling

351

- **Resource Loading**: Configurable system for fetching external resources (scripts, stylesheets, images)

352

- **Cookie Management**: HTTP cookie storage and handling via CookieJar

353

- **Script Execution**: Sandboxed JavaScript execution with configurable security levels

354

- **Parser**: HTML/XML parsing using parse5 for standards-compliant document creation

355

356

## Capabilities

357

358

### JSDOM Constructor

359

360

The primary way to create jsdom instances from HTML strings or binary data.

361

362

```javascript { .api }

363

/**

364

* Create a new jsdom instance

365

* @param input - HTML string or binary data (Buffer, ArrayBuffer, TypedArray)

366

* @param options - Configuration options

367

* @returns JSDOM instance with window, document, and DOM APIs

368

*/

369

class JSDOM {

370

constructor(input?: string | Buffer | ArrayBuffer | TypedArray, options?: JSDOMOptions);

371

372

/** The Window object containing the DOM and browser APIs */

373

window: Window;

374

375

/** The VirtualConsole instance for this jsdom */

376

virtualConsole: VirtualConsole;

377

378

/** The CookieJar instance for this jsdom */

379

cookieJar: CookieJar;

380

381

/** Serialize the document to an HTML string including DOCTYPE */

382

serialize(): string;

383

384

/** Get source code location of a node (requires includeNodeLocations option) */

385

nodeLocation(node: Node): LocationInfo | undefined;

386

387

/** Get Node.js VM context for advanced script execution */

388

getInternalVMContext(): object;

389

390

/** Reconfigure the jsdom after creation */

391

reconfigure(settings: { windowTop?: any, url?: string }): void;

392

}

393

394

interface JSDOMOptions {

395

url?: string;

396

referrer?: string;

397

contentType?: string;

398

includeNodeLocations?: boolean;

399

storageQuota?: number;

400

runScripts?: "dangerously" | "outside-only";

401

resources?: "usable" | ResourceLoader;

402

pretendToBeVisual?: boolean;

403

virtualConsole?: VirtualConsole;

404

cookieJar?: CookieJar;

405

beforeParse?: (window: Window) => void;

406

}

407

```

408

409

[JSDOM Class](./jsdom-class.md)

410

411

### Configuration Options

412

413

Comprehensive options for customizing jsdom behavior including URL handling, script execution, resource loading, and rendering simulation.

414

415

```javascript { .api }

416

interface JSDOMOptions {

417

/** Document URL (default: "about:blank") */

418

url?: string;

419

/** Document referrer */

420

referrer?: string;

421

/** Content type for parsing (default: "text/html") */

422

contentType?: string;

423

/** Enable node location tracking for source mapping */

424

includeNodeLocations?: boolean;

425

/** Storage quota for localStorage/sessionStorage in code units */

426

storageQuota?: number;

427

/** Script execution mode */

428

runScripts?: "dangerously" | "outside-only";

429

/** Resource loading configuration */

430

resources?: "usable" | ResourceLoader;

431

/** Pretend to be a visual browser */

432

pretendToBeVisual?: boolean;

433

/** Virtual console for output capture */

434

virtualConsole?: VirtualConsole;

435

/** Cookie jar for cookie storage */

436

cookieJar?: CookieJar;

437

/** Callback before HTML parsing */

438

beforeParse?: (window: Window) => void;

439

}

440

```

441

442

[Configuration](./configuration.md)

443

444

### Convenience Factory Methods

445

446

Static methods on JSDOM for creating instances from URLs or files.

447

448

```javascript { .api }

449

/**

450

* Create jsdom by fetching from a URL

451

* @param url - URL to fetch (HTTP/HTTPS)

452

* @param options - Configuration options (url and contentType not allowed)

453

* @returns Promise resolving to JSDOM instance

454

*/

455

static fromURL(url: string, options?: JSDOMOptions): Promise<JSDOM>;

456

457

/**

458

* Create jsdom by reading from a file

459

* @param filename - File path relative to current working directory

460

* @param options - Configuration options

461

* @returns Promise resolving to JSDOM instance

462

*/

463

static fromFile(filename: string, options?: JSDOMOptions): Promise<JSDOM>;

464

465

/**

466

* Create a DocumentFragment from HTML (lightweight, no full document)

467

* @param string - HTML string to parse

468

* @returns DocumentFragment with parsed content

469

*/

470

static fragment(string?: string): DocumentFragment;

471

```

472

473

[JSDOM Class](./jsdom-class.md)

474

475

### Virtual Console

476

477

EventEmitter-based console for capturing output from within jsdom, including page console calls and jsdom implementation errors.

478

479

```javascript { .api }

480

/**

481

* Create a virtual console for capturing output

482

*/

483

class VirtualConsole extends EventEmitter {

484

constructor();

485

486

/**

487

* Forward console output to another console

488

* @param anyConsole - Console object to forward to

489

* @param options - Control jsdom error forwarding

490

* @returns This VirtualConsole for chaining

491

*/

492

forwardTo(

493

anyConsole: Console,

494

options?: { jsdomErrors?: "none" | string[] }

495

): VirtualConsole;

496

}

497

```

498

499

VirtualConsole emits events for all console methods: `"log"`, `"warn"`, `"error"`, `"info"`, `"debug"`, `"trace"`, `"dir"`, `"dirxml"`, `"assert"`, `"count"`, `"countReset"`, `"group"`, `"groupCollapsed"`, `"groupEnd"`, `"table"`, `"time"`, `"timeLog"`, `"timeEnd"`, `"clear"`, and special `"jsdomError"` event.

500

501

[Virtual Console](./virtual-console.md)

502

503

### Resource Loading

504

505

Configurable system for loading external resources including custom loaders and proxy support.

506

507

```javascript { .api }

508

/**

509

* Resource loader for fetching external resources

510

*/

511

class ResourceLoader {

512

constructor(options?: {

513

strictSSL?: boolean;

514

proxy?: string;

515

userAgent?: string;

516

});

517

518

/**

519

* Fetch a resource from a URL

520

* @param urlString - URL to fetch

521

* @param options - Request options

522

* @returns Promise for Buffer with abort() method

523

*/

524

fetch(

525

urlString: string,

526

options?: {

527

accept?: string;

528

cookieJar?: CookieJar;

529

referrer?: string;

530

element?: Element;

531

}

532

): Promise<Buffer> & { abort(): void };

533

}

534

```

535

536

[Resources](./resources.md)

537

538

### Cookie Management

539

540

HTTP cookie storage and handling compatible with browser cookie behavior.

541

542

```javascript { .api }

543

/**

544

* Cookie jar for HTTP cookie storage (extends tough-cookie.CookieJar)

545

*/

546

class CookieJar {

547

constructor(store?: any, options?: object);

548

549

setCookie(cookieOrString: string | Cookie, currentUrl: string, options?: object, callback?: Function): void;

550

setCookieSync(cookieOrString: string | Cookie, currentUrl: string, options?: object): Cookie;

551

getCookies(currentUrl: string, options?: object, callback?: Function): void;

552

getCookiesSync(currentUrl: string, options?: object): Cookie[];

553

}

554

```

555

556

[Resources](./resources.md)

557

558

### Window and DOM APIs

559

560

Complete browser-like environment with standard web APIs accessible via `dom.window`.

561

562

The Window object provides:

563

- **DOM APIs**: Node, Element, Document, DocumentFragment, and all HTML elements

564

- **Events**: EventTarget, Event classes, addEventListener, dispatchEvent

565

- **Selection and Traversal**: Range, Selection, NodeIterator, TreeWalker

566

- **Storage**: localStorage, sessionStorage (Web Storage API)

567

- **Timers**: setTimeout, setInterval, setImmediate, requestAnimationFrame

568

- **Network**: WebSocket, basic XMLHttpRequest, Headers

569

- **Parsing**: DOMParser, XMLSerializer

570

- **Files**: Blob, File, FileReader, FileList

571

- **Custom Elements**: customElements, CustomElementRegistry

572

- **Navigation**: location, history (limited)

573

- **Console**: console object (connected to VirtualConsole)

574

- **And many more standard web platform APIs**

575

576

[DOM APIs](./dom-apis.md)

577

578

## Common Usage Patterns

579

580

### Creating a simple DOM

581

582

```javascript

583

const { JSDOM } = require("jsdom");

584

585

const dom = new JSDOM(`<!DOCTYPE html><body><div id="app"></div></body>`);

586

const { document } = dom.window;

587

588

const app = document.getElementById("app");

589

app.innerHTML = "<h1>Hello from jsdom!</h1>";

590

```

591

592

### Testing DOM-dependent code

593

594

```javascript

595

const { JSDOM } = require("jsdom");

596

597

// Create a DOM environment

598

const dom = new JSDOM(`<!DOCTYPE html><body></body>`, {

599

url: "https://example.org/",

600

runScripts: "dangerously"

601

});

602

603

// Make window available globally for tests

604

global.window = dom.window;

605

global.document = dom.window.document;

606

global.navigator = dom.window.navigator;

607

608

// Now DOM-dependent code can run in tests

609

```

610

611

### Executing scripts safely

612

613

```javascript

614

const { JSDOM } = require("jsdom");

615

616

const dom = new JSDOM(`<!DOCTYPE html><body></body>`, {

617

runScripts: "outside-only"

618

});

619

620

// Execute code in the jsdom context

621

dom.window.eval(`

622

document.body.innerHTML = '<p>Added from script</p>';

623

`);

624

625

console.log(dom.window.document.body.innerHTML); // '<p>Added from script</p>'

626

```

627

628

### Loading a page from a URL

629

630

```javascript

631

const { JSDOM } = require("jsdom");

632

633

JSDOM.fromURL("https://example.com/", {

634

resources: "usable",

635

runScripts: "dangerously"

636

}).then(dom => {

637

console.log(dom.serialize());

638

});

639

```

640

641

### Capturing console output

642

643

```javascript

644

const { JSDOM, VirtualConsole } = require("jsdom");

645

646

const virtualConsole = new VirtualConsole();

647

virtualConsole.on("error", (message) => {

648

console.error("Page error:", message);

649

});

650

651

const dom = new JSDOM(`

652

<!DOCTYPE html>

653

<script>console.log("Hello from page");</script>

654

`, { runScripts: "dangerously", virtualConsole });

655

```

656

657

## Important Notes

658

659

### Security Warning

660

661

The `runScripts: "dangerously"` option allows code execution within jsdom. This is not a secure sandbox - scripts can escape and access your Node.js environment if they try hard enough. **Only use with trusted content**.

662

663

### Unimplemented Features

664

665

jsdom does not support:

666

- **Navigation**: Cannot change the global object via `window.location.href = "..."`

667

- **Layout**: No visual layout calculation, layout properties return zeros

668

669

### Workarounds

670

671

- **Navigation**: Create new JSDOM instances for each page

672

- **Layout**: Use `Object.defineProperty()` to mock layout values

673

674

### Encoding Sniffing

675

676

jsdom accepts binary data (Buffer, ArrayBuffer, TypedArray) and automatically sniffs encoding using:

677

1. UTF-8 or UTF-16 BOM (takes precedence)

678

2. `charset` parameter in `contentType` option

679

3. `<meta charset>` tag in HTML

680

4. Default: `"UTF-8"` for XML, `"windows-1252"` for HTML

681

682

### Closing jsdoms

683

684

Timers keep Node.js processes alive. Use `window.close()` to terminate all timers and event listeners.

685

686

```javascript

687

dom.window.close(); // Shut down the jsdom

688

```

689

690

## Next Steps

691

692

- [Configuration](./configuration.md) - Detailed configuration options

693

- [DOM APIs](./dom-apis.md) - Available browser APIs

694

- [JSDOM Class](./jsdom-class.md) - JSDOM methods and properties

695

- [Resources](./resources.md) - Resource loading and cookies

696

- [Virtual Console](./virtual-console.md) - Console output handling

697

- [Troubleshooting](./troubleshooting.md) - Common issues and solutions

698

699

## Types

700

701

```javascript { .api }

702

interface LocationInfo {

703

startOffset: number;

704

endOffset: number;

705

startLine: number;

706

endLine: number;

707

startCol: number;

708

endCol: number;

709

startTag?: TagLocationInfo;

710

endTag?: TagLocationInfo;

711

attrs?: Record<string, AttrLocationInfo>;

712

}

713

714

interface TagLocationInfo {

715

startOffset: number;

716

endOffset: number;

717

startLine: number;

718

endLine: number;

719

startCol: number;

720

endCol: number;

721

attrs?: Record<string, AttrLocationInfo>;

722

}

723

724

interface AttrLocationInfo {

725

startOffset: number;

726

endOffset: number;

727

startLine: number;

728

endLine: number;

729

startCol: number;

730

endCol: number;

731

}

732

```

733