แยกสตริง HTML ด้วย JS


258

ฉันค้นหาวิธีแก้ปัญหา แต่ไม่มีอะไรเกี่ยวข้องดังนั้นนี่คือปัญหาของฉัน:

ฉันต้องการแยกสตริงที่มีข้อความ HTML ฉันต้องการที่จะทำใน JavaScript

ฉันลองใช้ห้องสมุดนี้แต่ดูเหมือนว่ามันจะแยกวิเคราะห์ 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 ที่จะทำหรือไม่?



1
วิธีการที่ซ้ำกันที่เชื่อมโยงสร้างเอกสาร HTML จากสตริงที่กำหนด จากนั้นคุณสามารถใช้doc.getElementsByTagName('a')เพื่ออ่านลิงก์ (หรือแม้กระทั่งdoc.links)
Rob W

เป็นมูลค่าการกล่าวขวัญว่าถ้าคุณใช้เฟรมเวิร์กเช่น React.js อาจมีวิธีการที่เฉพาะเจาะจงกับเฟรมเวิร์กเช่น: stackoverflow.com/questions/23616226/
Mike Lyons

นี่ตอบคำถามของคุณหรือไม่? Strip HTML จากข้อความ JavaScript
Leif Arne Storset

คำตอบ:


373

สร้างองค์ประกอบ 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

9
เพียงแค่ทราบ: ด้วยวิธีนี้ถ้าฉันทำ "การแจ้งเตือน (el.innerHTML)" ฉันสูญเสียแท็ก <html>, <body> และ <head> ....
เวที

2
ปัญหา: ฉันต้องได้รับลิงก์จากแท็ก <frame> แต่ด้วยวิธีนี้แท็กเฟรมจะถูกลบ ...
เวที

3
@ เวทีฉันมีความล่าช้าเล็กน้อยในงานปาร์ตี้ แต่คุณควรจะสามารถใช้document.createElement('html');เพื่อรักษาแท็ก<head>และ <body>
omninonsense

3
ดูเหมือนว่าคุณกำลังวางองค์ประกอบ html ไว้ในองค์ประกอบ html
symbiont

6
ฉันกังวลถูกโหวตว่าเป็นคำตอบอันดับต้น ๆ parse()วิธีการแก้ปัญหาด้านล่างนี้สามารถใช้ซ้ำได้และสวยงามยิ่งขึ้น
Justin

233

มันค่อนข้างง่าย:

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

ตาม MDNการทำเช่นนี้ใน chrome คุณต้องแยกวิเคราะห์เป็น XML ดังนี้:

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

ขณะนี้ webkit ไม่ได้รับการสนับสนุนและคุณต้องปฏิบัติตามคำตอบของ Florian และไม่ทราบว่าจะทำงานในกรณีส่วนใหญ่บนเบราว์เซอร์มือถือ

แก้ไข: ตอนนี้รองรับอย่างกว้างขวาง


35
น่าสังเกตว่าในปี 2559 DOMParser ได้รับการสนับสนุนอย่างกว้างขวางแล้ว caniuse.com/#feat=xml-serializer
aendrew

5
น่าสังเกตว่าลิงก์ที่เกี่ยวข้องทั้งหมดในเอกสารที่สร้างจะใช้งานไม่ได้เนื่องจากเอกสารได้รับการสร้างโดยสืบทอดdocumentURLจากwindowซึ่งมักจะแตกต่างจาก URL ของสตริง
ceving

2
น่าสังเกตว่าคุณควรโทรเพียงnew DOMParserครั้งเดียวจากนั้นนำวัตถุนั้นกลับมาใช้ซ้ำตลอดช่วงที่เหลือของสคริปต์
Jack Giffin

1
parse()แก้ปัญหาด้านล่างนำมาใช้ใหม่มากขึ้นและที่เฉพาะเจาะจงเพื่อ HTML นี่เป็นสิ่งที่ดีถ้าคุณต้องการเอกสาร XML อย่างไรก็ตาม
Justin

ฉันจะแสดงหน้าเว็บแยกวิเคราะห์นี้ในกล่องโต้ตอบหรืออะไร? ฉันไม่สามารถหาคำตอบสำหรับเรื่องนี้ได้
Shariq Musharaf

18

แก้ไข: การแก้ปัญหาด้านล่างสำหรับ HTML "แฟรกเมนต์" เท่านั้นเนื่องจาก HTML, ส่วนหัวและเนื้อหาจะถูกลบออก ฉันเดาทางออกสำหรับคำถามนี้คือวิธี parseFromString () DOMParser


สำหรับชิ้นส่วน HTML โซลูชั่นที่แสดงในที่นี้ใช้ได้กับ HTML ส่วนใหญ่อย่างไรก็ตามในบางกรณีจะไม่สามารถใช้งานได้

<td>Test</td>ตัวอย่างเช่นลองแยก อันนี้จะไม่ทำงานในโซลูชั่น div.innerHTML หรือ DOMParser.prototype.parseFromString และ range.createContextualFragment solution แท็ก td หายไปและเหลือเพียงข้อความเท่านั้น

jQuery เท่านั้นที่จัดการกับเคสนั้นได้ดี

ดังนั้นวิธีแก้ปัญหาในอนาคต (MS Edge 13+) คือการใช้เทมเพลตแท็ก:

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

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

สำหรับเบราว์เซอร์รุ่นเก่าฉันได้แยกเมธอด parseHTML () ของ jQuery ลงใน gist อิสระ - https://gist.github.com/Munawwar/6e6362dbdf77c7865a99


หากคุณต้องการที่จะเขียนรหัสไปข้างหน้าได้ว่ายังทำงานบนเบราว์เซอร์เก่าคุณสามารถPolyfill<template>แท็ก มันขึ้นอยู่กับองค์ประกอบที่กำหนดเองที่คุณอาจต้องpolyfill อันที่จริงแล้วคุณอาจต้องการใช้webcomponents.jsกับองค์ประกอบที่กำหนดเอง, แม่แบบ, เงาของ dom, สัญญาและสิ่งอื่น ๆ อีกมากมายในคราวเดียว
Jeff Laughlin

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

4
ทำไมคุณ prefixing $? นอกจากนี้ยังเป็นที่กล่าวถึงในที่ซ้ำกันเชื่อมโยง , text/htmlไม่ได้รับการสนับสนุนเป็นอย่างดีและจะต้องมีการดำเนินการโดยใช้ polyfill
Rob W

1
ฉันคัดลอกบรรทัดนี้จากโครงการฉันใช้เพื่อนำหน้าตัวแปรด้วย $ ในแอปพลิเคชัน javascript (ไม่ใช่ในห้องสมุด) มันเป็นเพียงเพื่อหลีกเลี่ยงการขัดแย้งกับห้องสมุด มันไม่ค่อยมีประโยชน์เท่าไหร่เพราะตัวแปรเกือบทุกตัวจะถูกกำหนดขอบเขต แต่มันก็เคยเป็นแบบที่มีประโยชน์ มันยัง (อาจ) ช่วยระบุตัวแปรได้อย่างง่ายดาย
Mathieu

1
น่าเศร้าที่DOMParserไม่สามารถใช้งานกับtext/htmlChrome ได้หน้า MDN นี้ให้วิธีแก้ปัญหา
Jokester

หมายเหตุด้านความปลอดภัย: สิ่งนี้จะทำงานโดยไม่มีบริบทเบราว์เซอร์ดังนั้นจึงไม่มีสคริปต์ใดทำงาน ควรเหมาะสำหรับอินพุตที่ไม่น่าเชื่อถือ
Leif Arne Storset

6

วิธีที่เร็วที่สุดในการวิเคราะห์ HTML ใน Chrome และ 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;

ฉันอยากจะแนะนำเพื่อสร้างฟังก์ชั่นผู้ช่วยที่ใช้ createContextualFragment ถ้ามีและกลับไปที่ innerHTML เป็นอย่างอื่น

เกณฑ์มาตรฐาน: http://jsperf.com/domparser-vs-createelement-innerhtml/3


โปรดทราบว่าเช่น (ง่ายๆ) innerHTMLนี้จะดำเนินการ's<img> onerror
Ry-

ปัญหาเกี่ยวกับสิ่งนี้คือ html เช่น '<td> test </td>' จะไม่สนใจ td ในบริบท document.body (และสร้างโหนดข้อความ 'ทดสอบ' เท่านั้น) ถ้าใช้ภายในเครื่องมือสร้าง Templating บริบทที่เหมาะสมจะพร้อมใช้งาน
Munawwar

ด้วย BTW, IE 11 รองรับ createContextualFragment
Munawwar

คำถามคือวิธีแยกวิเคราะห์ JS - ไม่ใช่ Chrome หรือ Firefox
ทะเล 26.2

หมายเหตุด้านความปลอดภัย: สิ่งนี้จะเรียกใช้สคริปต์ใด ๆ ในอินพุตและดังนั้นจึงไม่เหมาะสมสำหรับอินพุตที่ไม่น่าเชื่อถือ
Leif Arne Storset

6

ฟังก์ชั่นต่อไปนี้parseHTMLจะคืนค่าอย่างใดอย่างหนึ่ง:

  • Documentเมื่อไฟล์ของคุณเริ่มต้นด้วยประเภทเอกสาร

  • DocumentFragmentเมื่อแฟ้มของคุณไม่ได้เริ่มต้นด้วยประเภทเอกสาร


รหัส :

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

ฉันไม่สามารถใช้มันกับ IE8 ได้ ฉันได้รับข้อผิดพลาด "วัตถุไม่สนับสนุนคุณสมบัติหรือวิธีการนี้" สำหรับบรรทัดแรกในฟังก์ชั่น ฉันไม่คิดว่าฟังก์ชั่น createHTMLDocument นั้นมีอยู่จริง
Sebastian Carroll

กรณีการใช้งานของคุณคืออะไร? หากคุณต้องการแยก HTML และ HTML ของคุณมีไว้สำหรับเนื้อความของเอกสารของคุณคุณสามารถทำสิ่งต่อไปนี้: (1) var div = document.createElement ("DIV"); (2) div.innerHTML = มาร์กอัป (3) result = div.childNodes; --- สิ่งนี้ให้คอลเลกชันของโหนดย่อยของคุณและควรจะทำงานได้ไม่เฉพาะใน IE8 แต่ยังอยู่ใน IE6-7
John Slegers

ขอบคุณสำหรับตัวเลือกอื่นฉันจะลองถ้าฉันต้องทำอีกครั้ง ในตอนนี้แม้ว่าฉันจะใช้โซลูชัน JQuery ด้านบน
เซบาสเตียนคาร์โรล

@SebastianCarroll โปรดทราบว่า IE8 ไม่รองรับtrimวิธีการกับสตริง ดูstackoverflow.com/q/2308134/3210837
แปรงสีฟัน

2
@ แปรงสีฟัน: IE8 รองรับยังมีความเกี่ยวข้องในตอนเช้าของปี 2017 หรือไม่?
John Slegers

4

หากคุณเปิดให้ใช้ jQuery มีสิ่งอำนวยความสะดวกที่ดีสำหรับการสร้างองค์ประกอบ DOM ที่แยกออกมาจากสตริงของ HTML สามารถสอบถามสิ่งเหล่านี้ได้ด้วยวิธีปกติเช่น:

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


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


เฉพาะเด็กที่ถูกต้องNodeภายในผู้ปกครองNode(เริ่มต้นของRange) จะถูกแยกวิเคราะห์ มิฉะนั้นผลลัพธ์ที่ไม่คาดคิดอาจเกิดขึ้น:

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

หมายเหตุด้านความปลอดภัย: สิ่งนี้จะเรียกใช้สคริปต์ใด ๆ ในอินพุตและดังนั้นจึงไม่เหมาะสมสำหรับอินพุตที่ไม่น่าเชื่อถือ
Leif Arne Storset

0

ด้วยรหัสง่ายๆนี้คุณสามารถทำได้:

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"]'));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.