下面是一个由word文档自动生成的html脚本,文本编辑器为summernote。
var html = `
<p>
<b>
<br>
</b>
</p>
<p>
<b>អ្នកធានា</b>
</p>
<p>
<b>ឈ្មោះ: ……………………………</b>
</p>
<p>
<b>អត្តសញ្ញាណប័ណ្ណលេខៈ………………...............
<span style="white-space:pre"></span>..........................................
</b>
</p>
<p>
<b>
<span style="white-space:pre"></span>ហត្ថលេខានិង ស្នាមមេដៃស្តាំ
<span style="white-space:pre"></span>
</b>
</p>
<p>
<b>
<br>
</b>
</p>
<p>`;
在为我生成hmlt代码之后,我尝试通过删除不必要的空标记和不包含任何值的标记来清理它。
所以,我尝试了我的JS脚本如下:
html.replace('<p><br></p>', ''); // remove unneccessary tage
html.replace(' ', ''); // remove space
console.log(html);
然而,在JS脚本上面没有任何变化之后,空的和不必要的标签仍然存在。
我不知道为什么它不起作用,但我尝试了非常简单的替换'<p><br></p>not replaced'.replace('<p><br></p>','')
,它工作得很好。
上面有什么问题?如何从上面删除所有不必要的标签?感谢。
答案 0 :(得分:3)
您的replace
行无效,因为它与HTML的确切结构不匹配,并且不会考虑代码之间的空白。您可以在replace
来电中使用RegExp
来处理空白,如下所示:
html.replace(/<p>\s*<br>\s*<\/p>/, '');
// / start of the regex literal
// <p> a literal "<p>"
// \s any whitespace character
// * previous char, zero or more times
// <br> a literal "br"
// \s any whitespace character
// * previous char, zero or more times
// <\/p> a literal "</p>" (with escaped slash)
// / end of regex
这符合<p><br></p>
,但<b>
内的<p>
会犯规。你可以制作越来越复杂的正则表达式来处理越来越多的深奥情境,但是that way lies madness和isn't possible in the general case。
相反,我们可以将生成的HTML拉入DocumentFragment
。然后我们可以将它作为DOM树使用,而不是字符串:
const template = document.createElement('template');
template.innerHTML = html;
const fragment = template.content;
removeUselessNodes(fragment); // we'll need to write this one
<template>
HTMLTemplateElement
可以帮助我们,因为我们可以为其innerHTML
属性分配一个HTML字符串,并将其作为DocumentFragment
从{{3}中拉出来属性。如果我们更改DocumentFragment
的结构,则这些更改将反映在innerHTML
属性中。*
*我无法找到支持我的文档,但它在Firefox和Chromium中适用于我。
现在我们需要实际删除那些[不]包含任何值的&#34;不必要的空标签和标签。&#34;我们将定义无用节点来帮助实现这一目标:
所有其他节点都不是无用的。
我们需要一个功能来识别和删除无用的节点。由于我们希望在整个树中搜索无用的节点,因此我们将在节点的子节点上递归调用该函数:
function removeUselessNodes(node) {
for (let i = node.childNodes.length - 1; i >= 0; --i) {
removeUselessNodes(node.childNodes.item(i));
}
我们反过来遍历子节点,因为Node.childNodes
是一个实时列表,我们将从中删除元素。循环不知道我们正在进行的更改,如果我们前进,将会跳过元素。从列表末尾删除元素不会破坏向后迭代循环。我们首先执行递归调用,因为它可以更容易地检查最后一个无用节点条件。
通过所有树遍历,我们可以从无用节点条件开始。让我们逐一介绍它们:
- 评论节点无用。
这很容易。 Node
有一个属性,表明其类型child nodes。我们可以检查并删除节点,如果它是评论:
if (node.nodeType === Node.COMMENT_NODE) {
node.remove();
return;
}
删除无用节点后立即返回;没有什么可做的。下一个:
- 空白或仅包含空格的文本节点无用。
&#34; [E] mpty或仅包含[s]空格&#34;是另一种表示&#34;不包含非空格&#34;的方式,我们可以使用nodeType
进行测试。
if (
node.nodeType === Node.TEXT_NODE
&& !/\S/.test(node.textContent)
) {
node.remove();
return;
}
(\s
是一个空白字符,\S
(注意大小写)是一个非空白字符。)
最后一次测试需要一点点拆包:
RegExp.test
仅包含无用节点或<br>
元素的非void元素节点无用。
Void元素是不能生孩子的元素:<img>
和<hr>
之类的元素。他们并非无用;他们有自己的意义。出于我们的目的,非空白元素需要有意义的孩子才有意义。 <p>
本身只会在页面上留出一些空间。它的子文本节点是文本的来源。当<br>
与其他节点相邻时,<br>
并不是无用的,但它本身并不足以使其父节点有意义。
将其分解为单独的测试,我们得到
if (
node.nodeType === Node.ELEMENT_NODE
元素我们之前测试了节点类型:
&& ![
'AREA',
'BASE',
'BR',
'COL',
'EMBED',
'HR',
'IMG',
'INPUT',
'LINK',
'META',
'PARAM',
'SOURCE',
'TRACK',
'WBR'
].includes(node.tagName)
没有方便的方法来检查JavaScript中的无效性,但HTML5规范包括child nodes我们可以使用a list of void elements属性进行检查:
<br>
由于我们已经从此节点中删除了所有无用的子节点,因此如果节点的所有子节点都是childNodes
元素,则该节点将通过第三次测试。 NodeList
是every
,它没有length
方法,但是使用0索引元素和 && Array.prototype.every.call(node.childNodes, n => n.tagName === 'BR')
) {
node.remove();
return;
}
}
属性,我们可以调用数组&#39}。其上的Element.tagName
方法:
fragment
这样,所有template.innerHTML
个无用节点都被删除了。您可以从const adoptedNode = document.adoptNode(fragment);
document.querySelector('#destination').appendChild(adoptedNode);
获取生成的HTML,也可以将其直接发送到every
的其他元素:
var html = `
<p>
<b>
<br>
</b>
</p>
<p>
<b>អ្នកធានា</b>
</p>
<p>
<b>ឈ្មោះ: ……………………………</b>
</p>
<p>
<b>អត្តសញ្ញាណប័ណ្ណលេខៈ………………...............
<span style="white-space:pre"></span>..........................................
</b>
</p>
<p>
<b>
<span style="white-space:pre"></span>ហត្ថលេខានិង ស្នាមមេដៃស្តាំ
<span style="white-space:pre"></span>
</b>
</p>
<p>
<b>
<br>
</b>
</p>
<p>`;
function removeUselessNodes(node) {
for (let i = node.childNodes.length - 1; i >= 0; --i) {
removeUselessNodes(node.childNodes.item(i));
}
if (node.nodeType === Node.COMMENT_NODE) {
node.remove();
return;
}
if (
node.nodeType === Node.TEXT_NODE
&& !/\S/.test(node.textContent)
) {
node.remove();
return;
}
if (
node.nodeType === Node.ELEMENT_NODE
&& ![
'AREA',
'BASE',
'BR',
'COL',
'EMBED',
'HR',
'IMG',
'INPUT',
'LINK',
'META',
'PARAM',
'SOURCE',
'TRACK',
'WBR'
].includes(node.tagName)
&& Array.prototype.every.call(node.childNodes, n => n.tagName === 'BR')
) {
node.remove();
return;
}
}
const template = document.createElement('template');
template.innerHTML = html;
const fragment = template.content;
removeUselessNodes(fragment);
document.querySelector('#rawHTML').value = template.innerHTML;
const adoptedNode = document.adoptNode(fragment);
document.querySelector('#destination').appendChild(adoptedNode);
全部放在一起:
#rawHTML {
width: 95vw;
height: 10em;
}
&#13;
<textarea id="rawHTML"></textarea>
<div id="destination"></div>
&#13;
{{1}}&#13;