在 JavaScript 中如何解析 HTML 字符串以转换为表格数据(二维数组)

In JavaScript how to parse HTML string to convert to a tabular data (2d array)

提问人:Amitava Karan 提问时间:11/9/2023 更新时间:11/9/2023 访问量:38

问:

我喜欢在客户端解析 html 字符串。我们使用 React 和 TypeScript 作为前端框架。在解析html时,我还喜欢获取与元素关联的样式。它可以是内联样式,继承自父元素,也可以在 head 部分的 style 标记中使用某个类定义(没有外部 css 文件引用)。我们的目标是解析 html 字符串并将其转换为具有 backgroundColor、textColor、fontSize 等样式的表格格式数据。

我知道 DOMParser 可用于解析 html 字符串。我们可以使用隐藏的 iFrame 来加载 html 文档并获取每个元素的计算样式。

我想知道在这种情况下是否有其他库可以帮助我们?另外,是否有任何替代方法可以在不使用 iframe 的情况下获取样式,因为我们喜欢使用 Web worker 来计算 html 解析?

Sample HTML string

<html>
  <head>
    <style>
      li {
        color: red;
      }

      .highlight {
        background-color: yellow;
      }
    </style>
  </head>
  <body>
    <!--StartFragment-->
    <h1>The ol and ul elements</h1>

    <h1>The ol and ul elements</h1>
    <p style="font-size: 16px">The ol element defines an ordered list:</p>
    <ol>
      <li>Coffee</li>
      <li>Tea</li>
      <li>Milk</li>
    </ol>

    <p class="highlight">The ul element defines an unordered list:</p>
    <ul>
      <li>Coffee</li>
      <li>Tea</li>
      <li>Milk</li>
    </ul>
    <h1>The table element</h1>

    <table>
      <tbody>
        <tr>
          <th>Month</th>
          <th>Savings</th>
        </tr>
        <tr>
          <td>January</td>
          <td>$100</td>
        </tr>
        <tr>
          <td>February</td>
          <td>$80</td>
        </tr>
      </tbody>
    </table>
    <!--EndFragment-->
  </body>
</html>

解析后,预期的输出将是一个 javascript 对象数组,其中每个项目都将具有从 html 字符串解析的文本和样式属性。最后,这个二维数组将被处理并用于呈现为表格结构。

enter image description here

非常感谢这方面的任何帮助或指导。提前致谢!

javascript reactjs typescript html 解析

评论

0赞 Elvis Adomnica 11/9/2023
为什么需要 iframe?你可以用 DOMParser 解析文档(基本上有一个未挂载的 DOM),然后你可以随心所欲地操作它,包括从中读取样式。还是我错过了什么?

答:

1赞 Ashish Shevale 11/9/2023 #1

这可以使用一些 DOM 操作和 JS 来完成。我决定用JS实现这一点,但是这段代码可以很容易地修改为与react一起使用。请考虑以下代码。

function getDocumentFromString() {
  const sampleHtmlString = `
<html>
  <head>
    <style>
      li {
        color: red;
      }

      .highlight {
        background-color: yellow;
      }
    </style>
  </head>
  <body>
    <!--StartFragment-->
    <h1>The ol and ul elements</h1>

    <p style="font-size: 16px">The ol element defines an ordered list:</p>
    <ol>
      <li>Coffee</li>
      <li>Tea</li>
      <li>Milk</li>
    </ol>

    <p class="highlight">The ul element defines an unordered list:</p>
    <ul>
      <li>Coffee</li>
      <li>Tea</li>
      <li>Milk</li>
    </ul>
    <h1>The table element</h1>

    <table>
      <thead>
        <tr>
          <th>Month</th>
          <th>Savings</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>January</td>
          <td>$100</td>
        </tr>
        <tr>
          <td>February</td>
          <td>$80</td>
        </tr>
      </tbody>
    </table>
    <!--EndFragment-->
  </body>
</html>
`;

  const domParser = new DOMParser();
  const strDocument = domParser.parseFromString(sampleHtmlString, 'text/html');

  return strDocument;
}

function getInlineStyles(element) {
  const styles = Array.from(element.style);
  return styles.reduce((acc, curr) => acc.concat(`${curr}:${element.style[curr]};`), '');
}

function getClassName(element) {
  return Array.from(element.classList).join(' ');
}

function parseHeading1(element) {
  return [
    {
      text: element.innerText,
      style: 'font-size:20px;font-weight:600'.concat(getInlineStyles(element)),
      className: getClassName(element),
    },
  ];
}

function parseParagraph(element) {
  return [
    {
      text: element.innerText,
      style: ''.concat(getInlineStyles(element)),
      className: getClassName(element),
    },
  ];
}

function parseOrderedList(element) {
  return Array(...element.children).map((child, idx) => ([
    {
      text: idx + 1,
      style: 'color:red'.concat(getInlineStyles(child)),
      className: getClassName(child),
    },
    {
      text: child.innerText,
      style: 'color:red'.concat(getInlineStyles(child)),
      className: getClassName(child),
    },
  ]));
}

function parseUnorderedList(element) {
  return Array(...element.children).map((child) => ([
    {
      text: child.innerText,
      style: 'color:red'.concat(getInlineStyles(element)),
      className: getClassName(child),
    },
  ]));
}

function parseTable(element) {
  const header = element.querySelectorAll('thead tr th');
  const body = element.querySelectorAll('tbody tr');

  const headingElements = Array(...header).map(th => ({
    text: th.innerText,
    style: 'font-weight:600'.concat(getInlineStyles(th)),
    className: getClassName(th),
  }));
  const bodyElements = Array(...body)
    .map(tr => {
      const cells = Array(...tr.querySelectorAll('td'));
      return cells.map(cell => ({
        text: cell.innerText,
        style: ''.concat(getInlineStyles(cell)),
        className: getClassName(cell),
      }))
    });

  return [
    headingElements,
    ...bodyElements,
  ]
}

function convertElementsToTabular(elements) {
  const result = [];
  let noOfColumns = 1;
  Array(...elements).forEach(element => {
    switch (element.tagName) {
      case 'H1':
        result.push(parseHeading1(element));
        break;
      case 'P':
        result.push(parseParagraph(element));
        break;
      case 'OL':
        noOfColumns = Math.max(noOfColumns, 2);
        result.push(...parseOrderedList(element));
        break;
      case 'UL':
        result.push(...parseUnorderedList(element));
        break;
      case 'TABLE':
        result.push(...parseTable(element));
        break;
    }
  });

  console.log(result)
  return result;
}

function parseTableStructureToHtml(tableStructure) {
  const getTableCell = cell => `<td ${cell.style && 'style='.concat(cell.style)} ${cell.className && 'class='.concat(cell.className)}>${cell.text}</td>`;
  const getTableRow = row => `<tr>${row.map(getTableCell).join('')}</tr>`;
  const getTable = data => `<tbody>${data.map(getTableRow).join('')}</tbody>`;

  console.log(getTable(tableStructure))
  return `<table>
      <tbody>
        ${getTable(tableStructure)}
      </tbody>
    </table>`;
}

function main() {
  const strDocument = getDocumentFromString();
  const elementsOnScreen = strDocument.querySelector('body').children;
  const tableStructure = convertElementsToTabular(elementsOnScreen);
  
  const htmlStr = parseTableStructureToHtml(tableStructure);
  document.getElementById('main-container').innerHTML = htmlStr;
}

让我们逐个函数地浏览流程。

main - 这是调用其他方法的 main 方法

getDocumentFromString - 根据传递给它的字符串返回一个 DOM 对象。生成的 DOM 用于其余的处理。

convertElementsToTabular - 遍历屏幕上存在的所有元素,并使用每个元素的 来确定如何处理它。tagName

基于 ,调用不同的方法来处理元素,如 parseHeading1parseParagraph、parseOrderedListparseUnorderedListparseTabletagName

如果您的用例包含更多类型的标签(H2、H3、SPAN 等),您只需要在此处添加更多案例并相应回调即可。

parseHeading1parseParagraphparseOrderedListparseUnorderedListparseTable - 这些方法返回一个数组,其中包含要在结果表中显示的文本。 每个元素包含 -

  1. text
  2. style- 它使用该方法从示例字符串中获取样式,还允许您添加自己的样式来自定义标记的外观。getInlineStyles
  3. className- 使用该方法从示例字符串中获取元素的类名。getClassName

parseTableStructureToHtml - 解析 返回的数组,并返回一个字符串,其中包含基于数据的表行和单元格的标记。convertElementsToTabular

然后将此 html 字符串插入到页面上的 div 中以呈现它。

希望这对你有所帮助!