You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jest+JSDOM异常:dom.window.document.getElementById成功但全局document.getElementById失效

解决JSDOM+Jest中global.document.getElementById返回null的问题

问题现象

  • 设置1000ms延迟,排除时序问题影响
  • document.documentElement.outerHTML能正常显示完整HTML内容,说明document可访问DOM结构
  • document是有效的Document对象,document.getElementById为有效函数
  • document.getElementById('loginEmailForm')莫名返回null
  • 调用dom.window.document.getElementById('loginEmailForm')能正确获取到HTMLElement{},无论在前者调用前后结果一致
  • window.document.getElementById('loginEmailForm')与document.getElementById('loginEmailForm')始终返回null

环境版本

  • node v22.3.0
  • jest v29.7.0
  • jsdom v24.1.0

测试代码

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

beforeEach(() => {
  jest.resetModules();

  const dom = new JSDOM(`
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <title>test</title>
    </head>
    <body>
      <form id="loginEmailForm">
        <input type="email" id="email">
        <span id="emailError"></span>
        <button type="submit">Submit</button>
      </form>
    </body>
    </html>
  `, { runScripts: "dangerously" });

  global.dom = dom;
  global.window = dom.window;
  global.document = dom.window.document;

  require('../src/login-email.js');
});

describe('Login Email Form', () => {
  test('form and input elements exist', async () => {
    await new Promise(resolve => setTimeout(resolve, 1000));
    console.log("document:", document.documentElement.outerHTML);
    console.log(document);
    console.log(document.getElementById);
    console.log(document.getElementById('loginEmailForm'));
    console.log(dom.window.document.getElementById('loginEmailForm'));

    expect(dom.window.document.getElementById('loginEmailForm')).not.toBeNull();
    expect(dom.window.document.getElementById('email')).not.toBeNull();
    expect(dom.window.document.getElementById('emailError')).not.toBeNull();
  });
});

问题原因

核心问题是你引入的../src/login-email.js脚本中,可能存在修改全局document或window.document引用的操作,导致挂载到global的document不再指向JSDOM创建的原始Document对象。

另外,runScripts: "dangerously"选项会允许执行HTML及外部脚本中的代码,若脚本内有覆盖全局document的逻辑,就会出现全局document与dom.window.document分离的情况。

解决方案

1. 排查并修复login-email.js中的document操作

检查login-email.js文件,确认是否存在直接赋值document的代码(如document = ...),或使用了会修改全局document引用的库。确保脚本内的document操作始终指向JSDOM创建的实例,避免重新赋值。

2. 调整测试代码,避免全局document挂载冲突

修改测试逻辑,不再直接将document挂载到global,而是通过dom.window直接获取document引用,确保上下文一致:

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

beforeEach(() => {
  jest.resetModules();

  const dom = new JSDOM(`
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <title>test</title>
    </head>
    <body>
      <form id="loginEmailForm">
        <input type="email" id="email">
        <span id="emailError"></span>
        <button type="submit">Submit</button>
      </form>
    </body>
    </html>
  `, { runScripts: "dangerously" });

  global.dom = dom;
  global.window = dom.window;
  // 移除全局document挂载,让脚本通过window访问document

  require('../src/login-email.js');
});

describe('Login Email Form', () => {
  test('form and input elements exist', async () => {
    await new Promise(resolve => setTimeout(resolve, 1000));
    const doc = dom.window.document;
    console.log("document:", doc.documentElement.outerHTML);
    console.log(doc.getElementById('loginEmailForm'));

    expect(doc.getElementById('loginEmailForm')).not.toBeNull();
    expect(doc.getElementById('email')).not.toBeNull();
    expect(doc.getElementById('emailError')).not.toBeNull();
  });
});

3. 禁用不必要的runScripts选项

如果测试不需要执行HTML或外部脚本中的JS代码,可移除runScripts: "dangerously"选项,避免脚本修改DOM上下文:

const dom = new JSDOM(`...`); // 不设置runScripts选项

内容的提问来源于stack exchange,提问作者user3642765

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 15:40:18