如何关闭未关闭的 HTML 标签?

IT技术 javascript php
2021-02-23 23:02:12

每当我们从数据库或类似来源通过一些编辑获取一些用户输入的内容时,我们可能会检索仅包含开始标记但不包含结束标记的部分。

这可能会妨碍网站的当前布局。

是否有客户端或服务器端的方法来解决这个问题?

6个回答

找到了一个很好的答案:

使用 PHP 5 并使用 DOMDocument 对象的 loadHTML() 方法。这会自动解析格式错误的 HTML,随后对 saveXML() 的调用将输出有效的 HTML。DOM 函数可以在这里找到:

http://www.php.net/dom

这个的用法:

$doc = new DOMDocument();
$doc->loadHTML($yourText);
$yourText = $doc->saveHTML();
$doc->getElementsByTagName('body')->innerHTML() 可惜没有方法,但在这里的评论中至少有三种古怪的方法可以做到。
2021-04-26 23:02:12
改进 XHTML: $doc = new DOMDocument(); libxml_use_internal_errors(true); $doc->loadHTML('<?xml version="1.0" encoding="UTF-8"?><html_tags>'.$html_text.'</html_tags>'); libxml_clear_errors(); $html_text = substr($doc->saveXML($doc->getElementsByTagName('html_tags')->item(0)), strlen('<html_tags>'), -strlen('</html_tags>'));
2021-05-08 23:02:12
为了使用这个内联(无标签!DOCTYPE和无<html><body>标签),我使用了这个:$inner = substr($yourText, 119, strlen($yourText) - 119 - 15);119 个字符用于 doctype 和开始标签;15 个用于结束标记和最后的换行符。不像解析 DOM 和获取内部内容那么简洁,但它更短、更容易并且可能也更快。
2021-05-09 23:02:12
使用标准的 PHP 库很好,但它有点混乱,因为如果没有在最后做一个选择器,这种技术不适用于 HTML 片段。
2021-05-14 23:02:12
@BobStein-VisiBone 看到我的回答,有一种相当简洁的方法。因为 saveHTML() 可以采用 DOMNode。
2021-05-19 23:02:12

我有 php 的解决方案

<?php
    // close opened html tags
    function closetags ( $html )
        {
        #put all opened tags into an array
        preg_match_all ( "#<([a-z]+)( .*)?(?!/)>#iU", $html, $result );
        $openedtags = $result[1];

        #put all closed tags into an array
        preg_match_all ( "#</([a-z]+)>#iU", $html, $result );
        $closedtags = $result[1];
        $len_opened = count ( $openedtags );

        # all tags are closed
        if( count ( $closedtags ) == $len_opened )
        {
            return $html;
        }
        $openedtags = array_reverse ( $openedtags );

        # close tags
        for( $i = 0; $i < $len_opened; $i++ )
        {
            if ( !in_array ( $openedtags[$i], $closedtags ) )
            {
                $html .= "</" . $openedtags[$i] . ">";
            }
            else
            {
                unset ( $closedtags[array_search ( $openedtags[$i], $closedtags)] );
            }
        }
        return $html;
    }
    // close opened html tags
?>

你可以像这样使用这个功能

   <?php echo closetags("your content <p>test test"); ?>
我喜欢那个功能。我看到的一个问题是它无法修复损坏的嵌套(例如"<b>Bold and <i>Italic</b> text"),而某些用户非常擅长这样做。
2021-04-20 23:02:12
-1 检查count($closedtags) == count($openedtags)是不够的......例如"<a href=''><b>link</a></a>"
2021-05-07 23:02:12

您可以使用整洁

Tidy 是 Tidy HTML 清理和修复实用程序的绑定,它允许您不仅清理和以其他方式操作 HTML 文档,还可以遍历文档树。

HTMLPurifier

HTML Purifier 是一个用 PHP 编写的符合标准的 HTML 过滤器库。HTML Purifier 不仅会使用经过彻底审核、安全但允许的白名单删除所有恶意代码(也称为 XSS),还会确保您的文档符合标准,这只有在全面了解 W3C 规范的情况下才能实现。

对于 HTML 片段,并根据KJS 的回答,当片段具有一个根元素时,我已成功执行以下操作:

$dom = new DOMDocument();
$dom->loadHTML($string);
$body = $dom->documentElement->firstChild->firstChild;
$string = $dom->saveHTML($body);

如果没有根元素,这是可能的(但似乎只包装了 p 标签中的第一个文本子节点text <p>para</p> text):

$dom = new DOMDocument();
$dom->loadHTML($string);
$bodyChildNodes = $dom->documentElement->firstChild->childNodes;

$string = '';
foreach ($bodyChildNodes as $node){
   $string .= $dom->saveHTML($node);
}

或者更好的是,从 PHP >= 5.4 和 libxml >= 2.7.8(2.7.7 for LIBXML_HTML_NOIMPLIED):

$dom = new DOMDocument();

// Load with no html/body tags and do not add a default dtd
$dom->loadHTML($string, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

$string = $dom->saveHTML();    
由于 LIBXML_HTML_NOIMPLIED 等部分,我刚刚对此进行了 +1,它完美地解决了我的片段问题。
2021-05-09 23:02:12
这条评论帮了我很多忙。只需要将这两个元素添加到您的代码中: libxml_use_internal_errors(true); // 在您声明新的 DOMDoc 以抑制错误之前。在此处使用 mb_convert_encoding() 处理特殊字符:php.net/manual/en/domdocument.loadhtml.php#74777
2021-05-16 23:02:12

除了像 Tidy 这样的服务器端工具,您还可以使用用户的浏览器为您做一些清理工作。真正伟大的事情之一innerHTML是它将对动态内容应用相同的即时修复,就像对 HTML 页面一样。这段代码运行良好(有两个警告),实际上没有任何内容写入页面:

var divTemp = document.createElement('div');
divTemp.innerHTML = '<p id="myPara">these <i>tags aren\'t <strong> closed';
console.log(divTemp.innerHTML); 

警告:

  1. 不同的浏览器会返回不同的字符串。这还不错,除了在 IE 的情况下,它会返回大写的标签并从标签属性中去除引号,这将不会通过验证。这里的解决方案是在服务器端做一些简单的清理。但至少文档将是结构正确的 XML。

  2. 我怀疑您可能需要在阅读 innerHTML 之前延迟 - 让浏览器有机会消化该字符串 - 否则您可能会准确地取回放入的内容。我刚刚在 IE8 上尝试过,它看起来像字符串立即被解析,但我对 IE6 不太确定。最好在延迟后读取 innerHTML(或将其放入 setTimeout() 以强制它到队列的末尾)。

我建议你接受@Gordon 的建议并使用 Tidy,如果你可以访问它(它需要更少的工作来实现)并且失败了,使用 innerHTML 并在 PHP 中编写你自己的 tidy 函数。

尽管这不是您的问题的一部分,因为这是针对 CMS 的,但也可以考虑将YUI 2 Rich Text Editor用于此类内容。它相当容易实现,有点容易定制,大多数用户都非常熟悉该界面,并且它会输出完全有效的代码。还有其他几种现成的富文本编辑器,但 YUI 拥有最好的许可证,也是我见过的最强大的。