113

I'm trying to fetch a file and return its HTML. However it's not as simple as I'd have imagined.

    fetch('/path/to/file')
    .then(function (response) {
      return response.body;
    })
    .then(function (body) {
      console.log(body);
    });

This returns an object called ReadableByteStream. How do I use this to grab the HTML file content?

If I change the contents of /path/to/file to be a JSON string, and change the above to:

    fetch('/path/to/file')
    .then(function (response) {
      return response.json();
    })
    .then(function (json) {
      console.log(json);
    });

... it returns the JSON correctly. How do I do fetch HTML?

danronmoon
  • 3,814
  • 5
  • 34
  • 56
ditto
  • 5,917
  • 10
  • 51
  • 88
  • 4
    this raises concerns: what do you intend to do with that HTML? because I hope it's not "inject it into my active document". Instead ask for data, in JSON form, and then build DOM around that, clientside, so that you *know* the user isn't loading potentially hacked and super unsafe blind HTML. – Mike 'Pomax' Kamermans Apr 14 '16 at 19:33
  • 8
    @Mike'Pomax'Kamermans You are assuming the HTML was coming from a user. I return server side generated HTML to take advantage of strongly typed views created with a strongly typed server language. – Louise Eggleton Jul 09 '19 at 17:41
  • 1
    And you trust your server? I certainly don't trust mine, I don't even own the hardware it runs on. Instead of asking for HTML, I can wholeheartedly recommend using [morphdom](https://github.com/patrick-steele-idem/morphdom) instead: have your server generate the HTML but then generate a _diff_ that you send to your client, and have them apply that diff to the active DOM with morphdom again. Smaller payloads, so things are more responsive, and safer, too. – Mike 'Pomax' Kamermans Jul 09 '19 at 17:46
  • 1
    @Mike'Pomax'Kamermans Since JS is runnable in the console, can't the user inject just about anything in the active document anyway? – Denis G. Labrecque Jul 21 '20 at 02:27
  • Not with sane CSP turned on, no. They can _add it_ but the browser will refuse to run it because it violates the server's `script-src` CSP rules. – Mike 'Pomax' Kamermans Jul 21 '20 at 02:45
  • This does not make sense, you are generating the main webpage from the same server, and then requesting it another from the same server: of course you have to trust your server! Else you would not trust the original page neither! – algo Jan 22 '23 at 18:50

7 Answers7

161

You can download the html with fetch and then parse it with DomParser API.

fetch('somePage.html')
    .then(function(response) {
        // When the page is loaded convert it to text
        return response.text()
    })
    .then(function(html) {
        // Initialize the DOM parser
        var parser = new DOMParser();

        // Parse the text
        var doc = parser.parseFromString(html, "text/html");

        // You can now even select part of that html as you would in the regular DOM 
        // Example:
        // var docArticle = doc.querySelector('article').innerHTML;

        console.log(doc);
    })
    .catch(function(err) {  
        console.log('Failed to fetch page: ', err);  
    });
Vladimir Jovanović
  • 3,288
  • 1
  • 20
  • 27
  • didnt know about that, ive been using innerHTML – Muhammad Umer Apr 07 '19 at 03:15
  • 1
    It's nice that the answer is exactly what I was looking for, even though the question didn't mention writing to DOM. Also relevant: https://api.jquery.com/load/ – caffeinum Jul 25 '19 at 07:30
  • 16
    @caffeinum - thanks! When you fetch the external html page, it will be in plain text format and you can't do anything meaningful with that. IMHO the natural next step was to do something with that document and in order to do that, we must parse that text as DOM. At that point we can select and manipulate that document. I didn't want to mention jQuery on purpose, because future is in vanilla JS. We should all transition to that, if we haven't already. – Vladimir Jovanović Jul 29 '19 at 18:05
  • so script attached to this file will run?? – Muhammad Saquib Shaikh Nov 16 '20 at 15:39
  • 3
    I was looking for a way to fetch a document object. I’m surprised there isn’t something like `response.document()`. This did the job. Thanks – Manngo Apr 16 '22 at 08:49
130

You need to use the .text() method, instead of .json(). This converts the byte stream into plain text, which can be parsed by the browser as HTML.

bronzehedwick
  • 2,862
  • 2
  • 22
  • 28
22

you can return the response with .text(), and then render the page in the doc as you want.

function fetchHtml() {
  fetch('./file.html')
  .then((response) => {
    return response.text();
  })
  .then((html) => {
    document.body.innerHTML = html     
  });
}
Ben
  • 12,614
  • 4
  • 37
  • 69
mahmoud miz
  • 229
  • 3
  • 4
12

Using Promise Chaining with .then() is an older way of coding fetches and responses. A more modern way would be to use async functions and await like the following:

async function fetchMyDocument() {      
  try {
    let response = await fetch('/path/to/file.html'); // Gets a promise
    document.body.innerHTML = await response.text(); // Replaces body with response
  } catch (err) {
    console.log('Fetch error:' + err); // Error handling
  }
}

And about the direct answer to the question, (like every other answer) .text() is used instead of .json() on the response.

Leviathan
  • 2,468
  • 1
  • 18
  • 24
FoxPaw
  • 186
  • 1
  • 9
1

It should be:

fetch('/path/to/file').then(function(response) {
    return response.text();
}).then(function(string) {
    console.log(string);
}).catch(function(err) {  
    console.log('Fetch Error', err);  
});
0
  • 1- call function fetch and add the path of the page.
  • 2- then convert the fetch data to text by function .text().
  • 3- then append the page component to your parent container.

       async function getAbout() {


    await fetch('./component/about.html').then(res => res.text()).then(data => {
    page_2.innerHTML = data;

     }).then(() => {
       
         // after fetch write js code here  
     })}
  • for fetch, component don't add <body> or <HTML> tag < just use other like <div> or other tags.

  • for better performance use async-await!.

Omar bakhsh
  • 896
  • 11
  • 18
0

Use this:

var fetchAndParse = async (url) => { let div = document.createElement("div"); div.innerHTML = await (await fetch(url)).text(); return div }

Today I wanted to know the length of all the OpenSource licenses. I ended up running a code like this one on https://opensource.org/licenses/ (remove .slice(0, 3) to map it onto all links):

var $$ = (x) => ([...document.querySelectorAll(x)])
var fetchAndParse = async (url) => { let div = document.createElement("div"); div.innerHTML = await (await fetch(url)).text(); return div }
var waitTimeout = async (duration) => { return new Promise((accept) => setTimeout(accept, duration)) }
var licenseArray = await Promise.all($$(".license-table--title a[href]").slice(0, 3).map(async (anchor, k) => {
  let text = await fetchAndParse(anchor.href).then(div => div.querySelector("#LicenseText, .entry-content").textContent.replace(/\s+/g, ' ').trim()).catch(error => { console.error(anchor.href, error); return '' })
  return [anchor.href.replace(/^.*\/([^/]+)\/?/, '$1'), anchor.textContent.length, text.length, anchor.textContent, { href: anchor.href, text }]
}))
Mathieu CAROFF
  • 1,230
  • 13
  • 19