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

Cypress:如何滚动到临时从DOM中移除的元素?

问题描述

我有一个带可折叠行的表格,行默认展开,且是动态加载的——随页面滚动才加载,滚动出视口的行会被从DOM中移除。总行数不到50条,视口内约显示5条。

已知两个随机行的HTML ID,想要对每行执行scrollIntoView()后折叠,但滚动到第一行时,第二行被移出DOM,导致测试找不到该行而失败。

HTML结构概述

<!-- 行容器Div(可点击折叠/展开) -->
<div id="行ID">
  <!-- 标题Div(始终可见) -->
  <div>标题文本</div>
  <!-- 内容Div(点击后折叠/展开) -->
  <div>行内容</div>
</div>

点击最外层Div(ID所在元素)时,内容Div会折叠/展开,标题Div始终保持可见。

已尝试的测试代码

// 页面对象类中的方法
static collapseRows(rowIds: number[]): void {
  rowIds.forEach((rowId) => {   
    cy.get(`[id = "${rowId}"]`)
    .scrollIntoView({ duration: 1000, easing: "linear" })
    .then(() => {
      cy.get(`[id = "${rowId}"]`).click();// 折叠或展开行
    });
  });
}
解决方法

1. 优先处理视口内已存在的行

先筛选出当前DOM中已经加载的行(视口内可见的),处理完这些后再滚动加载并处理剩下的行,避免滚动导致已找到的行被移除。

static collapseRows(rowIds: number[]): void {
  // 筛选当前DOM中存在的行
  const existingRows = rowIds.filter(rowId => {
    return Cypress.$(`[id="${rowId}"]`).length > 0;
  });
  // 筛选需要滚动加载的行
  const needScrollRows = rowIds.filter(rowId => !existingRows.includes(rowId));

  // 先处理已加载的行
  existingRows.forEach(rowId => {
    cy.get(`[id="${rowId}"]`).click();
  });

  // 再处理需要滚动加载的行
  needScrollRows.forEach(rowId => {
    cy.get(`[id="${rowId}"]`, { timeout: 5000 })
      .scrollIntoView({ duration: 1000, easing: "linear" })
      .click();
  });
}

2. 利用始终可见的标题定位行

因为标题Div始终在DOM中,哪怕行内容被移除,也可以通过标题文本找到对应的行容器:

// 需要传入行ID和对应标题的数组
static collapseRows(rowData: {id: number, title: string}[]): void {
  rowData.forEach(({id, title}) => {
    cy.contains('div', title)
      .parents(`[id="${id}"]`)
      .scrollIntoView({ duration: 1000, easing: "linear" })
      .click();
  });
}

调用示例:collapseRows([{id: 123, title: "用户列表"}, {id: 456, title: "订单详情"}])

3. 强制等待行加载完成

滚动后行可能需要短暂时间加载到DOM,给查找操作增加超时时间,或者在滚动后加短等待:

static collapseRows(rowIds: number[]): void {
  rowIds.forEach((rowId) => {   
    cy.scrollTo(`#${rowId}`, { duration: 1000, easing: "linear" });
    cy.wait(500); // 等待行加载到DOM
    cy.get(`[id="${rowId}"]`, { timeout: 5000 })
      .click();
  });
}

4. 确保串行执行操作

forEach是同步遍历,但Cypress命令是异步队列机制,可能导致多个滚动操作并行触发。改用Cypress._.each或递归处理,保证前一个操作完成后再执行下一个:

static collapseRows(rowIds: number[]): void {
  Cypress._.each(rowIds, (rowId) => {
    cy.get(`[id="${rowId}"]`, { timeout: 5000 })
      .scrollIntoView({ duration: 1000, easing: "linear" })
      .click();
  });
}

递归实现方式:

static collapseRows(rowIds: number[]): void {
  if (rowIds.length === 0) return;
  const [currentId, ...restIds] = rowIds;
  cy.get(`[id="${currentId}"]`, { timeout: 5000 })
    .scrollIntoView({ duration: 1000, easing: "linear" })
    .click()
    .then(() => {
      this.collapseRows(restIds);
    });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 01:16:07