我喜欢在客户端解析 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 字符串解析的文本和样式属性。最后,这个二维数组将被处理并用于渲染为表格结构。
非常感谢对此的任何帮助或指导。预先感谢!
这可以使用一点 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
,调用不同的方法来处理元素,如parseHeading1,parseParagraph,parseOrderedList,parseUnorderedList和parseTable。
如果您的用例包含更多类型的标签(H2、H3、SPAN等),您只需要在此处添加更多用例和相应的回调即可。
parseHeading1、parseParagraph、parseOrderedList、parseUnorderedList、parseTable - 这些方法返回一个数组,其中包含要在结果表中显示的文本。 每个元素包含 -
text
style
- 它使用 getInlineStyles
方法从示例字符串中获取样式,还允许您添加自己的样式来自定义标签的外观。className
- 使用 getClassName
方法从示例字符串中获取元素上的类名称。parseTableStructureToHtml - 解析
convertElementsToTabular
返回的数组,并返回一个包含基于数据的表格行和单元格标签的字符串。
然后将此 html 字符串插入页面上的 div 内以进行渲染。
希望这对您有帮助!