0

0

解决JSDOM中MutationObserver的“参数1不是Node类型”错误

霞舞

霞舞

发布时间:2025-07-16 17:26:25

|

456人浏览过

|

来源于php中文网

原创

解决jsdom中mutationobserver的“参数1不是node类型”错误

正如摘要所述,当你在JSDOM中使用MutationObserver时,可能会遇到一个令人困惑的错误:“TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'”。 即使你确信传递给observe方法的参数是一个有效的DOM节点,该错误仍然可能出现。这通常是因为存在多个JSDOM实例,导致MutationObserver和DOM节点来自不同的JSDOM环境。

问题根源:多个JSDOM实例

在Jest测试环境中,jest-environment-jsdom 会自动创建一个全局的JSDOM实例。同时,你的测试代码可能又显式地创建了另一个JSDOM实例(例如,通过 new JSDOM())。

当你在测试中使用 new MutationObserver() 时,它会默认使用全局的JSDOM实例。但是,你传递给 observe 方法的DOM节点可能来自你显式创建的JSDOM实例。由于JSDOM不允许跨实例操作节点,因此会抛出上述错误。

解决方案:确保MutationObserver和DOM节点来自同一实例

解决这个问题的关键在于确保你使用的 MutationObserver 和DOM节点都来自同一个JSDOM实例。以下是几种可能的解决方案:

1. 使用JSDOM实例的MutationObserver

最直接的解决方案是将 MutationObserver 的创建与特定的JSDOM实例关联起来。例如,如果你创建了一个名为 dom 的JSDOM实例,那么你应该使用 new dom.window.MutationObserver() 来创建 MutationObserver。

修改后的代码示例:

import { JSDOM } from 'jsdom';
import fs from 'fs';
import path from 'path';

const html = fs.readFileSync(path.resolve(__dirname, '../index.html'), 'utf8');

let dom;
let container;

function waitForElm(dom, parentElm, selector) { // 传递dom实例
  return new Promise(resolve => {
    if (parentElm.querySelector(selector)) {
      return resolve(parentElm.querySelector(selector));
    }

    const observer = new dom.window.MutationObserver(mutations => { // 使用dom实例的MutationObserver
      if (parentElm.querySelector(selector)) {
        resolve(parentElm.querySelector(selector));
        observer.disconnect();
      }
    });

    observer.observe(parentElm, {
      childList: true,
      subtree: true
    });
  });
}

describe('index.html', () => {
  beforeEach(() => {
    dom = new JSDOM(html, { runScripts: 'dangerously', resources: "usable" });
  })

  it('adds a tag', async () => {
    container = dom.window.document.body.querySelector("#app-root");
    const elm = await waitForElm(dom, container, '.my-embed'); // 传递dom实例
    expect(container.querySelector('.my-embed')).not.toBeNull();
  });
});

在这个示例中,我们将 dom 实例传递给 waitForElm 函数,并在 waitForElm 函数中使用 dom.window.MutationObserver 创建 MutationObserver。这样可以确保 MutationObserver 和 parentElm 都来自同一个JSDOM实例。

uBrand
uBrand

一站式AI品牌创建平台,在线品牌设计,AI品牌策划,智能品牌营销;uBrand帮助创业者轻松打造个性品牌!

下载

注意事项:

  • 这种方法需要你将JSDOM实例(或者至少是它的 window 对象)传递给所有需要使用 MutationObserver 的函数。

2. 使用全局的JSDOM实例

另一种解决方案是始终使用全局的JSDOM实例,并避免显式创建新的JSDOM实例。你可以通过 document 对象访问全局JSDOM实例。

修改后的代码示例:

import '@testing-library/jest-dom/extend-expect';
import fs from 'fs';
import path from 'path';

const html = fs.readFileSync(path.resolve(__dirname, '../index.html'), 'utf8');

//  不再显式创建 JSDOM 实例
// let dom;
let container;

function waitForElm(parentElm, selector) {
  return new Promise(resolve => {
    if (parentElm.querySelector(selector)) {
      return resolve(parentElm.querySelector(selector));
    }

    const observer = new MutationObserver(mutations => {
      if (parentElm.querySelector(selector)) {
        resolve(parentElm.querySelector(selector));
        observer.disconnect();
      }
    });

    observer.observe(parentElm, {
      childList: true,
      subtree: true
    });
  });
}

describe('index.html', () => {
  beforeEach(() => {
    //不再使用 new JSDOM,直接修改全局 document
    document.documentElement.innerHTML = html;
    // 或者使用 document.write(html);
    // dom = new JSDOM(html, { runScripts: 'dangerously', resources: "usable" });
  })

  it('adds a tag', async () => {
    container = document.body.querySelector("#app-root");
    const elm = await waitForElm(container, '.my-embed');
    expect(container.querySelector('.my-embed')).not.toBeNull();
  });
});

在这个示例中,我们不再显式创建JSDOM实例,而是直接修改全局的 document 对象的内容。 我们使用document.documentElement.innerHTML = html; 将HTML内容写入全局的JSDOM实例中。 这样,所有的DOM操作都将在同一个JSDOM实例中进行。

注意事项:

  • 这种方法可能会影响其他测试用例,因为你正在修改全局状态。 因此,在使用这种方法时,请确保在每个测试用例之前重置全局状态。
  • 使用 document.write() 或 document.documentElement.innerHTML = ... 设置HTML内容可能会导致一些副作用,例如重新加载脚本。你需要仔细评估这些副作用是否会影响你的测试结果。

总结

MutationObserver 在 JSDOM 中报错“参数1不是Node类型”通常是由于多个JSDOM实例引起的。为了解决这个问题,你需要确保 MutationObserver 和 DOM 节点都来自同一个 JSDOM 实例。 你可以选择使用 JSDOM 实例的 MutationObserver,或者始终使用全局的 JSDOM 实例。 选择哪种方法取决于你的具体需求和测试环境。 在选择解决方案时,请仔细评估每种方法的优缺点,并选择最适合你的情况的方法。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
DOM是什么意思
DOM是什么意思

dom的英文全称是documentobjectmodel,表示文件对象模型,是w3c组织推荐的处理可扩展置标语言的标准编程接口;dom是html文档的内存中对象表示,它提供了使用javascript与网页交互的方式。想了解更多的相关内容,可以阅读本专题下面的文章。

3407

2024.08.14

DOM是什么意思
DOM是什么意思

dom的英文全称是documentobjectmodel,表示文件对象模型,是w3c组织推荐的处理可扩展置标语言的标准编程接口;dom是html文档的内存中对象表示,它提供了使用javascript与网页交互的方式。想了解更多的相关内容,可以阅读本专题下面的文章。

3407

2024.08.14

go语言 注释编码
go语言 注释编码

本专题整合了go语言注释、注释规范等等内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

go语言 math包
go语言 math包

本专题整合了go语言math包相关内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

go语言输入函数
go语言输入函数

本专题整合了go语言输入相关教程内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

golang 循环遍历
golang 循环遍历

本专题整合了golang循环遍历相关教程,阅读专题下面的文章了解更多详细内容。

0

2026.01.31

Golang人工智能合集
Golang人工智能合集

本专题整合了Golang人工智能相关内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

2026赚钱平台入口大全
2026赚钱平台入口大全

2026年最新赚钱平台入口汇总,涵盖任务众包、内容创作、电商运营、技能变现等多类正规渠道,助你轻松开启副业增收之路。阅读专题下面的文章了解更多详细内容。

72

2026.01.31

高干文在线阅读网站大全
高干文在线阅读网站大全

汇集热门1v1高干文免费阅读资源,涵盖都市言情、京味大院、军旅高干等经典题材,情节紧凑、人物鲜明。阅读专题下面的文章了解更多详细内容。

72

2026.01.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号