Controlla se una stringa è html o meno


98

Ho una certa stringa per la quale voglio controllare se è un html o meno. Sto usando regex per lo stesso ma non ottengo il risultato corretto.

Ho convalidato il mio regex e funziona benissimo qui .

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

Ecco il violino ma la regex non è in esecuzione lì. http://jsfiddle.net/wFWtc/

Sulla mia macchina, il codice funziona correttamente, ma come risultato ottengo un falso anziché vero. Cosa manca qui?


5
Utilizza un parser HTML per analizzare l'HTML. Per favore leggi questo se non l'hai già fatto.
Frédéric Hamidi

3
la domanda continua a venire, dovrebbe esserci uno stack bot che imposterà in modo quasi automatico un commento su ogni domanda con html e regex al suo interno
Bartlomiej Lewandowski

3
Dipende dal livello di raffinatezza che desideri dall'assegno. Potresti controllare se la stringa contiene almeno uno <e almeno uno >e chiamarlo HTML, oppure puoi verificare che sia strettamente valido con la sintassi HTML corretta, o qualsiasi altra cosa tra. Per i casi più semplici non è necessario un parser HTML.
JJJ

2
Perché controlli che una stringa sia HTML?
nhahtdh

2
@ user1240679: formato di markup valido? Che tipo di validità? In senso stretto, è necessario DTD per descriverlo. In senso lato, potresti voler controllare che i tag siano abbinati correttamente. Nessuno dei 2 casi precedenti non è un lavoro per regex.
nhahtdh

Risposte:


315

Una regex migliore da utilizzare per verificare se una stringa è HTML è:

/^/

Per esempio:

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

In effetti, è così buono che tornerà trueper ogni stringa passata, perché ogni stringa è HTML . Seriamente, anche se è mal formattato o non valido, è comunque HTML.

Se quello che stai cercando è la presenza di elementi HTML, piuttosto che semplicemente qualsiasi contenuto di testo, potresti usare qualcosa del tipo:

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

Non ti aiuterà in alcun modo ad analizzare l'HTML, ma sicuramente contrassegnerà la stringa come contenente elementi HTML.


47
Sono onestamente sorpreso di non aver ricevuto più voti negativi per lo snark.
zzzzBov

7
@clenemt, quindi consideri a < b && a > cHTML?
zzzzBov

1
@zzzzBov sai che consideri a<b && a>cHTML ... Vorrei che il rilevamento dell'HTML potesse essere semplificato così tanto. L'analisi non è mai facile.
oriadam

2
@oriadam, il contesto era per rilevare elementi in quel caso. Se utilizzi a < b && a > cil browser, i caratteri >e verranno trasformati <in entità &gt;e in modo &lt;appropriato. Se invece utilizzi a<b && a>cil browser interpreterai il markup come a<b && a>c</b>perché la mancanza di uno spazio significa che <bapre un <b>elemento. Ecco una rapida demo di ciò di cui sto parlando .
zzzzBov

4
Questa è probabilmente la risposta di troll più votata che abbia mai visto. ;)
aandis

72

Metodo n . 1 . Ecco la semplice funzione per verificare se la stringa contiene dati 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'idea è di consentire al parser DOM del browser di decidere se la stringa fornita assomiglia a un HTML o meno. Come puoi vedere, controlla semplicemente ELEMENT_NODE( nodeTypedi 1).

Ho fatto un paio di test e sembra che funzioni:

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

Questa soluzione rileverà correttamente la stringa HTML, tuttavia ha un effetto collaterale che img / vide / ecc. i tag inizieranno a scaricare la risorsa una volta analizzati in innerHTML.

Metodo n . 2 . Un altro metodo utilizza DOMParser e non ha effetti collaterali di caricamento delle risorse:

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

Note:
1. Array.fromè il metodo ES2015, può essere sostituito con [].slice.call(doc.body.childNodes).
2. La funzione freccia in somechiamata può essere sostituita con la normale funzione anonima.


3
Questa è un'idea fantastica. Tuttavia, questa funzione non è stata in grado di rilevare il tag di chiusura (cioè isHTML("</a>") --> false).
Lewis

9
Ottima soluzione! .. L'unico effetto collaterale negativo di è che se il tuo html contiene risorse statiche come un attributo src dell'immagine .. innerHTMLcostringerà il browser a iniziare a recuperare quelle risorse. :(
Jose Browne

@JoseBrowne anche se non è aggiunto al DOM?
kuus

1
@kuus Sì, anche se non aggiungendo. Usa la soluzione DOMParser.
dfsq

1
Buona idea, ma la risposta accettata non sarebbe migliore per le prestazioni? Soprattutto se hai stringhe enormi (gioco di parole) o se devi usare molto questo test.
DerpyNerd

13

Un po 'di convalida con:

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

Questo cerca tag vuoti (alcuni predefiniti) e /tag vuoti XHTML terminati e convalida come HTML a causa del tag vuoto OPPURE acquisirà il nome del tag e tenterà di trovare il tag di chiusura da qualche parte nella stringa per convalidare come HTML.

Demo spiegato: http://regex101.com/r/cX0eP2

Aggiornare:

Validazione completa con:

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

Ciò esegue una corretta convalida poiché contiene TUTTO i tag HTML, prima quelli vuoti seguiti dagli altri che richiedono un tag di chiusura.

Demo spiegato qui: http://regex101.com/r/pE1mT5


1
Solo una nota la regex in basso funziona ma non rileverà tag html non chiusi come "'<strong> ciao mondo". ammesso che sia rotto, quindi html dovrebbe essere trattato come una stringa ma per scopi pratici la tua app potrebbe voler rilevare anche questi.
TK123

L'HTML è stato progettato pensando al perdono degli user-agent. I tag "non validi" non sono validi, sono solo sconosciuti e consentiti. Gli attributi "non validi" non sono validi ... Ciò è particolarmente evidente quando si inizia a coinvolgere "componenti web" e tecnologie come JSX, che mescolano HTML e descrizioni di componenti più ricche, generando tipicamente shadow DOM. Slap questo in un file e eval document.querySelector('strange')- funzionerà.
amcgregor,

(Per riassumere: a causa del modo in cui sono scritte le specifiche, il tentativo di "convalidare" il markup HTML è essenzialmente un compito stupido. Il collegamento fornito a un documento HTML di esempio con un elemento "non valido", lì, è completamente formato al 100%, documento HTML completo —ed è stato dal 1997 — come altro esempio.)
amcgregor,

9

La risposta di zzzzBov sopra è buona, ma non tiene conto dei tag di chiusura vaganti, come ad esempio:

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

Una versione che cattura anche i tag di chiusura potrebbe essere questa:

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

Sarebbe stato meglio suggerire una modifica, invece di pubblicarla come commento.
Zlatin Zlatev

Penso che tu voglia dire <[a-z/][\s\S]*>- nota il taglio nel primo gruppo.
Ryan Guill

7

Ecco una battuta sciatta che uso di tanto in tanto:

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

Fondamentalmente restituirà trueper le stringhe contenenti un <seguito da ANYTHINGseguito da >.

Con ANYTHING, intendo praticamente qualsiasi cosa tranne una stringa vuota.

Non è eccezionale, ma è una battuta.

Utilizzo

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

Come puoi vedere è tutt'altro che perfetto, ma in alcuni casi potrebbe fare il lavoro per te.


1
proprio quello di cui avevo bisogno. Niente di speciale, solo pulito. Grazie!
moeiscool

6

Tutte le risposte qui sono troppo inclusive, cercano solo <seguite da> . Non esiste un modo perfetto per rilevare se una stringa è HTML, ma puoi fare di meglio.

Di seguito cerchiamo le etichette finali e saranno molto più strette e accurate:

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

Ed eccolo in azione:

# 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

Se stai creando una regex da una stringa letterale, devi eseguire l'escape di eventuali backslash:

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

Questo non è necessario se usi un'espressione regolare letterale, ma poi devi sfuggire alle barre in avanti:

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

Inoltre, il tuo jsfiddle non ha funzionato perché hai assegnato un onloadgestore all'interno di un altro onloadgestore: l'impostazione predefinita nel pannello Frameworks & Extensions a sinistra è avvolgere il JS in un file onload. Cambialo in un'opzione nowrap e correggi l'escape letterale della stringa e "funziona" (entro i limiti indicati da tutti nei commenti): http://jsfiddle.net/wFWtc/4/

Per quanto ne so, le espressioni regolari JavaScript non hanno riferimenti a ritroso. Quindi questa parte della tua espressione:

</\1>

non funzionerà in JS (ma funzionerà in alcune altre lingue).



Bene, questo testerà che uno dei tag sembra OK, ma niente per il resto. Non sono sicuro del tipo di "validità" che l'OP vuole.
nhahtdh

1
che dire di <br> <hr> <input...>@ user1240679?
CSᵠ

3

/<\/?[^>]*>/.test(str) Rileva solo se contiene tag html, può essere un xml


27 is < 42, and 96 > 42. Questo non è HTML.
amcgregor

3

Con jQuery:

function isHTML(str) {
  return /^<.*?>$/.test(str) && !!$(str)[0];
}

2
isHTML("<foo>");// restituisce true isHTML("div");// restituisce true se sono presenti messaggi di posta div
elettronica

@yekta - Di cosa stai parlando? Questo dovrebbe controllare se la stringa è html o meno. Un'email non è un tag html per quanto ne so ... isHTML ('foo@bar.com ') -> false // correct
gtournie

1
Una stringa può essere qualsiasi cosa, se sai che è un tag HTML, perché controllare se è HTML in primo luogo, non seguo il tuo punto. Non @è una sintassi valida per un selettore. Quindi, quando lo passi a un selettore jQuery, genererà un'eccezione (cioè $("you@example.com")da !!$(str)[0]). Mi riferisco specificamente alla !!$(str)[0] porzione. Hai appena modificato la tua risposta, ma ora stai verificando l'HTML prima che jQuery faccia qualcosa.
yekta

Non credo che l'autore volesse controllare se fosse solo una stringa. Questo è il punto. Quello che voleva era una funzione in grado di controllare se la stringa era un tag HTML valido , non solo HTML (altrimenti questo è un po 'stupido). Ho aggiornato la mia risposta dopo aver letto il commento @ACK_stoverflow, ma sono sicuro che dovrebbe farlo una semplice regex.
gtournie

3

Usando jQuery in questo caso, la forma più semplice sarebbe:

if ($(testString).length > 0)

Se $(testString).length = 1, significa che c'è un tag HTML all'interno textStging.


Come per la risposta appena sotto (a partire da "With jQuery", scritta quattro anni prima di questa!), Si consideri la scarsa scelta di molteplici usi da un unico punto di ingresso. $()è un'operazione di selezione CSS. Ma anche una fabbrica di nodi DOM dalla serializzazione HTML testuale. Ma anche ... come per l'altra risposta che soffre della stessa dipendenza da jQuery, "div" non è HTML, ma verrebbe restituito truese <div>nella pagina sono presenti elementi. Questo è un approccio molto, molto pessimo, come ho imparato ad aspettarmi con quasi tutte le soluzioni che coinvolgono inutilmente jQuery. (Lascialo morire.)
amcgregor

1

Esistono soluzioni fantasiose che implicano l'utilizzo del browser stesso per tentare di analizzare il testo, identificando se sono stati costruiti nodi DOM, il che sarà ... lento. O espressioni regolari che saranno più veloci, ma ... potenzialmente imprecise. Ci sono anche due domande molto distinte che sorgono da questo problema:

Q1: una stringa contiene frammenti HTML?

La stringa fa parte di un documento HTML, contenente markup di elementi HTML o entità codificate? Questo può essere utilizzato come indicatore del fatto che la stringa potrebbe richiedere sbiancamento / sanificazione o decodifica di entità:

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

Puoi vedere questo modello in uso rispetto a tutti gli esempi da tutte le risposte esistenti al momento della stesura di questo articolo, oltre ad alcuni ... un testo campione generato da WYSIWYG o Word piuttosto orribile e una varietà di riferimenti a entità carattere.

Q2: la stringa è un documento HTML?

La specifica HTML è incredibilmente allentata su ciò che considera un documento HTML . I browser fanno di tutto per analizzare quasi tutti i testi inutili come HTML. Due approcci: o considera semplicemente tutto HTML (poiché se consegnato con un text/htmlContent-Type, sarà fatto un grande sforzo per cercare di interpretarlo come HTML dallo user-agent) o cerca il marcatore del prefisso:

<!DOCTYPE html>

In termini di "buona formazione", questo e quasi nient'altro è "richiesto". Quello che segue è un documento HTML completo e completamente valido al 100% contenente ogni elemento HTML che pensi venga omesso:

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

Sì. Ci sono delle regole esplicite su come formare gli elementi "mancanti", come <html>, <head>e <body>. Anche se trovo piuttosto divertente che l'evidenziazione della sintassi di SO non sia riuscita a rilevarlo correttamente senza un suggerimento esplicito.


0

La mia soluzione è

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

La tua espressione regolare sembra altamente difettosa rispetto a un'espressione più completa e richiedere la pre-elaborazione (la sostituzione iniziale) è estremamente sfortunato.
amcgregor

-1

C'è un pacchetto NPM is-html che può tentare di risolvere questo https://github.com/sindresorhus/is-html


Non capisco l'espressione che sta tentando di usare che fallisce tranne che sul doctype dichiarato, e il modello "completo" costruito da elementi HTML noti estratti da una dipendenza aggiuntiva ignora il fatto che non è così che funziona l'HTML, e non ha è stato per molto, molto tempo. Inoltre, il modello di base menziona esplicitamente <html>e <body>tag, entrambi completamente opzionali . Il test "non corrisponde a XML" lo sta dicendo.
amcgregor

@amcgregor se pensi che la tua soluzione sia migliore forse contribuisci al repository isHTML? e aggiungi la tua suite di test da regex101? sarebbe prezioso per la comunità
Colin D

Lo scopo fondamentale di quella libreria è fuorviante e sarà intrinsecamente sbagliato in un gran numero di casi, di solito con false segnalazioni come non HTML a causa della presenza di tag che non comprende; la convalida non può avere successo in questo modo. Inoltre, una semplice regex o una (modifica: coppia di ) librar [i] ... potremmo aver dimenticato come programmare , e Node / NPM non è un linguaggio o una toolchain che generalmente desidero utilizzare, contribuire o incoraggiare l'uso di .
amcgregor

Va bene amcgergor, sei piuttosto negativo con me quando stavo solo cercando di aiutarti. Non sono d'accordo con la premessa che npm sia fuorviante. Immagina che la tua risposta di overflow dello stack abbia avuto una piccola modifica in futuro. Io, come sviluppatore che usa la tua libreria, mi limiterò ad aggiornare e otterrei un comportamento più corretto. Invece, devo ... convivere con il comportamento rotto o rivisitare questa risposta di overflow dello stack per ottenere le tue modifiche? Questo è l'universo alternativo
Colin D

Negativo? Stavo spiegando la mia posizione e perché non avrei fatto ciò che altrimenti sembrerebbe una cosa sensata. Nota, tuttavia, che l'articolo che ho collegato è stato il seguito di un primo leggermente più infiammatorio (collegato in primo piano) che ha generato molte discussioni. Ha pubblicato un documento tecnico , anche lì linkato, verso il basso. Contrasto al tuo istinto di rielaborazione con prove sulla qualità. Rif: §7.2 (& the left-pad disaster & eslint)
amcgregor
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.