【问题标题】:How to remove unnecessary tags from html generate from text editor如何从文本编辑器生成的 html 中删除不必要的标签
【发布时间】:2018-07-07 04:41:50
【问题描述】:

以下是文本编辑器从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('&nbsp;', ''); // remove &nbsp; space
console.log(html);

但是,在上面的 JS 脚本没有任何改变之后,空的和不必要的标签仍然存在。

我不知道为什么它不起作用,但我尝试了非常简单的替换'&lt;p&gt;&lt;br&gt;&lt;/p&gt;not replaced'.replace('&lt;p&gt;&lt;br&gt;&lt;/p&gt;',''),它工作得很好。

上面有什么问题?如何从上面删除所有不必要的标签?谢谢。

【问题讨论】:

  • 中间有空格...使用正则表达式检测。
  • @JonasW。谢谢,我不清楚正则表达式函数,我怎样才能用它来检测空值标签。
  • 也允许换行 \r\n
  • @HouyNarun 如果您是正则表达式的新手,这个网站非常适合尝试并让您迈出第一步。您甚至可以粘贴上面的 html 并直接在上面进行实验:regexr.com 它帮助了我一开始很多。
  • @CodeAt30,非常感谢,我会做书签并留作参考。还不知道怎么用,但我会围绕它,:)

标签: javascript summernote


【解决方案1】:

您的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

这将匹配&lt;p&gt;&lt;br&gt;&lt;/p&gt;,但你的&lt;p&gt;s 中的&lt;b&gt;s 犯规了。您可以制作更复杂的正则表达式来处理越来越深奥的情况,但that way lies madnessisn'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

&lt;template&gt; HTMLTemplateElement 在这里可以帮助我们,因为我们可以将 HTML 字符串分配给其 innerHTML 属性,然后将其作为 DocumentFragmentcontent 属性中拉出。如果我们更改DocumentFragment 的结构,这些更改将反映在innerHTML 属性中。*

*我找不到支持我的文档,但它适用于 Firefox 和 Chromium。

现在我们需要实际删除“不必要的、空的标签和[不] 包含任何值的标签”。我们将定义无用节点来帮助做到这一点:

  • 评论节点没用
  • 为空或仅包含空格的文本节点无用
  • void 元素节点,其child nodes 仅包含无用节点或&lt;br&gt; 元素无用

所有其他节点并非无用

我们需要一个函数来识别和删除无用的节点。由于我们想在整个树中搜索无用的节点,我们将在节点的子节点上递归调用该函数:

function removeUselessNodes(node) {
  for (let i = node.childNodes.length - 1; i >= 0; --i) {
    removeUselessNodes(node.childNodes.item(i));
  }

我们反向迭代子节点,因为Node.childNodes 是一个活动列表,我们将从其中删除元素。循环不知道我们正在进行的更改,如果我们继续前进,它会跳过元素。从列表末尾删除元素不会破坏向后迭代循环。我们首先执行递归调用,因为它使检查最后一个无用节点的条件更容易。

通过所有的树遍历,我们可以从无用节点条件开始。让我们一一来看:

  • 评论节点没用

这个很简单。 Nodes 有一个属性表明它们的类型,nodeType。如果是评论,我们可以检查它并删除节点:

  if (node.nodeType === Node.COMMENT_NODE) {
    node.remove();
    return;
  }

删除无用节点后立即返回;没有什么可做的了。下一个:

  • 为空或仅包含空格的文本节点无用

“[E]mpty or contains[s] only whitespace”是“不包含非空白”的另一种说法,我们可以使用RegExp.test 进行测试。

  if (
    node.nodeType === Node.TEXT_NODE
    && !/\S/.test(node.textContent)
  ) {
    node.remove();
    return;
  }

\s 是空白字符,\S(注意大写)是非空白字符。)

最后的测试需要稍微拆包:

  • void 元素节点,其child nodes 仅包含无用节点或&lt;br&gt; 元素无用

Void 元素是不能有子元素的元素:比如&lt;img&gt;s 和&lt;hr&gt;s。它们不是没用的;它们本身就有意义。为了我们的目的,非空元素需要有意义的子元素才有意义。 &lt;p&gt; 本身只是在页面上腾出一些空间。它的子文本节点是文本的来源。 &lt;br&gt; 与其他节点相邻时并不是无用的,但它本身不足以使其父节点有意义。

将其分解为单独的测试,我们得到

  • 必须是元素节点
  • 必须是非空的
  • 子节点只能包含无用节点或&lt;br&gt;元素

我们之前已经测试过节点类型:

  if (
    node.nodeType === Node.ELEMENT_NODE

没有方便的方法来检查 JavaScript 中的 void-ness,但 HTML5 规范包含 a list of void elements,我们可以使用 Element.tagName 属性进行检查:

    && ![
      'AREA',
      'BASE',
      'BR',
      'COL',
      'EMBED',
      'HR',
      'IMG',
      'INPUT',
      'LINK',
      'META',
      'PARAM',
      'SOURCE',
      'TRACK',
      'WBR'
    ].includes(node.tagName)

由于我们已经从这个节点中移除了所有无用的子节点,如果它的所有子节点都是&lt;br&gt; 元素,则该节点通过第三次测试。 childNodes 是一个NodeList,它没有every 方法,但是有0 索引元素和length 属性,我们可以在其上调用Array 的every 方法:

    && Array.prototype.every.call(node.childNodes, n => n.tagName === 'BR')
  ) {
    node.remove();
    return;
  }
}

这样,fragments 的所有无用节点都已被删除。您可以从 template.innerHTML 获取生成的 HTML,也可以使用 document.adoptNode 将其直接发送到另一个元素:

const adoptedNode = document.adoptNode(fragment);
document.querySelector('#destination').appendChild(adoptedNode);

把它们放在一起:

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;
}
<textarea id="rawHTML"></textarea>
<div id="destination"></div>

【讨论】:

  • @AuxTco,非常感谢,它作为初学者使用正则表达式函数的文档非常有用。我会尝试使用它并从中理解。
猜你喜欢
  • 2011-10-30
  • 2020-12-27
  • 1970-01-01
  • 2020-04-24
  • 1970-01-01
  • 2020-12-08
  • 1970-01-01
  • 2019-04-13
  • 2013-07-19
相关资源
最近更新 更多