Подтвердить что ты не робот

Как закрыть закрытые HTML-теги?

Всякий раз, когда мы извлекаем некоторый пользовательский контент с некоторым редактированием из базы данных или аналогичных источников, мы можем получить часть, которая содержит только тег открытия, но не закрывается.

Это может помешать текущему макету веб-сайта.

Есть ли у клиента или серверный способ его исправления?

4b9b3361

Ответ 1

Нашел отличный ответ для этого:

Используйте PHP 5 и используйте метод loadHTML() объекта DOMDocument. Этот автоматический анализ плохо сформированного HTML и последующий вызов saveXML() выдаст допустимый HTML. Функции DOM можно найти здесь:

http://www.php.net/dom

Использование этого:

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

Ответ 2

Вы можете использовать Tidy:

Tidy является связующим для утилиты чистки и восстановления Tidy HTML, которая позволяет не только очищать и иным образом манипулировать HTML-документами, но также перемещаться по дереву документов.

или HTMLPurifier

HTML-очиститель - это стандартно-совместимый   Библиотека HTML-фильтра, написанная в   PHP. Очиститель HTML не только удалит все вредоносные   код (более известный как XSS) с тщательно проверенным,   безопасный, но разрешающий белый список,   он также гарантирует, что ваши документы   совместимых стандартов, что только достижимо с   всестороннее знание спецификаций W3C.

Ответ 3

У меня есть решение для 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"); ?>

Ответ 4

В дополнение к серверным инструментам, таким как Tidy, вы также можете использовать браузер пользователя для выполнения некоторой очистки. Одна из самых замечательных вещей о innerHTML заключается в том, что он будет применять один и тот же "на лету" ремонт к динамическому контенту, как и к HTML-страницам. Этот код работает очень хорошо (с двумя оговорками), и на самом деле ничего не записывается на страницу:

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

Предостережения:

  • Различные браузеры возвращают разные строки. Это не так уж плохо, за исключением IE, который вернет заглавные теги и разделит кавычки от атрибутов тегов, которые не пройдут проверку. Решение здесь - сделать простую очистку на стороне сервера. Но по крайней мере документ будет правильно структурирован XML.

  • Я подозреваю, что вам может потребоваться задержка перед чтением innerHTML - дать браузеру возможность переварить строку - или вы рискуете получить обратно именно то, что было введено. Я просто попробовал на IE8, и похоже, что строка сразу анализируется, но я не уверен в IE6. Вероятно, лучше было бы прочитать innerHTML после задержки (или выбросить его в setTimeout(), чтобы заставить его до конца очереди).

Я бы порекомендовал вам воспользоваться советом @Gordon и использовать Tidy, если у вас есть доступ к нему (требуется меньше усилий для реализации), и в противном случае используйте innerHTML и напишите свою собственную опрятную функцию в PHP.

И хотя это не является частью вашего вопроса, так как это для CMS, рассмотрите также использование YUI 2 Rich Text Editor для такие вещи. Это довольно легко реализовать, несколько легко настроить, интерфейс очень хорошо знаком большинству пользователей, и он выплескивает совершенно правильный код. Есть несколько других готовых текстовых редакторов, но YUI обладает лучшей лицензией и является самым мощным из всех, что я видел.

Ответ 5

Для фрагментов HTML и работы от ответа KJS У меня был успех со следующим, когда фрагмент имеет один корневой элемент:

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

Без корневого элемента это возможно (но, похоже, только первый текстовый дочерний элемент node в 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 для 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();    

Ответ 6

Лучшая функция PHP для удаления не открытых/не закрытых тегов из webmaster-glossar.de(me)

function closetag($html){
    $html_new = $html;
    preg_match_all ( "#<([a-z]+)( .*)?(?!/)>#iU", $html, $result1);
    preg_match_all ( "#</([a-z]+)>#iU", $html, $result2);
    $results_start = $result1[1];
    $results_end = $result2[1];
    foreach($results_start AS $startag){
        if(!in_array($startag, $results_end)){
            $html_new = str_replace('<'.$startag.'>', '', $html_new);
        }
    }
    foreach($results_end AS $endtag){
        if(!in_array($endtag, $results_start)){
            $html_new = str_replace('</'.$endtag.'>', '', $html_new);
        }
    }
    return $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

Ответ 7

Эрик Арвидссон написал хороший парсер SAX SAX в 2004 году. http://erik.eae.net/archives/2004/11/20/12.18.31/

Он отслеживает открытые теги, поэтому с минималистичным обработчиком 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;
}

Ответ 8

Я использовал собственный метод DOMDocument, но с некоторыми улучшениями безопасности.

Примечание. Другие ответы, которые используют DOMDocument, не учитывают html-нити, такие как

This is a <em>HTML</em> strand

Приведенное выше фактически приведет к

<p>This is a <em>HTML</em> strand

Мое решение ниже

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;
    }
}