用JS解析HTML字符串

我想解析一个包含HTML文本的字符串。我想用JavaScript写。

我尝试了纯JavaScript HTML解析器库,但似乎它解析我当前页面的HTML,而不是从字符串。因为当我尝试下面的代码时,它改变了我页面的标题:

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

我的目标是从一个HTML外部页面中提取链接,我读起来就像一个字符串。

你知道一个API来做它吗?

668491 次浏览

创建一个虚拟DOM元素并将字符串添加到其中。然后,您可以像操作任何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

编辑:添加一个jQuery的答案,以取悦粉丝!

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

如果您愿意使用jQuery,它有一些很好的工具可以从HTML字符串创建独立的DOM元素。然后可以通过通常的方法查询这些信息,例如:

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

编辑-刚刚看到@Florian的答案是正确的。这基本上就是他说的,但是用的是jQuery。

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

下面的函数parseHTML将返回:


代码:

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

使用方法:

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

这很简单:

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

根据MDN,要在chrome中这样做,你需要像这样解析XML:

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

webkit目前不支持它,你必须遵循Florian的回答,而且它在大多数情况下在移动浏览器上是否有效还不得而知。

编辑:现在广泛支持

在Chrome和Firefox中解析HTML的最快方法是Range#createContextualFragment:

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

我建议创建一个helper函数,如果可用,使用createContextualFragment,否则返回innerHTML。

基准:http://jsperf.com/domparser-vs-createelement-innerhtml/3

编辑:下面的解决方案仅适用于HTML“片段”;由于html,头部和身体被删除。我想这个问题的解决方案是DOMParser的parseFromString()方法:

const parser = new DOMParser();
const document = parser.parseFromString(html, "text/html");

对于HTML片段,这里列出的解决方案适用于大多数HTML,但在某些情况下它将不起作用。

例如,尝试解析<td>Test</td>。这个不会在div.innerHTML解决方案或DOMParser.prototype.parseFromString或range上工作。createContextualFragment解决方案。td标签丢失,只留下文本。

只有jQuery能很好地处理这种情况。

所以未来的解决方案(MS Edge 13+)是使用模板标签:

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


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

对于较旧的浏览器,我已经将jQuery的parseHTML()方法提取为一个独立的gist - https://gist.github.com/Munawwar/6e6362dbdf77c7865a99

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>`) );
人力资源> < p > < 只有父类Node (Range的开头)中有效的子类Node才会被解析。否则,可能出现不可预期的结果:

// <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>');
let content = "<center><h1>404 Not Found</h1></center>"
let result = $("<div/>").html(content).text()
< p >内容:<center><h1>404 Not Found</h1></center>, < br > 结果:"404 Not Found" < / p >

1的方式

使用document.cloneNode()

性能:

调用document.cloneNode()花费了大约0.22499999977299012毫秒。

也许还会更多。

var t0, t1, html;


t0 = performance.now();
html = document.cloneNode(true);
t1 = performance.now();


console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.")


html.documentElement.innerHTML = '<!DOCTYPE html><html><head><title>Test</title></head><body><div id="test1">test1</div></body></html>';


console.log(html.getElementById("test1"));

2方法

使用document.implementation.createHTMLDocument()

性能:

调用document.implementation.createHTMLDocument()花费了大约0.14000000010128133毫秒。

var t0, t1, html;


t0 = performance.now();
html = document.implementation.createHTMLDocument("test");
t1 = performance.now();


console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.")


html.documentElement.innerHTML = '<!DOCTYPE html><html><head><title>Test</title></head><body><div id="test1">test1</div></body></html>';


console.log(html.getElementById("test1"));

3路

使用document.implementation.createDocument()

性能:

调用document.implementation.createHTMLDocument()花费了大约0.14000000010128133毫秒。

var t0 = performance.now();
html = document.implementation.createDocument('', 'html',
document.implementation.createDocumentType('html', '', '')
);
var t1 = performance.now();


console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.")


html.documentElement.innerHTML = '<html><head><title>Test</title></head><body><div id="test1">test</div></body></html>';


console.log(html.getElementById("test1"));

4路

使用new Document()

性能:

调用document.implementation.createHTMLDocument()花费了大约0.13499999840860255毫秒。

  • 请注意

ParentNode.append是2020年的实验技术。

var t0, t1, html;


t0 = performance.now();
//---------------
html = new Document();


html.append(
html.implementation.createDocumentType('html', '', '')
);
    

html.append(
html.createElement('html')
);
//---------------
t1 = performance.now();


console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.")


html.documentElement.innerHTML = '<html><head><title>Test</title></head><body><div id="test1">test1</div></body></html>';


console.log(html.getElementById("test1"));

我不得不使用Angular NGX Bootstrap弹出窗口中解析的元素的innerHTML。这是对我有效的解决办法。

public htmlContainer = document.createElement( 'html' );

在构造函数

this.htmlContainer.innerHTML = ''; setTimeout(() => { this.convertToArray(); });

 convertToArray() {
const shapesHC = document.getElementsByClassName('weekPopUpDummy');
const shapesArrHCSpread = [...(shapesHC as any)];
this.htmlContainer = shapesArrHCSpread[0];
this.htmlContainer.innerHTML = shapesArrHCSpread[0].textContent;
}

在html中

<div class="weekPopUpDummy" [popover]="htmlContainer.innerHTML" [adaptivePosition]="false" placement="top" [outsideClick]="true" #popOverHide="bs-popover" [delay]="150" (onHidden)="onHidden(weekEvent)" (onShown)="onShown()">

要在node.js中做到这一点,你可以使用像node-html-parser这样的HTML解析器。语法如下所示:

import { parse } from 'node-html-parser';


const root = parse('<ul id="list"><li>Hello World</li></ul>');


console.log(root.firstChild.structure);
// ul#list
//   li
//     #text


console.log(root.querySelector('#list'));
// { tagName: 'ul',
//   rawAttrs: 'id="list"',
//   childNodes:
//    [ { tagName: 'li',
//        rawAttrs: '',
//        childNodes: [Object],
//        classNames: [] } ],
//   id: 'list',
//   classNames: [] }
console.log(root.toString());
// <ul id="list"><li>Hello World</li></ul>
root.set_content('<li>Hello World</li>');
root.toString();    // <li>Hello World</li>

我认为最好的方法是像这样使用 API:

//Table string in HTML format
const htmlString = '<table><tbody><tr><td>Cell 1</td><td>Cell 2</td></tr></tbody></table>';


//Parse using DOMParser native way
const parser = new DOMParser();
const $newTable = parser.parseFromString(htmlString, 'text/html');


//Here you can select parts of your parsed html and work with it
const $row = $newTable.querySelector('table > tbody > tr');


//Here i'm printing the number of columns (2)
const $containerHtml = document.getElementById('containerHtml');
$containerHtml.innerHTML = ['Your parsed table have ', $row.cells.length, 'columns.'].join(' ');
<div id="containerHtml"></div>

function parseElement(raw){
let el = document.createElement('div');
el.innerHTML = raw;
let res = el.querySelector('*');
res.remove();
return res;
}

注意:原始字符串不应该多于1个元素

const html =
`<script>
alert('👋 there ! Wanna grab a 🍺');
</script>`;


const scriptEl = document.createRange().createContextualFragment(html);
parent.append(scriptEl);

我发现了这个解决方案,我认为这是最好的解决方案,它解析HTML和执行里面的脚本。