You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何获取符合自定义格式规则的元素完整文本内容

如何获取符合自定义格式规则的元素完整文本内容

嘿,我来帮你搞定这个自定义文本格式化的问题!你想要的是严格按照给定规则把HTML内容转换成符合要求的多线字符串,不想用textContentinnerText完全合理——这俩确实没法精准控制咱们要的缩进、换行和标签过滤逻辑。之前你写的递归函数方向是对的,只是在缩进处理、换行控制和空白清理上有点小问题,咱们来一步步调整修复它。

首先,先把咱们要严格遵守的规则再明确一遍(从你的示例里提炼出来的):

  • 段落<p>:内容里的<br>换成换行,每个段落结束后加两个换行;如果段落在列表项<li>里,前后也要各加两个换行
  • 列表<li>:每个列表项前加换行+对应层级的缩进(每嵌套一层加4个空格),嵌套列表的缩进要逐层递增
  • 忽略的元素:<button>、带select-none类的元素直接跳过;<strong><i><span>这类标签只提取文本,标签本身忽略
  • <pre>标签:保留内部代码的格式,忽略不需要的元素(比如button、select-none的div),最终把有效代码包裹在<pre>标签里,同时要清理掉代码块里多余的前置空白

接下来是修复后的递归函数,我加了详细的注释,你可以对照着看调整的地方:

function flatten(elem, nesting = "") {
  let text = "";
  const tag = elem.tagName?.toLowerCase(); // 统一转小写,避免大小写问题

  // 处理不同标签的前置内容
  switch(tag) {
    case "li":
      // 列表项前加换行+当前层级的缩进
      text += `\n${nesting}`;
      break;
    case "pre":
      // pre标签开头先写<pre>换行
      text += "<pre>\n";
      // pre内部的内容单独处理,避免缩进干扰
      let preContent = "";
      break;
  }

  for (const child of elem.childNodes) {
    if (child.nodeType === Node.TEXT_NODE) {
      // 处理文本节点:非pre内部清理多余空白,pre内部保留原始格式
      if (tag !== "pre") {
        // 替换多个换行/空格为单个空格,同时去掉首尾无效空白
        const cleanedText = child.textContent.replace(/\s+/g, " ").trim();
        if (cleanedText) text += cleanedText;
      } else {
        preContent += child.textContent;
      }
    } else {
      const childTag = child.tagName?.toLowerCase();
      const className = child.className || "";

      // 完全忽略的元素:button、带select-none类的元素
      if (childTag === "button" || className.includes("select-none")) {
        continue;
      }

      // 处理br标签:直接替换为换行
      if (childTag === "br") {
        text += "\n";
        continue;
      }

      // 处理pre内部的子元素:直接提取文本,保留代码格式
      if (tag === "pre") {
        preContent += flatten(child, "");
        continue;
      }

      // 其他标签:递归处理,传递正确的嵌套缩进
      const childNesting = tag === "li" ? nesting + "    " : nesting;
      text += flatten(child, childNesting);
    }
  }

  // 处理不同标签的后置内容
  switch(tag) {
    case "p":
      // 段落结束后加两个换行,符合示例要求
      text += "\n\n";
      break;
    case "pre":
      // 清理pre内容的多余前置空格:找到所有非空行的最小缩进量统一去除
      const lines = preContent.split("\n").filter(line => line.trim() !== "");
      if (lines.length === 0) {
        text += "</pre>\n";
        break;
      }
      const minIndent = Math.min(...lines.map(line => line.match(/^\s*/)[0].length));
      const cleanedPre = lines.map(line => line.slice(minIndent)).join("\n");
      text += cleanedPre + "\n</pre>\n";
      break;
  }

  return text;
}

关键调整点说明:

  1. 缩进处理:原来的函数直接修改了nesting变量,导致嵌套列表的缩进混乱,现在改用childNesting传递新的缩进,原变量保持不变,确保层级正确。
  2. 空白清理:非pre的文本节点会清理多余的换行和空格,避免HTML里的空白字符导致输出出现大量空行;pre内部的文本则通过计算最小缩进量来对齐代码,解决原输出里代码前置空格过多的问题。
  3. 标签过滤逻辑:明确区分了“完全忽略的元素”和“只提取文本的标签”,比如<strong><i>这类标签会递归提取文本,而<button>直接跳过。
  4. 换行控制:调整了<li><p>的换行逻辑,避免原输出里出现的多余空行,比如<li>结束后不再额外加换行,<p>结束后加两个换行完全匹配你的示例要求。

你可以把这个函数拿去测试,应该能得到和你期望一致的输出啦!

备注:内容来源于stack exchange,提问作者Ξένη Γήινος

火山引擎 最新活动