Vérifiez si une chaîne est html ou non

98

J'ai une certaine chaîne pour laquelle je veux vérifier si c'est un html ou non. J'utilise regex pour le même mais n'obtiens pas le bon résultat.

J'ai validé mon regex et cela fonctionne très bien ici .

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

Voici le violon, mais l'expression régulière ne fonctionne pas là-dedans. http://jsfiddle.net/wFWtc/

Sur ma machine, le code fonctionne bien mais j'obtiens un faux au lieu de vrai comme résultat. Que manque-t-il ici?

user1240679
la source
5
Utilisez un analyseur HTML pour analyser le HTML. Veuillez lire ceci si vous ne l'avez pas déjà fait.
Frédéric Hamidi
3
la question continue à venir, il devrait y avoir un bot de pile qui mettra automatiquement un commentaire sur chaque question avec html et regex
Bartlomiej Lewandowski
3
Cela dépend un peu du niveau de sophistication que vous attendez du chèque. Vous pouvez vérifier si la chaîne en contient au moins un <et au moins un >et l'appeler HTML, ou vous pouvez vérifier qu'elle est strictement valide avec une syntaxe HTML correcte, ou quoi que ce soit entre les deux. Pour le plus simple des cas, un analyseur HTML n'est pas nécessaire.
JJJ
2
Pourquoi vérifiez-vous qu'une chaîne est HTML?
nhahtdh
2
@ user1240679: format de balisage valide? Quel genre de validité? Au sens strict, vous avez besoin de DTD pour le décrire. Dans un sens vague, vous voudrez peut-être vérifier que les balises correspondent correctement. L'un ou l'autre des 2 cas ci-dessus ne convient pas aux regex.
nhahtdh

Réponses:

315

Une meilleure regex à utiliser pour vérifier si une chaîne est HTML est:

/^/

Par exemple:

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

En fait, c'est tellement bon, qu'il retournera truepour chaque chaîne qui lui est passée, car chaque chaîne est HTML . Sérieusement, même s'il est mal formaté ou invalide, c'est toujours du HTML.

Si ce que vous recherchez est la présence d'éléments HTML, plutôt que simplement du contenu textuel, vous pouvez utiliser quelque chose du genre:

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

Cela ne vous aidera en aucun cas à analyser le HTML, mais cela marquera certainement la chaîne comme contenant des éléments HTML.

zzzzBov
la source
47
Je suis honnêtement surpris de ne pas avoir reçu plus de votes négatifs pour le snark.
zzzzBov
7
@clenemt, vous considérez a < b && a > cdonc comme du HTML?
zzzzBov
1
@zzzzBov vous savez que vous considérez a<b && a>cêtre du HTML ... Je souhaite que la détection HTML puisse être simplifiée à ce point. L'analyse n'est jamais facile.
oriadam
2
@oriadam, le contexte était pour détecter des éléments dans ce cas. Si vous utilisez a < b && a > cle navigateur, les caractères >et seront transformés <en entités &gt;et de &lt;manière appropriée. Si, à la place, vous utilisez a<b && a>cle navigateur interprétera le balisage comme a<b && a>c</b>parce que le manque d'espace signifie que cela <bouvre un <b>élément. Voici une rapide démonstration de ce dont je parle .
zzzzBov
4
C'est probablement la réponse troll la plus votée que j'ai vue. ;)
aandis
72

Méthode n ° 1 . Voici la fonction simple pour tester si la chaîne contient des données 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;
}

L'idée est de permettre à l'analyseur DOM du navigateur de décider si la chaîne fournie ressemble ou non à un HTML. Comme vous pouvez le voir, il vérifie simplement ELEMENT_NODE( nodeTypesur 1).

J'ai fait quelques tests et il semble que cela fonctionne:

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

Cette solution détectera correctement la chaîne HTML, mais elle a un effet secondaire qui img / vide / etc. Les balises commenceront à télécharger la ressource une fois analysées dans innerHTML.

Méthode n ° 2 . Une autre méthode utilise DOMParser et n'a pas d'effets secondaires de chargement des ressources:

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

Remarques:
1. Array.fromest la méthode ES2015, peut être remplacée par [].slice.call(doc.body.childNodes).
2. La fonction de flèche dans l' someappel peut être remplacée par la fonction anonyme habituelle.

dfsq
la source
3
C'est une idée géniale. Cependant, cette fonction ne pouvait pas détecter la balise de fermeture (ie isHTML("</a>") --> false).
Lewis
9
Excellente solution! .. Le seul effet secondaire négatif de est que si votre html contient des ressources statiques comme un attribut image src .. innerHTMLforcera le navigateur à commencer à récupérer ces ressources. :(
Jose Browne
@JoseBrowne même s'il n'est pas ajouté au DOM?
kuus
1
@kuus Oui, même s'il n'y a pas d'ajout. Utilisez la solution DOMParser.
dfsq
1
Bonne idée, mais la réponse acceptée ne serait-elle pas meilleure pour la performance? Surtout si vous avez d'énormes cordes (jeu de mots) ou si vous devez beaucoup utiliser ce test.
DerpyNerd
13

Un peu de validation avec:

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

Cela recherche des balises vides (certaines prédéfinies) et /des balises vides XHTML terminées et valide en HTML à cause de la balise vide OU capture le nom de la balise et tente de trouver sa balise de fermeture quelque part dans la chaîne pour la valider en HTML.

Démo expliquée: http://regex101.com/r/cX0eP2

Mettre à jour:

Validation complète avec:

/<(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) 

Cela fait une validation correcte car il contient TOUTES les balises HTML, les vides en premier, suivies par les autres qui nécessitent une balise de fermeture.

Démo expliquée ici: http://regex101.com/r/pE1mT5

CSᵠ
la source
1
Juste une note, l'expression régulière du bas fonctionne mais elle ne détectera pas les balises html non fermées telles que "'<strong> hello world". si cela est cassé, le code HTML doit donc être traité comme une chaîne, mais pour des raisons pratiques, votre application peut également vouloir les détecter.
TK123 du
Le HTML est conçu en pensant à la tolérance des agents utilisateurs. Les balises "non valides" ne sont pas invalides, elles sont simplement inconnues et autorisées. Les attributs "non valides" ne sont pas invalides… Ceci est particulièrement notable quand on commence à impliquer des "composants Web" et des technologies comme JSX, qui mélangent du HTML et des descriptions de composants plus riches, générant généralement un DOM shadow. Claquez ceci dans un fichier et évaluez document.querySelector('strange')- cela fonctionnera.
amcgregor le
(Pour résumer: en raison de la façon dont la spécification est écrite, tenter de "valider" le balisage HTML est essentiellement une course d'idiot. Le lien donné vers un exemple de document HTML avec un élément "invalide", là, est un 100% entièrement formé, document HTML complet - et depuis 1997 - comme autre exemple.)
amcgregor
9

La réponse de zzzzBov ci-dessus est bonne, mais elle ne tient pas compte des balises de fermeture parasites, comme par exemple:

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

Une version qui capture également les balises de fermeture pourrait être la suivante:

/<[a-z/][\s\S]*>/i.test('foo </b> bar'); // true
AeonOfTime
la source
Cela aurait pu être mieux de suggérer une modification, au lieu de publier ceci sous forme de commentaire.
Zlatin Zlatev
Je pense que vous voulez dire <[a-z/][\s\S]*>- notez la barre oblique dans le premier groupe.
Ryan Guill
7

Voici un sloppy one-liner que j'utilise de temps en temps:

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

Il retournera essentiellement truepour les chaînes contenant un <suivi de ANYTHINGsuivi de >.

Par ANYTHING, je veux dire essentiellement tout sauf une chaîne vide.

Ce n'est pas génial, mais c'est une ligne unique.

Usage

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

Comme vous pouvez le voir, c'est loin d'être parfait, mais pourrait faire le travail pour vous dans certains cas.

Johan Dettmar
la source
1
juste ce dont j'avais besoin. Rien d'extraordinaire, juste propre. Merci!
moeiscool
6

Toutes les réponses ici sont trop inclusives, elles recherchent simplement <suivies de >. Il n'y a pas de moyen parfait de détecter si une chaîne est HTML, mais vous pouvez faire mieux.

Ci-dessous, nous recherchons des balises de fin , et seront beaucoup plus serrées et plus précises:

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

Et le voici en action:

# 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'>")
speedplane
la source
4

Si vous créez une expression régulière à partir d'une chaîne littérale, vous devez échapper à toute barre oblique inverse:

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

Ce n'est pas nécessaire si vous utilisez un littéral regex, mais vous devez ensuite échapper les barres obliques:

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

De plus, votre jsfiddle n'a pas fonctionné parce que vous avez affecté un onloadgestionnaire dans un autre onloadgestionnaire - la valeur par défaut définie dans le panneau Frameworks & Extensions sur la gauche consiste à envelopper le JS dans un fichier onload. Changez cela en une option nowrap et corrigez la chaîne littérale qui s'échappe et cela "fonctionne" (dans les limites que tout le monde a indiquées dans les commentaires): http://jsfiddle.net/wFWtc/4/

Pour autant que je sache, les expressions régulières JavaScript n'ont pas de références arrière. Donc cette partie de votre expression:

</\1>

ne fonctionnera pas dans JS (mais fonctionnera dans d'autres langues).

nnnnnn
la source
Eh bien, cela testera que l'une des balises semble correcte, mais rien sur le reste. Je ne sais pas quelle sorte de «validité» veut l'OP.
nhahtdh
1
qu'en est-il de <br> <hr> <input...>@ user1240679?
CSᵠ
3

/<\/?[^>]*>/.test(str) Détecter uniquement s'il contient des balises html, peut être un xml

briller
la source
27 is < 42, and 96 > 42. Ce n'est pas du HTML.
amcgregor
3

Avec jQuery:

function isHTML(str) {
  return /^<.*?>$/.test(str) && !!$(str)[0];
}
gtournie
la source
2
isHTML("<foo>");// renvoie true isHTML("div");// renvoie true s'il y a des divs sur la page
ACK_stoverflow
@yekta - De quoi parlez-vous? Ceci est censé vérifier si la chaîne est html ou non. Un email n'est pas une balise html pour autant que je sache ... isHTML ('[email protected] ') -> false // correct
gtournie
1
Une chaîne peut être n'importe quoi, si vous savez que c'est une balise HTML, alors pourquoi vérifier si son HTML en premier lieu, je ne suis pas tout à fait votre point de vue. Le @n'est pas une syntaxe valide pour un sélecteur. Ainsi, lorsque vous le passez à un sélecteur jQuery, il lèvera une exception (ie $("[email protected]")from !!$(str)[0]). Je parle spécifiquement de la !!$(str)[0] partie. Vous venez de modifier votre réponse, mais vous vérifiez maintenant le HTML avant que jQuery ne fasse quoi que ce soit.
yekta
Je ne pense pas que l'auteur ait voulu vérifier s'il ne s'agissait que d'une chaîne. C'est le but. Ce qu'il voulait, c'était une fonction capable de vérifier si la chaîne était une balise HTML valide , pas seulement HTML (sinon c'est un peu stupide). J'ai mis à jour ma réponse après avoir lu le commentaire @ACK_stoverflow, mais je suis sûr qu'une simple regex devrait le faire.
gtournie
3

En utilisant jQuery dans ce cas, la forme la plus simple serait:

if ($(testString).length > 0)

Si $(testString).length = 1, cela signifie qu'il y a une balise HTML à l'intérieur textStging.

Christo Peev
la source
Comme pour la réponse ci-dessous (commençant par "Avec jQuery", écrit quatre ans avant celui-ci!), Considérez le mauvais choix de multiples utilisations à partir d'un seul point d'entrée. $()est une opération de sélection CSS. Mais aussi une fabrique de nœuds DOM à partir de la sérialisation HTML textuelle. Mais aussi… selon l'autre réponse souffrant de la même dépendance sur jQuery, "div" n'est pas du HTML, mais cela reviendrait truesi des <div>éléments existent sur la page. C'est une très, très mauvaise approche, comme je m'y attendais avec presque toutes les solutions impliquant inutilement jQuery. (Laissez-le mourir.)
amcgregor
1

Il existe des solutions sophistiquées impliquant l'utilisation du navigateur lui-même pour tenter d'analyser le texte, en identifiant si des nœuds DOM ont été construits, ce qui sera… lent. Ou des expressions régulières qui seront plus rapides, mais… potentiellement inexactes. Deux questions très distinctes se posent également à propos de ce problème:

Q1: Une chaîne contient-elle des fragments HTML?

La chaîne fait-elle partie d'un document HTML, contenant un balisage d'élément HTML ou des entités codées? Cela peut être utilisé comme un indicateur que la chaîne peut nécessiter un blanchiment / désinfection ou un décodage d'entité:

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

Vous pouvez voir ce modèle utilisé contre tous les exemples de toutes les réponses existantes au moment de la rédaction de cet article, ainsi que quelques exemples de texte ... plutôt hideux générés par WYSIWYG ou Word et une variété de références d'entités de caractères.

Q2: La chaîne est-elle un document HTML?

La spécification HTML est incroyablement lâche quant à ce qu'elle considère comme un document HTML . Les navigateurs vont à l'extrême pour analyser presque tous les textes inutiles au format HTML. Deux approches: soit simplement considérer tout HTML (car s'il est livré avec un text/htmlContent-Type, un gros effort sera dépensé pour essayer de l'interpréter comme HTML par l'agent utilisateur) ou rechercher le marqueur de préfixe:

<!DOCTYPE html>

En termes de "bonne formation", cela, et presque rien d'autre n'est "requis". Ce qui suit est un document HTML 100% complet et entièrement valide contenant tous les éléments HTML que vous pensez être omis:

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

Ouaip. Il existe des règles explicites sur la façon de former des éléments « manquants » tels que <html>, <head>et <body>. Bien que je trouve plutôt amusant que la coloration syntaxique de SO n'ait pas réussi à détecter cela correctement sans un indice explicite.

amcgregor
la source
0

Ma solution est

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);
Kamrujaman Shohel
la source
Votre expression régulière semble très défectueuse par rapport à une expression plus complète , et exiger un prétraitement (le remplacement initial) est très malheureux.
amcgregor
-1

Il existe un package NPM is-html qui peut tenter de résoudre ce problème https://github.com/sindresorhus/is-html

Colin D
la source
Je ne comprends pas l'expression qu'il tente d'utiliser qui échoue sauf sur le doctype déclaré, et le modèle "complet" construit à partir d'éléments HTML connus extraits d'une dépendance supplémentaire ignore le fait que ce n'est pas ainsi que HTML fonctionne, et ne l'a pas depuis très, très longtemps. De plus, le modèle de base mentionne explicitement <html>et <body>balises, qui sont tous deux entièrement facultatifs . Le test "pas de correspondance XML" est révélateur.
amcgregor
@amcgregor si vous pensez que votre solution est meilleure, peut-être contribuer au repo isHTML? et ajoutez votre suite de tests de regex101? ce serait précieux pour la communauté
Colin D
Le but fondamental de cette bibliothèque est erroné et sera intrinsèquement faux dans un grand nombre de cas, généralement par faux-drapeau comme non-HTML en raison de la présence de balises qu'il ne comprend pas; la validation ne peut pas réussir de cette façon. De plus, une simple expression régulière ou une (modifier: paire de ) librar [s]… nous avons peut-être oublié comment programmer , et Node / NPM n'est pas un langage ou une chaîne d'outils que je souhaite généralement utiliser, contribuer à ou encourager l'utilisation de .
amcgregor
D'accord, amcgergor, tu es plutôt négatif avec moi quand j'essayais juste d'aider. Je ne suis pas d'accord avec le principe selon lequel npm est mal orienté. Imaginez que votre réponse de débordement de pile propose une petite modification à l'avenir. En tant que développeur utilisant votre bibliothèque, je ferais simplement une mise à niveau et j'obtiendrais un comportement plus approprié. Au lieu de cela, je dois ... vivre avec le comportement cassé ou revoir cette réponse de débordement de pile pour obtenir vos modifications? C'est l'univers alternatif
Colin D
Négatif? J'expliquais ma position et pourquoi je ne ferais pas ce qui semblerait autrement raisonnable. Notez, cependant, que l'article que j'ai lié était la suite d' une première un peu plus incendiaire (liée à l'avance) qui a suscité de nombreuses discussions. Il a publié un article technique , également lié là-bas, vers le bas. Je contrecarre votre instinct de retravailler avec des preuves de qualité. Réf: §7.2 (& le désastre du pad gauche et eslint)
amcgregor