2

Here is what I want to do, in a browser, thanks to Javascript:

var mytext = '';
mytext += loadfile('intro.md');
mytext += loadfile('chap1.md');
mytext += loadfile('chap2.md');
... other files ...
$('#container').html(markdownrender(mytext));

Note: I do know that in-browser JavaSript is not really made to do such things, I have read many linked questions to this, such as How do I load the contents of a text file into a javascript variable?.

I have two issues:

Cross origin problem

  • As noted in this answer, jQuery.get('foo.txt', function(data) { alert(data); }); will be blocked as a Cross-Origin request when used with file://... (Firefox, Chrome, etc.). Even when this error doesn't appear, another one appears: no successful loading and not well-formed error. This cannot be solved when using local files, the only solution would be to use a server (see this comment).
    Is there any update on this , with new versions of jQuery, since this answer was from 2008?

  • This XMLHttpRequest solution works (no Cross-Origin error) but is quite long / verbose. Is there a more modern way to do it, now, 8 years later?

Syncronous / asynchronous

  • Even if the previously-mentioned XMLHttpRequest solution works, I don't see an elegant way to be sure that file intro.md will be finished to be loaded before chap1.md, and idem about chap2.md. Then there is a risk the files will be loaded in wrong order. I can see dirty hacks that will work, but no real satisfying solution.

Is there nowadays a modern way to load content from a few static files in (browser) JavaScript?

Note: I'm looking for a solution that also works on local files file://..., without any server. (I do have servers myself, local, distant, etc. but here I want to find a solution that works even for someone who has only a browser).

Community
  • 1
  • 1
Basj
  • 41,386
  • 99
  • 383
  • 673
  • Comments are not for extended discussion; this conversation has been [moved to chat](http://chat.stackoverflow.com/rooms/129719/discussion-on-question-by-basj-loading-a-few-files-from-javascript). – Bhargav Rao Dec 04 '16 at 08:02

2 Answers2

2

Summary of what is working / not working:

FileReader API

Allows to have an <input> that asks, with a popup, to Browse and select a local file and open it:

enter image description here

But doesn't allow to silently "include" a text file that is in the same directory. See Is it possible to load a file with JS/HTML5 FileReader on non served page? and this answer. Thus mytext = loadfile('intro.md'); suggested in the question is not possible with this API.

Fetch

Suggested by @devilfart. Works but not crossbrwoser (IE and Safari non supported).

jQuery

jQuery.get('test.txt', function(data) { alert(data); });

works great with a server. Doesn't work on local (non-served): after checking carefully, it's a Cross-Origin problem on Chrome, and either a non well-formed or syntax error on Firefox.

XMLHttpRequest

This:

var client = new XMLHttpRequest();
client.open('GET', './test.txt');
client.onreadystatechange = function() { alert(client.responseText); }
client.send();
  • works when used with a server

  • works on local non-served with Firefox (strangely, two alert happen: one with empty content, one with the loaded content)

  • doesn't work on local non-served with Chrome (Cross Origin problem).


Interesting note found here:

The only caveat is Chrome, which restricts access to local files via AJAX. To resolve this, simply add --allow-file-access-from-files to your Chrome runtime. All other modern browsers work on direct files without any hassle. csi.js also works fine from any web server, assuming you are following appropriate CORS policies.

Community
  • 1
  • 1
Basj
  • 41,386
  • 99
  • 383
  • 673
1

To answer the question of is there a more elegant solution than section 2, yes there is with the Fetch Api. Though it is only avalible in modern browsers.

var mytext = '';
fetch('intro.md')
    .then( res => res.text() )
    .then(text => {
      mytext += text
      return fetch('chap1.md')
    })
    .then( res => res.text() )
    .then(text => {
      mytext += text
      return fetch('chap2.md')
    })
    .then( res => res.text() )
    .then(text => {
      mytext += text
      //mytext is fully formed
      $('#container').html(markdownrender(mytext));
    })

However if you are only concerned about modern browsers then we can use an array reduce method of avoid some of the repition;

var mytext = '';
var mdArray = [ 'chapter1.txt', 'chapter2.txt', ''];

mdArray.reduce((accumulator, currentValue, currentIndex, array) => {

  return accumulator.then(res => res.text()).then(text => {
    mytext += text;
    return fetch(currentValue);
  })
}, fetch('intro.txt')).then( () => {

  //can do anything with mytext in here
  $('#container').html(markdownrender(mytext));
})
devilfart
  • 351
  • 1
  • 5
  • Thanks, interesting solution! Please note that in 2016, this is still experimental (`This is an experimental technology. Because this technology's specification has not stabilized...`) and not supported at all for IE and Safari. We'll see in the future if this will stabilize. – Basj Dec 04 '16 at 10:27