1

The Skinny

I'm well aware that JS requires AJAX (or some synch/asynch call) to get the contents of a file after page load.

(From previous link)

var client = new XMLHttpRequest();
client.open('GET', '/foo.txt');
client.onreadystatechange = function() {
  alert(client.responseText);
}
client.send();

Browsers won't allow direct access to files. This is for security reasons, mitigating XSS attacks and so forth.

But is there a way to include it on page load... without including it into the markup?

I am using a couple client-side templates (not interested in client- vs server-side debate). Currently, the only way to include them on page load, without additional calls to the server, is to toss the contents into a custom-type script tag:

<script id="t1" type="x-some-template">
    ... contents of template
<script>

And then reference the contents of the script block in some way:

var templateContents = $('#t1').html();

But if the template is long, it muddies up the markup (assuming the user is viewing the source). This is entirely aesthetics and nit-picking, and does not affect how anything operates, but is there some sneaky way of including the templates without having them appear in the markup?

So Far...

I tried putting the template into its own .js file, and including it in the same way:

<script id="t1" type="x-some-template" src="t1.js"><script>

But then there is no way to reference the content, since it is never loaded by the browser.

(From javascript.info)

If you put an unsupported value into type, e.g. , the contents will be ignored. This is a trick used add data that was not rendered to the page.

But if you put in a known type:

<script id="t1" type="text/javascript" src="t1.js"><script>

An error will be thrown, since the contents of the file isn't in proper JS.

Other solutions could be assigning the template content to a JS variable, and including the external file, then referencing said variable:

// t1.js

var temp = `... contents of template... `;

// page.html

var templateContents = temp;

But even with the template literal, it can be a nightmare including complex templates. And then you have the issue of assigning each template to a different variable and tracking them all. Headaches abound. The problem would be mitigated slightly if you could assign each template to a standard variable, and then reference them based on the script block that included it...

var templateContents1 = $('#t1').temp;
var templateContents2 = $('#t2').temp; // Or something like this...
... 

But that's a no-go.

I've search high and low, but all I've come across is endless AJAX, and HTML Imports, which have awful support.

Finally, The Question

Has anyone stumbled across a beautiful way to do this? And in a browser-independent (i.e. universally supported) way? I want to stay away from any post-load server calls (i.e., AJAX).

Load the page with the content, and use it persistently.

Thoughts On Potential Solutions

The only way to beautify the markup (that I can come up with) is to use the currently-working method, and then completely remove the script block:

var templateContents = $('#t1').html();
$('#t1').remove(); // Or similar

But this isn't a very good solution, 'cause then the content is no longer available if JS decides to garbage collect on templateContents, and you have to re-load the template.

I'm not much for server/.htaccess stuffs, but maybe I can modify the .htaccess (or similar) to read the custom-type script as plain text, and then it persists in memory to use on an as-needed basis?

Golly, it sure would be swell if...

The ideal situation would be similar to how I described above, where the plain template content is in a separate file (without being assigned to a variable)...

// t1.js

... all the contents of the template...

And then including it in a single, beautiful line...

// page.html

<script id="t1" type="whatever-works" src="t1.js"></script>

And reference the template contents in an equally simple and beautiful line...

var templateContents = $('#t1').someTextAccessMethod();

Any thoughts?

Community
  • 1
  • 1
Birrel
  • 4,754
  • 6
  • 38
  • 74
  • Thanks if you read the whole thing! Next long one I'll throw in a promo-code at the end for your next online purchase of winter tires, or something. – Birrel Sep 19 '16 at 09:37
  • Winter tyres ftw! – evolutionxbox Sep 19 '16 at 09:44
  • Did you try using localStorage...store the content into a variable & then fetching it on page load ? – Nikhil Nanjappa Sep 19 '16 at 09:45
  • You can try using iframe. – jcubic Sep 19 '16 at 09:47
  • @jcubic iframe is a worse option. The contents are still shown in the markup (and actually much longer, 'cause it automatically inserts a whole bunch of additional markup), and then additionally requires me to add css to the iframe to hide it. – Birrel Sep 19 '16 at 09:59
  • @NikhilNanjappa I didn't even know such a thing existed. Looking at it now, wouldn't I still need to have a line similar to `localStorage.template = '... long template string... ';` to initialize it? Or else an AJAX call, setting it to the result of the call? I'm not sure of any other way to assign the contents without them appearing in the markup, or having to do the additional call. – Birrel Sep 19 '16 at 10:06
  • Frankly I don't understand why don't you like client-side templates. "muddies up the markup" is exactly nitpicking, as you wrote yourself. Just move all such templates into the end of the document body, and they won't "muddy up the markup" – hindmost Sep 19 '16 at 10:17
  • No you can simply store the contents into any localStorage variable you can create anytime. its just like your `var` but persists in the browser which can be retrieved anytime accessing the same variable name. I could show you in an answer if you want ? You can use `localStorage.setItem("yourVariableName", yourvalue);` & get the value later using `localStorage.getItem("yourVariableName");` – Nikhil Nanjappa Sep 19 '16 at 10:29
  • @hindmost I have no problem with client-side, and prefer sending just the raw data from the server, opposed to the entire formatted content. And yeah, it is just nitpicky. No one is going to lose their job over this, so it isn't worth anyone putting a bunch of time in, if there isn't an obvious or well-known solution. – Birrel Sep 19 '16 at 17:39
  • @NikhilNanjappa I understand how to set and get the data, but `yourValue` still has to be assigned either by a) initializing the page with it (`yourValue = 'long string of stuff';`), or b) by using an ajax call and using the results (`yourValue = ajax.responseText;`). Either way, it's either filling the markup with it, or using AJAX - both of which I want to stay away from. Unless you mean using the "delete the block after it is read" and assign the data to localStorage after that time? Check out [this fiddle](https://jsfiddle.net/Birrel/73zhbjko/4/) with the differences. – Birrel Sep 19 '16 at 18:01
  • What's the problem with HTML imports? There's a polyfill that works in every browsers. – Supersharp Oct 08 '16 at 19:07

2 Answers2

0

You can try this.

Here is exactly what I did.

main.html

<!DOCTYPE HTML>
<html>

<head>
   <title>load txt</title>
</head>   
<body>
   <script type="text/javascript" src="test.js" type="application/json">
   </script>
   <script>
       document.write(a.data);
   </script>
</body>

</html>

And the test.js file.

var a = {
    "data" : "All the content of .txt file here with proper escape sequence and other formatting stuff"
  }

Now, Explanation.

You can put all your data in txt file in a object. Save that object in a variable and in a seperate file. You can use script tag to load that file during inital page load.

Use type="application/json" to stop the browser from performing any checks on the content of object.

The extension of the file can be anything you want. because the browser just checks the type of script being downloaded.

And once you have loaded the txt file wrapped in a object you can do JSON.stringify, load it into localStorage and then remove the script tag from html.

After this it'll be accessible even if you close the website and reopen it. And you don't have to modify anything on the server side or client side.

Ishan Jain
  • 665
  • 6
  • 20
  • This is the same as the case I laid-out in my question: `Other solutions could be assigning the template content to a JS variable...`. Re-read my question, you'll see it. And if I include two templates (or ten, or 100), then they all have to be named differently (`var a = ...`, `var b = ...`, and so on). Not a solution, regardless of adding `type="application/json"`. – Birrel Sep 19 '16 at 18:06
  • Oh, I see it now. I am sorry I can't imagine any other way to do this. – Ishan Jain Sep 20 '16 at 00:30
0

I see that you have no problem with putting the contents on a <script> tag except that its making your markup/view source dirty. So why not smartly pour your file content onto an external JS file(host it somewhere/locally) & then in your project call that external script, something like:

<script type="text/html" src="http://yourdomain.com/contents_of_template.js">
    var t1 = "<div> template contents here </div>"
</script> 

Basically this way you will get the contents and at the same time the contents of the file cannot be seen on your viewsource since its part of another external JS file. Let me know if this makes sense to you.

You could also explore options like window localStorage or FileAPI to read & write files, but again both would require some way of the reading the contents via AJAX or so.

Nikhil Nanjappa
  • 6,454
  • 3
  • 28
  • 44
  • If I do the external file, don't I still have to assign each template to a unique variable (i.e. `var t1 = '... long template string';`)? I'd like to a) keep each template in its own file (likely not a .js extension), and b) avoid assigning the templates to variables. How would I use the method you've detailed, while keeping the template file as the raw template (i.e. `
    {{some_var}}
    ` - no JS anywhere)
    – Birrel Sep 22 '16 at 07:12
  • Then what you want to do is to assign a variable inside external `contents_of_template.js`. Then you have the variable available for use wherever you want in jquery. `var t1 = "
    template contents here
    "`. Also change the `
    – Nikhil Nanjappa Sep 22 '16 at 09:03