在网络工作者内部,我有一个 html 字符串,例如:
"<div id='foo'> <img src='bar'></img> <ul id='baz'></ul> </div>"
有没有我可以导入的库来轻松访问不同标签的 id 和 src 属性?正则表达式是工作人员内部的唯一方法吗?
有两种方法可以有效解决这个问题:
由于存在误报的风险,您可以使用以下内容:
var pattern = /<img [^>]*?src=(["'])((?:[^"']+|(?!\1)["'])*)(\1)/i;
var match = string.match(pattern);
var src = match ? match[2] : '';
如果获得正确的 HTML 是一个关键要求,只需让浏览器通过将字符串传递给调用者来解析 HTML。这是一个完整的例子:
来电者:
var worker = new Worker('worker.js');
worker.addEventListener('message', function(e) {
if (!e.data) return;
if (e.data.method === 'getsrc') {
// Unlike document.createElement, etc, the following method does not
// load the image when the HTML is parsed
var doc = document.implementation.createHTMLDocument('');
doc.body.innerHTML = e.data.data;
var images = doc.getElementsByTagName('img');
var result = [];
for (var i=0; i<images.length; i++) {
result.push(images[i].getAttribute('src'));
}
worker.postMessage({
messageID: e.data.messageID,
result: result
});
} else if (e.data.method === 'debug') {
console.log(e.data.data);
}
});
worker.js
// A simple generic messaging API
var callbacks = {};
var lastMessageID = 0;
addEventListener('message', function(e) {
if (callbacks[e.data.messageID]) {
callbacks[e.data.messageID](e.data.result);
}
});
function sendRequest(method, data, callback) {
var messageID = ++lastMessageID;
if (callback) callbacks[messageID] = callback;
postMessage({
method: method,
data: data,
messageID: messageID
});
}
// Example:
sendRequest('getsrc',
'<img src="foo.png">' +
"<img src='bar.png'>" +
'<textarea><img src="should.not.be.visible"></textarea>',
function(result) {
sendRequest('debug', 'Received: ' + result.join(', '));
}
);
2023 年,差不多 11 年后,还有更多选择。
htmlparse2
通常就足够了:
// Demo using htmlparser2 in a web worker
const WORKER_URL = URL.createObjectURL(
new Blob(
[
`
import { Parser } from 'https://esm.sh/[email protected]';
self.addEventListener('message', (event) => {
const ids = [];
const srcs = [];
const parser = new Parser({
onopentag(name, attributes) {
if ('id' in attributes) {
ids.push({ value: attributes.id, tag: name });
}
if ('src' in attributes) {
srcs.push({ value: attributes.src, tag: name });
}
},
});
parser.write(event.data);
parser.end();
self.postMessage({ ids, srcs });
});
`,
],
{ type: 'text/javascript' }
)
);
type AttributeMatch = {
value: string;
tag: string;
};
type FromWorker = {
ids: AttributeMatch[];
srcs: AttributeMatch[];
};
const worker = new Worker(WORKER_URL, { type: 'module' });
worker.addEventListener('message', (e) => {
const { ids, srcs } = e.data as FromWorker;
for (const id of ids) {
console.log(`id="${id.value}" on <${id.tag}>`);
}
for (const src of srcs) {
console.log(`src="${src.value}" on <${src.tag}>`);
}
});
const input =
'<div id="foo"> <img src="bar"></img> <ul id="baz"></ul> </div>';
worker.postMessage(input);
/* console output:
id="foo" on <DIV>
id="baz" on <UL>
src="bar" on <IMG>
*/
htmlparser2
是一个基于事件的解析器,实现 ontagname
事件处理程序 足以满足原始用例。
LinkeDOM (GitHub) 是一个更丰富的替代方案,非常有用,因为它提供了很多熟悉的 DOM API 函数,包括
document.createNodeIterator()
和 document.createTreeWalker()
:
// Demo using linkedom/worker in a web worker
import { parseHTML, NodeFilter } from 'https://esm.sh/[email protected]/worker';
self.addEventListener('message', (event) => {
const ids = [];
const srcs = [];
const { document } = parseHTML(event.data);
const i = document.createNodeIterator(document, NodeFilter.SHOW_ELEMENT);
for (let element = i.nextNode(); element !== null; element = i.nextNode()) {
const id = element.getAttribute('id');
if (id) ids.push({ value: id, tag: element.tagName });
const src = element.getAttribute('src');
if (src) srcs.push({ value: src, tag: element.tagName });
}
self.postMessage({ ids, srcs });
});
请注意,在
linkedom/worker
中,HTMLCanvasElement
本质上是一个存根。