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

问题描述 投票:0回答:1

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

我知道 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 字符串解析的文本和样式属性。最后,这个二维数组将被处理并用于渲染为表格结构。

非常感谢对此的任何帮助或指导。预先感谢!

javascript html reactjs typescript html-parsing
1个回答
0
投票

这可以使用一点 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 - 这是调用其他方法的主方法

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

convertElementsToTabular - 循环显示屏幕上的所有元素,并使用每个元素的

tagName
来确定如何处理它。

基于

tagName
,调用不同的方法来处理元素,如parseHeading1parseParagraphparseOrderedListparseUnorderedListparseTable

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

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

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

parseTableStructureToHtml - 解析

convertElementsToTabular
返回的数组,并返回一个包含基于数据的表格行和单元格标签的字符串。

然后将此 html 字符串插入页面上的 div 内以进行渲染。

希望这对您有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.