Проверьте, является ли строка html или нет

100

У меня есть определенная строка, для которой я хочу проверить, является ли это html или нет. Я использую регулярное выражение для того же, но не получаю должного результата.

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

var htmlRegex = new RegExp("<([A-Za-z][A-Za-z0-9]*)\b[^>]*>(.*?)</\1>");
return htmlRegex.test(testString);

Вот скрипка, но регулярное выражение там не работает. http://jsfiddle.net/wFWtc/

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

user1240679
источник
5
Используйте парсер HTML для анализа HTML. Пожалуйста, прочтите это, если вы еще этого не сделали.
Фредерик Хамиди
3
вопрос продолжает появляться, должен быть стек-бот, который автоматически будет устанавливать комментарий к каждому вопросу с html и регулярным выражением в нем
Бартломей Левандовски
3
Это зависит от того, какой уровень сложности вы хотите получить от чека. Вы можете проверить, содержит ли строка хотя бы один <и хотя бы один, >и назвать ее HTML, или вы можете проверить, что она строго действительна с правильным синтаксисом HTML или что-то среднее. В простейшем случае анализатор HTML не требуется.
JJJ
3
Почему вы проверяете, что строка является HTML?
nhahtdh
2
@ user1240679: Допустимый формат разметки? Какая справедливость? В самом строгом смысле для его описания вам понадобится DTD. В широком смысле вы можете проверить, правильно ли сопоставлены теги. Ни один из двух приведенных выше случаев не относится к регулярному выражению.
nhahtdh

Ответы:

319

Лучшее регулярное выражение для проверки того, является ли строка HTML:

/^/

Например:

/^/.test('') // true
/^/.test('foo bar baz') //true
/^/.test('<p>fizz buzz</p>') //true

Фактически, это настолько хорошо, что он будет возвращаться trueдля каждой переданной ему строки, потому что каждая строка является HTML . Серьезно, даже если он плохо отформатирован или недействителен, это все равно HTML.

Если вы ищете наличие элементов HTML, а не просто текстовое содержимое, вы можете использовать что-то вроде:

/<\/?[a-z][\s\S]*>/i.test()

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

zzzzBov
источник
52
Я искренне удивлен, что не получил больше отрицательных голосов за снарк.
zzzzBov
8
@clenemt, так ты считаешь a < b && a > cHTML?
zzzzBov
1
@zzzzBov, ты знаешь, что считаешь a<b && a>cHTML ... Я бы хотел, чтобы обнаружение HTML было настолько упрощено. Разбор никогда не бывает легким.
Ориадам,
2
@oriadam, в этом случае контекст был для обнаружения элементов. Если вы используете a < b && a > cбраузер поворачивать >и <символы в &gt;и &lt;лицо надлежащим образом . Если вместо этого вы используете a<b && a>cбраузер, он будет интерпретировать разметку как a<b && a>c</b>отсутствие пробела, что означает <bоткрытие <b>элемента. Вот небольшая демонстрация того, о чем я говорю .
zzzzBov
4
Это, вероятно, самый популярный ответ троллей, который я видел. ;)
aandis
74

Способ №1 . Вот простая функция для проверки, содержит ли строка данные HTML:

function isHTML(str) {
  var a = document.createElement('div');
  a.innerHTML = str;

  for (var c = a.childNodes, i = c.length; i--; ) {
    if (c[i].nodeType == 1) return true; 
  }

  return false;
}

Идея состоит в том, чтобы позволить парсеру DOM браузера решать, выглядит ли предоставленная строка как HTML или нет. Как видите, он просто проверяет ELEMENT_NODE( nodeTypeиз 1).

Провел пару тестов и вроде работает:

isHTML('<a>this is a string</a>') // true
isHTML('this is a string')        // false
isHTML('this is a <b>string</b>') // true

Это решение будет правильно определять строку HTML, однако имеет побочный эффект: img / vide / etc. Теги начнут загружать ресурс после анализа в innerHTML.

Способ №2 . Другой метод использует DOMParser и не имеет побочных эффектов загрузки ресурсов:

function isHTML(str) {
  var doc = new DOMParser().parseFromString(str, "text/html");
  return Array.from(doc.body.childNodes).some(node => node.nodeType === 1);
}

Примечания:
1. Array.fromэто метод ES2015, можно заменить на [].slice.call(doc.body.childNodes).
2. Стрелочную функцию в someвызове можно заменить обычной анонимной функцией.

dfsq
источник
3
Отличная идея. Однако эта функция не смогла обнаружить закрывающий тег (т.е. isHTML("</a>") --> false).
Льюис
9
Отличное решение! .. Единственный отрицательный побочный эффект заключается в том, что если ваш html содержит какие-либо статические ресурсы, такие как атрибут src изображения ... innerHTMLзаставит браузер начать выборку этих ресурсов. :(
Jose Browne
@JoseBrowne, даже если он не добавлен в DOM?
kuus
1
@kuus Да, даже если не дописывать. Используйте решение DOMParser.
dfsq
1
Хорошая идея, но разве принятый ответ не будет лучше для производительности? Особенно, если у вас огромные строки (каламбур) или вам приходится часто использовать этот тест.
DerpyNerd
13

Немного проверки с помощью:

/<(?=.*? .*?\/ ?>|br|hr|input|!--|wbr)[a-z]+.*?>|<([a-z]+).*?<\/\1>/i.test(htmlStringHere) 

Это ищет пустые теги (некоторые из них предопределены) и /завершенные пустые теги XHTML и проверяет как HTML из-за пустого тега ИЛИ захватывает имя тега и пытается найти его закрывающий тег где-нибудь в строке для проверки как HTML.

Разъясненная демонстрация: http://regex101.com/r/cX0eP2

Обновить:

Полная проверка с помощью:

/<(br|basefont|hr|input|source|frame|param|area|meta|!--|col|link|option|base|img|wbr|!DOCTYPE).*?>|<(a|abbr|acronym|address|applet|article|aside|audio|b|bdi|bdo|big|blockquote|body|button|canvas|caption|center|cite|code|colgroup|command|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frameset|head|header|hgroup|h1|h2|h3|h4|h5|h6|html|i|iframe|ins|kbd|keygen|label|legend|li|map|mark|menu|meter|nav|noframes|noscript|object|ol|optgroup|output|p|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|span|strike|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video).*?<\/\2>/i.test(htmlStringHere) 

Это правильная проверка, поскольку она содержит ВСЕ теги HTML, сначала пустые, а затем остальные, которым нужен закрывающий тег.

Разъясненная демонстрация здесь: http://regex101.com/r/pE1mT5

CSᵠ
источник
1
Обратите внимание, что нижнее регулярное выражение действительно работает, но оно не обнаруживает незакрытые теги HTML, такие как «<strong> hello world». при условии, что это неработающий HTML, поэтому его следует рассматривать как строку, но для практических целей ваше приложение может также захотеть их обнаружить.
TK123
HTML разработан с учетом всех требований пользовательских агентов. «Недействительные» теги не являются недействительными, они просто неизвестны и разрешены. «Недопустимые» атрибуты не являются недопустимыми… Это особенно заметно, когда начинают задействовать «веб-компоненты» и такие технологии, как JSX, которые смешивают HTML и более подробные описания компонентов, обычно генерируя теневую DOM. Вставьте это в файл и document.querySelector('strange')оцените - это сработает.
amcgregor
(Подводя итог: из-за того, как написана спецификация, попытка «проверить» разметку HTML - это, по сути, дурацкая затея. Ссылка на образец HTML-документа с «недопустимым» элементом является на 100% полностью сформированной, полный HTML-документ - и был с 1997 года - в качестве другого примера.)
amcgregor
10

Ответ zzzzBov выше хорош, но он не учитывает случайные закрывающие теги, например:

/<[a-z][\s\S]*>/i.test('foo </b> bar'); // false

Версия, которая также улавливает закрывающие теги, может быть следующей:

/<[a-z/][\s\S]*>/i.test('foo </b> bar'); // true
AeonOfTime
источник
Было бы лучше предложить правку, чем публиковать это как комментарий.
Златин Златев
Я думаю, вы имеете в виду <[a-z/][\s\S]*>- обратите внимание на косую черту в первой группе.
Райан Гилл
7

Вот неаккуратный однострочник, который я использую время от времени:

var isHTML = RegExp.prototype.test.bind(/(<([^>]+)>)/i);

Он будет в основном возвращаться trueдля строк, содержащих a, <за которым ANYTHINGследует> .

По ANYTHING словом я подразумеваю практически все, кроме пустой строки.

Это не здорово, но это однострочный.

использование

isHTML('Testing');               // false
isHTML('<p>Testing</p>');        // true
isHTML('<img src="hello.jpg">'); // true
isHTML('My < weird > string');   // true (caution!!!)
isHTML('<>');                    // false

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

Йохан Деттмар
источник
1
именно то, что мне нужно. Ничего особенного, просто чисто. Спасибо!
moeiscool
6

Все ответы здесь чрезмерны, они просто ищут, а за <ними следует >. Не существует идеального способа определить, является ли строка HTML, но вы можете сделать лучше.

Ниже мы ищем закрывающие теги , они будут намного точнее и точнее:

import re
re_is_html = re.compile(r"(?:</[^<]+>)|(?:<[^<]+/>)")

И вот оно в действии:

# Correctly identified as not HTML:
print re_is_html.search("Hello, World")
print re_is_html.search("This is less than <, this is greater than >.")
print re_is_html.search(" a < 3 && b > 3")
print re_is_html.search("<<Important Text>>")
print re_is_html.search("<a>")

# Correctly identified as HTML
print re_is_html.search("<a>Foo</a>")
print re_is_html.search("<input type='submit' value='Ok' />")
print re_is_html.search("<br/>")

# We don't handle, but could with more tweaking:
print re_is_html.search("<br>")
print re_is_html.search("Foo &amp; bar")
print re_is_html.search("<input type='submit' value='Ok'>")
скоростной самолет
источник
4

Если вы создаете регулярное выражение из строкового литерала, вам нужно избегать любых обратных косых черт:

var htmlRegex = new RegExp("<([A-Za-z][A-Za-z0-9]*)\\b[^>]*>(.*?)</\\1>");
// extra backslash added here ---------------------^ and here -----^

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

var htmlRegex = /<([A-Za-z][A-Za-z0-9]*)\b[^>]*>(.*?)<\/\1>/;
// forward slash escaped here ------------------------^

Также ваш jsfiddle не работал, потому что вы назначили onloadобработчик внутри другого onloadобработчика - по умолчанию, установленное на панели Frameworks & Extensions слева, заключается в том, чтобы обернуть JS в файл onload. Измените это на опцию nowrap и исправьте экранирование строкового литерала, и оно «работает» (в рамках ограничений, на которые все указали в комментариях): http://jsfiddle.net/wFWtc/4/

Насколько мне известно, регулярные выражения JavaScript не имеют обратных ссылок. Итак, эта часть вашего выражения:

</\1>

не будет работать в JS (но будет работать на некоторых других языках).

ннннн
источник
Что ж, это проверит, что один из тегов выглядит нормально, но ничего об остальных. Не уверен, какой «достоверности» хочет OP.
nhahtdh
1
а как насчет <br> <hr> <input...>@ user1240679?
CSᵠ
3

/<\/?[^>]*>/.test(str) Только определить, содержит ли он теги html, может быть xml

сиять
источник
27 is < 42, and 96 > 42. Это не HTML.
amcgregor
3

С jQuery:

function isHTML(str) {
  return /^<.*?>$/.test(str) && !!$(str)[0];
}
gtournie
источник
2
isHTML("<foo>");// возвращает истину isHTML("div");// возвращает истину, если divна странице есть s
ACK_stoverflow
@yekta - О чем ты? Предполагается, что это проверяет, является ли строка html или нет. Насколько я знаю, электронное письмо не является тегом html ... isHTML ('foo@bar.com ') -> false // правильно
gtournie
1
Строка может быть чем угодно, если вы знаете ее HTML-тег, тогда зачем вообще проверять его HTML, я не совсем понимаю вашу точку зрения. @Не является корректным синтаксис для выбора. Таким образом, когда вы передадите его селектору jQuery, он выдаст исключение (т.е. $("you@example.com")из !!$(str)[0]). Я имею в виду именно !!$(str)[0] часть. Вы только что отредактировали свой ответ, но теперь проверяете HTML до того, как jQuery что-нибудь сделает.
yekta
Не думаю, что автор хотел проверить, была ли это просто строка. В этом-то и дело. Ему нужна была функция, способная проверять, является ли строка действительным тегом HTML , а не только HTML (иначе это немного глупо). Я обновил свой ответ после того, как прочитал комментарий @ACK_stoverflow, но я уверен, что это должно сделать простое регулярное выражение.
gtournie
3

В этом случае с помощью jQuery простейшей формой будет:

if ($(testString).length > 0)

Если $(testString).length = 1, это означает, что внутри есть один HTML-тег textStging.

Христо Пеев
источник
Согласно ответу чуть ниже (начиная с «С jQuery», написанного за четыре года до этого!), Рассмотрите плохой выбор нескольких вариантов использования из одной точки входа. $()это операция селектора CSS. Но также фабрика узлов DOM из текстовой сериализации HTML. Но также… согласно другому ответу, страдающему такой же зависимостью от jQuery, «div» не является HTML, но он вернется, trueесли <div>на странице существуют какие-либо элементы. Это очень и очень плохой подход, как я и ожидал практически от любого решения, в котором без необходимости используется jQuery. (Пусть умирает.)
amcgregor
2

Существуют причудливые решения, включающие использование самого браузера, чтобы попытаться проанализировать текст, определить, были ли созданы какие-либо узлы DOM, что будет… медленным. Или регулярные выражения, которые будут быстрее, но… потенциально неточными. Из этой проблемы также возникают два очень разных вопроса:

Q1: содержит ли строка фрагменты HTML?

Струнная часть HTML-документа, содержащего разметку HTML-элемента или закодированные объекты? Это может использоваться как индикатор того, что строка может потребовать обесцвечивания / дезинфекции или декодирования объекта:

/</?[a-z][^>]*>|(\&(?:[\w\d]+|#\d+|#x[a-f\d]+);/

Ты можешь видеть этот шаблон используется на всех примерах из всех существующих ответов на момент написания этой статьи, а также с некоторыми… довольно ужасными образцами текста, созданными WYSIWYG или Word, и множеством ссылок на символьные сущности.

Q2: Является ли строка HTML-документом?

Спецификация HTML шокирующе свободна в отношении того, что она считает документом HTML . Браузеры идут на все, чтобы проанализировать практически любой мусорный текст как HTML. Два подхода: либо просто рассмотрите весь HTML (поскольку, если он поставляется с text/htmlContent-Type, будут затрачены большие усилия, чтобы попытаться интерпретировать его как HTML пользовательским агентом), либо найдите маркер префикса:

<!DOCTYPE html>

С точки зрения "правильности" это и почти ничего "не требуется". Ниже приводится 100% полный, полностью действительный HTML-документ, содержащий все элементы HTML, которые, по вашему мнению, опускаются:

<!DOCTYPE html>
<title>Yes, really.</title>
<p>This is everything you need.

Ага. Есть четкие правила о том , как сформировать «отсутствующие» элементы , такие как <html>, <head>, и <body>. Хотя мне кажется довольно забавным, что подсветка синтаксиса SO не смогла правильно определить это без явной подсказки.

Amcgregor
источник
0

Мое решение

const element = document.querySelector('.test_element');

const setHtml = elem =>{
    let getElemContent = elem.innerHTML;

    // Clean Up whitespace in the element
    // If you don't want to remove whitespace, then you can skip this line
    let newHtml = getElemContent.replace(/[\n\t ]+/g, " ");

    //RegEX to check HTML
    let checkHtml = /<([A-Za-z][A-Za-z0-9]*)\b[^>]*>(.*?)<\/\1>/.test(getElemContent);

    //Check it is html or not
    if (checkHtml){
        console.log('This is an HTML');
        console.log(newHtml.trim());
    }
    else{
        console.log('This is a TEXT');
        console.log(elem.innerText.trim());
    }
}

setHtml(element);
Камруджаман Шохель
источник
Ваше регулярное выражение кажется сильно дефектным по сравнению с более полным выражением , и требовать предварительной обработки (первоначальной замены) крайне нежелательно.
amcgregor
-1

Существует пакет NPM is-html, который может попытаться решить эту проблему https://github.com/sindresorhus/is-html.

Колин Д
источник
Я не понимаю выражение, которое он пытается использовать, которое терпит неудачу, за исключением объявленного типа документа, а "полный" шаблон, созданный из известных элементов HTML, извлеченных из дополнительной зависимости, игнорирует тот факт, что HTML работает не так, и не был очень и очень давно. Кроме того, в базовом шаблоне явно упоминаются <html>и <body>теги, которые являются необязательными . Показательный тест на «несоответствие XML».
amcgregor
@amcgregor, если вы думаете, что ваше решение лучше, возможно, внесите свой вклад в репозиторий isHTML? и добавить свой набор тестов из regex101? это было бы полезно для сообщества
Колин Д.
Фундаментальная цель этой библиотеки неверна и будет по своей сути неправильной в большом количестве случаев, обычно из-за ложной пометки как не-HTML из-за наличия тегов, которые она не понимает; проверка не может быть успешной. Кроме того, простое регулярное выражение или (edit: пара ) библиотека [и]… мы, возможно, забыли, как программировать , а Node / NPM - это не язык или набор инструментов, которые я обычно хочу использовать, способствовать или поощрять использование .
amcgregor
Хорошо, amcgergor, ты относишься ко мне довольно негативно, когда я просто пытался помочь. Я не согласен с тем, что npm ошибочен. Представьте, что ваш ответ о переполнении стека в будущем придет с небольшой настройкой. Я, как разработчик, использующий вашу библиотеку, просто обновляюсь и получаю более правильное поведение. Вместо этого я должен ... жить с нарушенным поведением или вернуться к этому ответу о переполнении стека, чтобы получить ваши изменения? Это альтернативная вселенная
Колин Д.
Отрицательный? Я объяснял свою позицию и почему я не буду делать то, что в противном случае казалось бы разумным. Обратите внимание, однако, что статья, на которую я ссылаюсь, была продолжением немного более подстрекательской статьи (ссылка выше), которая вызвала множество дискуссий. Он опубликовал техническую статью , ссылка на которую также приведена ниже. Я противопоставляю ваше внутреннее предчувствие повторной работе доказательствами качества. Ссылка: §7.2 (и катастрофа левой панели и eslint)
amcgregor