Javascript 如何关闭未关闭的HTML标记?

Javascript 如何关闭未关闭的HTML标记?,javascript,php,Javascript,Php,每当我们从数据库或类似的源获取一些用户输入的内容并进行一些编辑时,我们可能会检索只包含开始标记但不包含结束标记的部分 这会妨碍网站当前的布局 有没有客户端或服务器端的方法来解决此问题?您可以使用: Tidy是Tidy HTML clean and repair实用程序的绑定,它不仅允许您清理和处理HTML文档,还允许您遍历文档树 或 HTML净化器是符合标准的 用HTML编写的HTML过滤器库 PHP。HTML净化器不仅会删除所有恶意 代码(更好地称为XSS)具有经过彻底审核的, 安全但允许的白

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

这会妨碍网站当前的布局

有没有客户端或服务器端的方法来解决此问题?

您可以使用:

Tidy是Tidy HTML clean and repair实用程序的绑定,它不仅允许您清理和处理HTML文档,还允许您遍历文档树

HTML净化器是符合标准的 用HTML编写的HTML过滤器库 PHP。HTML净化器不仅会删除所有恶意 代码(更好地称为XSS)具有经过彻底审核的, 安全但允许的白名单, 它还将确保您的文档 符合标准,只有通过 全面了解W3C规范


我有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"); ?>

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

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

这些标记不是关闭的'; log(divTemp.innerHTML);

注意事项:

  • 不同的浏览器将返回不同的字符串。这并不是那么糟糕,除了IE,它将返回大写的标记,并将从标记属性中删除引号,而不会通过验证。这里的解决方案是在服务器端进行一些简单的清理。但至少文档将是结构正确的XML

  • 我怀疑您可能必须在读取innerHTML之前延迟一段时间——给浏览器一个消化字符串的机会——或者您可能会冒着返回输入内容的风险。我刚在IE8上试过,看起来字符串会立即被解析,但在IE6上我不太确定。最好在延迟后读取innerHTML(或者将其放入setTimeout()以强制其到达队列末尾)

  • 我建议你采纳@Gordon的建议,如果你有权使用Tidy(实现它需要更少的工作),那么就使用innerHTML并用PHP编写你自己的Tidy函数


    虽然这不是你的问题的一部分,因为这是一个CMS,也考虑使用这样的东西。它相当容易实现,有点容易定制,界面对大多数用户来说都非常熟悉,而且它能写出完全有效的代码。还有其他几种现成的富文本编辑器,但YUI拥有最好的许可证,是我见过的最强大的编辑器。

    Erik Arvidsson在2004年编写了一个漂亮的HTML SAX解析器

    它跟踪打开的标记,所以使用最简单的SAX处理程序,可以在正确的位置插入关闭标记:

    function tidyHTML(html) {
        var output = '';
        HTMLParser(html, {
            comment: function(text) {
                // filter html comments
            },
            chars: function(text) {
                output += text;
            },
            start: function(tagName, attrs, unary) {
                output += '<' + tagName;
                for (var i = 0; i < attrs.length; i++) {
                    output += ' ' + attrs[i].name + '=';
                    if (attrs[i].value.indexOf('"') === -1) {
                        output += '"' + attrs[i].value + '"';
                    } else if (attrs[i].value.indexOf('\'') === -1) {
                        output += '\'' + attrs[i].value + '\'';
                    } else { // value contains " and ' so it cannot contain spaces
                        output += attrs[i].value;
                    }
                }
                output += '>';
            },
            end: function(tagName) {
                output += '</' + tagName + '>';
            }
        });
        return output;
    }
    
    函数tidyHTML(html){
    var输出=“”;
    HTMLPasser(html{
    注释:函数(文本){
    //过滤html注释
    },
    字符:函数(文本){
    输出+=文本;
    },
    开始:函数(标记名、属性、一元){
    输出+='';
    },
    结束:函数(标记名){
    输出+='';
    }
    });
    返回输出;
    }
    
    找到了一个很好的答案:

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

    此选项的用法:

    $doc = new DOMDocument();
    $doc->loadHTML($yourText);
    $yourText = $doc->saveHTML();
    

    从webmaster-glossar.de(me)中删除未打开/未关闭标记的更好PHP函数

    函数closetag($html){
    $html_new=$html;
    preg#u match_all(“##iU“,$html,$result1);
    preg#u match_all(“##iU“,$html,$result2);
    $results_start=$result1[1];
    $results_end=$result2[1];
    foreach($results\u以$startag开头){
    如果(!in_数组($startag,$results_end)){
    $html_new=str_replace('',$html_new);
    }
    }
    foreach($results\u end AS$endtag){
    如果(!在数组中($endtag,$results\u start)){
    $html_new=str_replace('',$html_new);
    }
    }
    返回$html_new;
    }
    
    使用此功能,如:

    closetag('i <b>love</b> my <strike>cat'); 
    #output: i <b>love</b> my cat
    
    closetag('i <b>love</b> my cat</strike>'); 
    #output: i <b>love</b> my cat
    
    closetag(“我爱我的猫”);
    #输出:我爱我的猫
    closetag(“我爱我的猫”);
    #输出:我爱我的猫
    
    我习惯于原生的DOMDocument方法,但在安全性方面有一些改进

    <> Pote,使用DOMBOOST的其他答案不考虑HTML字符串,如

    This is a <em>HTML</em> strand
    
    这是一条HTML链
    
    上述情况实际上会导致

    <p>This is a <em>HTML</em> strand
    
    这是一条HTML链
    
    我的解决方案如下

    function closeDanglingTags($html) {
        if (strpos($html, '<') || strpos($html, '>')) {
            // There are definitiley HTML tags
            $wrapped = false;
            if (strpos(trim($html), '<') !== 0) {
                // The HTML starts with a text node. Wrap it in an element with an id to prevent the software wrapping it with a <p>
                //  that we know nothing about and cannot safely retrieve
                $html = cHE::getDivHtml($html, null, 'closedanglingtagswrapper');
                $wrapped = true;
            }
            $doc = new DOMDocument();
            $doc->encoding = 'utf-8';
            @$doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
            if ($doc->firstChild) {
                // Test whether the firstchild is definitely a DOMDocumentType
                if ($doc->firstChild instanceof DOMDocumentType) {
                    // Remove the added doctype
                    $doc->removeChild($doc->firstChild);
                }
            }
            if ($wrapped) {
                // The contents originally started with a text node and was wrapped in a div#plasmappclibtextwrap. Take the contents
                //  out of that div
                $node = $doc->getElementById('closedanglingtagswrapper');
                $children = $node->childNodes;  // The contents of the div. Equivalent to $('selector').children()
                $doc = new DOMDocument();   // Create a new document to add the contents to, equiv. to "var doc = $('<html></html>');"
                foreach ($children as $childnode) {
                    $doc->appendChild($doc->importNode($childnode, true)); // E.g. doc.append()
                }
            }
            // Remove the added html,body tags
            return trim(str_replace(array('<html><body>', '</body></html>'), '', html_entity_decode($doc->saveHTML())));
        } else {
            return $html;
        }
    }
    
    函数closeDanglingTags($html){
    if(strpos($html,,)){
    //有明确的HTML标记
    $wrapped=false;
    
    如果(strpos(trim($html),“用于html片段,并且当片段有一个根元素时,从我开始的工作已经成功地实现了以下几点:

    $dom = new DOMDocument();
    $dom->loadHTML($string);
    $body = $dom->documentElement->firstChild->firstChild;
    $string = $dom->saveHTML($body);
    
    如果没有根元素,这是可能的(但似乎只在
    textpara

    text
    中的p标记中包装第一个文本子节点):

    或者更好的是,从PHP>=5.4和libxml>=2.7.8(对于
    libxml\u HTML\u noimpled
    ,为2.7.7):


    我喜欢这个函数。我看到的一个问题是,它无法修复一些用户非常擅长的损坏的嵌套(例如,
    “粗体和斜体文本”
    )。-1检查
    count($closedtags)==count($openedtags)
    是不够的…例如,
    改进XHTML:
    $doc=new DOMDocument();libxml使用内部错误(true);$doc->loadHTML('.$html_text.');libxml_clear_errors();$html_text=
    
    $dom = new DOMDocument();
    $dom->loadHTML($string);
    $body = $dom->documentElement->firstChild->firstChild;
    $string = $dom->saveHTML($body);
    
    $dom = new DOMDocument();
    $dom->loadHTML($string);
    $bodyChildNodes = $dom->documentElement->firstChild->childNodes;
    
    $string = '';
    foreach ($bodyChildNodes as $node){
       $string .= $dom->saveHTML($node);
    }
    
    $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();