Analizzare una stringa HTML con JS


259

Ho cercato una soluzione ma nulla era rilevante, quindi ecco il mio problema:

Voglio analizzare una stringa che contiene testo HTML. Voglio farlo in JavaScript.

Ho provato questa libreria ma sembra che analizzi l'HTML della mia pagina corrente, non da una stringa. Perché quando provo il codice qui sotto, cambia il titolo della mia pagina:

var parser = new HTMLtoDOM("<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>", document);

Il mio obiettivo è quello di estrarre i collegamenti da una pagina esterna HTML che ho letto proprio come una stringa.

Conosci un'API per farlo?


1
possibile duplicato dell'accesso DOMParser JavaScript innerHTML e altre proprietà
Rob W

1
Il metodo sul duplicato collegato crea un documento HTML da una determinata stringa. Quindi, è possibile utilizzare doc.getElementsByTagName('a')per leggere i collegamenti (o anche doc.links).
Rob W,

Vale la pena ricordare che se si utilizza un framework come React.js, potrebbero esserci dei modi specifici per farlo, ad esempio: stackoverflow.com/questions/23616226/…
Mike Lyons

Questo risponde alla tua domanda? Rimuovi HTML dal testo JavaScript
Leif Arne Storset l'

Risposte:


373

Crea un finto elemento DOM e aggiungi la stringa ad esso. Quindi, puoi manipolarlo come qualsiasi elemento DOM.

var el = document.createElement( 'html' );
el.innerHTML = "<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>";

el.getElementsByTagName( 'a' ); // Live NodeList of your anchor elements

Modifica: aggiungendo una risposta jQuery per soddisfare i fan!

var el = $( '<div></div>' );
el.html("<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>");

$('a', el) // All the anchor elements

9
Solo una nota: con questa soluzione, se faccio un "alert (el.innerHTML)", perdo il tag <html>, <body> e <head> ....
stage

2
Problema: devo ottenere collegamenti dal tag <frame>. Ma con questa soluzione, i tag frame vengono eliminati ...
fase

3
@stage Sono un po 'in ritardo alla festa, ma dovresti essere in grado di utilizzare document.createElement('html');per conservare i tag <head>e <body>.
omninonsense,

3
sembra che tu stia inserendo un elemento html in un elemento html
symbiont

6
Sono preoccupato è votato come la risposta migliore. La parse()soluzione seguente è più riutilizzabile ed elegante.
Giustino

233

È abbastanza semplice:

var parser = new DOMParser();
var htmlDoc = parser.parseFromString(txt, 'text/html');
// do whatever you want with htmlDoc.getElementsByTagName('a');

Secondo MDN , per farlo in Chrome è necessario analizzare XML in questo modo:

var parser = new DOMParser();
var htmlDoc = parser.parseFromString(txt, 'text/xml');
// do whatever you want with htmlDoc.getElementsByTagName('a');

Al momento non è supportato da webkit e dovresti seguire la risposta di Florian, ed è sconosciuto funzionare nella maggior parte dei casi sui browser mobili.

Modifica: ora ampiamente supportato


35
Vale la pena notare che nel 2016 DOMParser è ora ampiamente supportato. caniuse.com/#feat=xml-serializer
aendrew

5
Vale la pena notare che tutti i collegamenti relativi nel documento creato sono interrotti, perché il documento viene creato ereditando il documentURLdi window, che molto probabilmente differisce dall'URL della stringa.
ceving il

2
Vale la pena notare che dovresti chiamare una solanew DOMParser volta e poi riutilizzare lo stesso oggetto per tutto il resto dello script.
Jack Giffin,

1
La parse()soluzione di seguito è più riutilizzabile e specifica per HTML. Questo è bello se hai bisogno di un documento XML, tuttavia.
Giustino

Come posso visualizzare questa pagina Web analizzata in una finestra di dialogo o qualcosa del genere? Non sono riuscito a trovare una soluzione per questo
Shariq Musharaf,

18

EDIT: la soluzione di seguito è solo per i "frammenti" HTML poiché html, head e body sono stati rimossi. Immagino che la soluzione a questa domanda sia il metodo parseFromString () di DOMParser.


Per i frammenti HTML, le soluzioni elencate qui funzionano per la maggior parte di HTML, tuttavia per alcuni casi non funzioneranno.

Ad esempio, prova ad analizzare <td>Test</td> . Questo non funzionerà né sulla soluzione div.innerHTML né su DOMParser.prototype.parseFromString né range.createContextualFragment. Il tag td scompare e rimane solo il testo.

Solo jQuery gestisce bene quel caso.

Quindi la soluzione futura (MS Edge 13+) è usare il tag template:

function parseHTML(html) {
    var t = document.createElement('template');
    t.innerHTML = html;
    return t.content.cloneNode(true);
}

var documentFragment = parseHTML('<td>Test</td>');

Per i browser più vecchi ho estratto il metodo parseHTML () di jQuery in un modo indipendente - https://gist.github.com/Munawwar/6e6362dbdf77c7865a99


Se si desidera scrivere un codice compatibile con il forward che funziona anche con i vecchi browser, è possibile eseguire il polyfill del <template>tag . Dipende da elementi personalizzati che potrebbero essere necessari anche per il polyfill . In effetti, potresti semplicemente voler utilizzare webcomponents.js per eseguire il polyfill di elementi personalizzati, modelli, shadow dom, promesse e poche altre cose tutto in una volta.
Jeff Laughlin,

12
var doc = new DOMParser().parseFromString(html, "text/html");
var links = doc.querySelectorAll("a");

4
Perché stai prefissando $? Inoltre, come menzionato nel duplicato collegato , text/htmlnon è supportato molto bene e deve essere implementato usando un polyfill.
Rob W,

1
Ho copiato questa riga da un progetto, sono abituato a aggiungere il prefisso alle variabili con $ nell'applicazione javascript (non in libreria). è solo per avere un conflitto con una biblioteca. questo non è molto utile poiché quasi ogni variabile ha un ambito ma era utile. aiuta (forse) a identificare facilmente le variabili.
Mathieu,

1
Purtroppo DOMParsernon funziona su text/htmlChrome, questa pagina MDN offre una soluzione alternativa.
Jokester,

Nota di sicurezza: questo verrà eseguito senza alcun contesto del browser, quindi non verranno eseguiti script. Dovrebbe essere adatto per input non attendibili.
Leif Arne Storset,

6

Il modo più veloce per analizzare HTML in Chrome e Firefox è Range # createContextualFragment:

var range = document.createRange();
range.selectNode(document.body); // required in Safari
var fragment = range.createContextualFragment('<h1>html...</h1>');
var firstNode = fragment.firstChild;

Vorrei raccomandare di creare una funzione di supporto che utilizza createContextualFragment se disponibile e altrimenti ricade in innerHTML.

Indice di riferimento: http://jsperf.com/domparser-vs-createelement-innerhtml/3


Nota che, come (il semplice) innerHTML, questo eseguirà un <img>'s onerror.
Ry-

Un problema con questo è che html come '<td> test </td>' ignorerebbe il td nel contesto document.body (e creerebbe solo il nodo di testo 'test') .OTOH, se usato internamente in un motore di template allora sarebbe disponibile il giusto contesto.
Munawwar,

Inoltre, IE 11 supporta createContextualFragment.
Munawwar,

La domanda era come analizzare JS - non Chrome o Firefox
sea26.2,

Nota di sicurezza: questo eseguirà qualsiasi script nell'input e quindi non è adatto per input non attendibili.
Leif Arne Storset,

6

parseHTMLVerrà restituita la seguente funzione :

  • a Documentquando il file inizia con un tipo di documento.

  • a DocumentFragmentquando il file non inizia con un tipo di documento.


Il codice :

function parseHTML(markup) {
    if (markup.toLowerCase().trim().indexOf('<!doctype') === 0) {
        var doc = document.implementation.createHTMLDocument("");
        doc.documentElement.innerHTML = markup;
        return doc;
    } else if ('content' in document.createElement('template')) {
       // Template tag exists!
       var el = document.createElement('template');
       el.innerHTML = markup;
       return el.content;
    } else {
       // Template tag doesn't exist!
       var docfrag = document.createDocumentFragment();
       var el = document.createElement('body');
       el.innerHTML = markup;
       for (i = 0; 0 < el.childNodes.length;) {
           docfrag.appendChild(el.childNodes[i]);
       }
       return docfrag;
    }
}

Come usare :

var links = parseHTML('<!doctype html><html><head></head><body><a>Link 1</a><a>Link 2</a></body></html>').getElementsByTagName('a');

Non sono riuscito a farlo funzionare su IE8. Viene visualizzato l'errore "L'oggetto non supporta questa proprietà o metodo" per la prima riga della funzione. Non credo che esista la funzione createHTMLDocument
Sebastian Carroll,

Qual è esattamente il tuo caso d'uso? Se vuoi solo analizzare HTML e il tuo HTML è destinato al corpo del tuo documento, puoi fare quanto segue: (1) var div = document.createElement ("DIV"); (2) div.innerHTML = markup; (3) risultato = div.childNodes; --- Questo ti dà una collezione di nodi e dovrebbe funzionare non solo in IE8 ma anche in IE6-7.
John Slegers,

Grazie per l'opzione alternativa, ci proverò se devo farlo di nuovo. Per ora però ho usato la soluzione JQuery sopra.
Sebastian Carroll,

@SebastianCarroll Nota che IE8 non supporta il trimmetodo sulle stringhe. Vedi stackoverflow.com/q/2308134/3210837 .
Spazzolino da denti

2
@Toothbrush: il supporto IE8 è ancora rilevante agli albori del 2017?
John Slegers,

4

Se sei aperto all'utilizzo di jQuery, ha alcune belle strutture per la creazione di elementi DOM distinti da stringhe di HTML. Questi possono quindi essere interrogati con i soliti mezzi, ad esempio:

var html = "<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>";
var anchors = $('<div/>').append(html).find('a').get();

Modifica - ho appena visto la risposta di @ Florian che è corretta. Questo è fondamentalmente esattamente quello che ha detto, ma con jQuery.


4
const parse = Range.prototype.createContextualFragment.bind(document.createRange());

document.body.appendChild( parse('<p><strong>Today is:</strong></p>') ),
document.body.appendChild( parse(`<p style="background: #eee">${new Date()}</p>`) );


Verranno analizzati solo i figli validi Nodeall'interno del genitore Node(inizio del Range). In caso contrario, potrebbero verificarsi risultati imprevisti:

// <body> is "parent" Node, start of Range
const parseRange = document.createRange();
const parse = Range.prototype.createContextualFragment.bind(parseRange);

// Returns Text "1 2" because td, tr, tbody are not valid children of <body>
parse('<td>1</td> <td>2</td>');
parse('<tr><td>1</td> <td>2</td></tr>');
parse('<tbody><tr><td>1</td> <td>2</td></tr></tbody>');

// Returns <table>, which is a valid child of <body>
parse('<table> <td>1</td> <td>2</td> </table>');
parse('<table> <tr> <td>1</td> <td>2</td> </tr> </table>');
parse('<table> <tbody> <td>1</td> <td>2</td> </tbody> </table>');

// <tr> is parent Node, start of Range
parseRange.setStart(document.createElement('tr'), 0);

// Returns [<td>, <td>] element array
parse('<td>1</td> <td>2</td>');
parse('<tr> <td>1</td> <td>2</td> </tr>');
parse('<tbody> <td>1</td> <td>2</td> </tbody>');
parse('<table> <td>1</td> <td>2</td> </table>');

Nota di sicurezza: questo eseguirà qualsiasi script nell'input e quindi non è adatto per input non attendibili.
Leif Arne Storset,

0

con questo semplice codice puoi farlo:

let el = $('<div></div>');
$(document.body).append(el);
el.html(`<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>`);
console.log(el.find('a[href="test0"]'));
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.