Package Markup with Javascript component for use as template - javascript

I've got an HTML document template.html which looks something like:
<div id="Foo">
<span class="Bar"></span>
...
</div>
Which I use as a template to construct some DOM for a component in Javascript.
At first, I just added it to my page example.html by copy-pasting the markup and wrapping it inside of a <template> tag. Then in my Javascript I constructed a clone to add it to the page by doing something like:
var myTemplate = Document.getElementById('#myTemplateId').cloneNode(true);
Document.getElementById('#newParent').appendChild(myTemplate, true);
While this works, I obviously don't want to have to copy and paste my templates over to be able to use my component.
What I would like to do is somehow package my template markup in with my Javascript. I still want to keep my Javascript and Markup separate for development and sanity, but at build time (I'm using GruntJS as my task runner) for the component I want to combine them somehow so they can be packaged in one, easy to link to file. What I mean by that is I want some way to package the template in with my Javascript, as Javascript. That said, I guess my broader question is:
How can you save/package Markup into a Javascript file or object (JSON) so that it can be used as a template to generate DOM in Javascript later?
The closest I have come to finding a potential solution was to convert the Markup document template.html to a string, save it to a variable, and then add it to a temporary element via .innerHTML and then convert it to make it a DocumentFragment. While that could work, there has to be a better way, and converting the Markup to a string doesn't feel right.
Update:
I know through my research that the trend of the industry is moving to HTML imports. However, it's not widely supported yet, so in the meantime, I still want to be able to package my templates into my js.

Related

Use of Template with HTML Custom Elements

I just started learning about the HTML custom elements, and through reading a series of intros, tutorials, and documentation, I think I have a good handle on how it works, but I have a philosophical question on the proper way to use or not use the <template> tag.
Custom elements give you the ability to encapsulate new functionality, simplifying the structure of your HTML document, and allowing you to simply insert a <my-custom-element>...</my-custom-element> tag instead of <div class="my-custom-element"><span class="part1">...</span><span class="part2">...</span></div>.
The class definition for the element then sets up the structure and functionality of that element. A bunch of the tutorials then describe how to use <template>...</template> and <slot>...</slot> to set up the contents of the custom element. You would then have to then include the template code in every HTML document in which you want to use the element rather than setting it up in the custom element class's constructor. Doesn't this run counter to the fact that custom elements help simplify and encapsulate functionality in a way that makes them more portable? Or am I misunderstanding the proper usage and/or placement of the template within the document?
Looking through SO, the closest I can find to addressing this is this question:
How to stamp out template in self contained custom elements with vanilla js?
But the answer essentially sidesteps this all together and says "Don't use <template>," and so doesn't really clear up my confusion.
Actually <template> elements can be imported from another document via HTML Imports, along with the Javascript code that will define the custom element:
<link rel="import" src="my-custom-element.html">
...
<custom-element></custom-element>
So it doesn't need to be included in a every HTML document. This post shows a minimal example.
HTML Imports are implemented only in Chrome and Opera. If you want to use them in the with Firefox and Safari you'll need to use the HTML Imports polyfill.
On the other hand and for the moment, Mozilla and Apple don't intend to implement HTML Imports natively in their respective browsers. Therefore they recommend to define custom elements with pure Javascript modules (with import or <script src="...">), and promote template literals strings instead, which offer some advantages (variables, functions), but are sometimes more complicated to code in an IDE (because of their string representation).
Maybe in the future standard HTML modules will be adopted by all browsers, and <template> will come back in the spotlight...
Note that without HTML Imports you can still import yourself some HTML documents with fetch():
fetch( "template.html" )
.then( stream => stream.text() )
.then( text =>
customElements.define( "c-e", class extends HTMLElement {
constructor() {
super()
this.attachShadow( { mode: 'open'} )
.innerHTML = text
}
} )
)
Update 2019
HTML Imports won't be supported natively after Chrome 73. You should then use the other solutions listed above (the polyfill, an alternate module loader, JS import, or a direct download with fetch).
Disclaimer: I'm an author of the rich-component library mentioned below.
After some time of experimenting with custom elements and recently raising a full blown project based solely upon them I'd like to share my insights on this:
any component tiny as it is, is a candidate to grow to some beast
HTML part of it may grow to a point where it is very non-convenient to keep it within JS
do use template, built and parsed once and from that point cloned and injected into the shadow root - this is the same best practice as to use document fragment instead of mutating a living DOM
if the template contents should be changed from component's instance to instance - some kind of data binding framework may be used, and if minimalist approach on those is taken - it might still be easier and more performant to deal with a cloned-from-template document fragment than operate on string or template literals
In order to not write the same dozens of lines over and over again I've prepared rich-component library, which:
normalizes some API for template provisioning and all those 'clone template, create shadow, inject template's content into it' lines of repeating code
known to fetch html contents when html URL is provided
caches the templates so the fetch is done only once

How to modify HTML from another file with JavaScript/jQuery

I need a reference of a HTML element that has an id of #searchResults
$.get('search-for-prospect', function() {
_content.find('.prospect-container').sort(function(a,b){
...stuff...
}).appendTo('#searchResults');
})
I tried using jQuery's get to get the that element, but it doesn't work as expected.
I need to get a reference of searchResults and append to it. How can I achieve that?
The only way to get HTML from another page with javascript is by making AJAX call (in fact js template engines work this way).
So you have to $.ajax the page you want, parse it as HTML and do what you want to do.
Beware: you are not editing the HTML file itself, but just its "in memory copy".
Javascript, as far as it is used as client-side technology, does not allow modifying files or in general accessing the file system. So if you're looking for some trick to write in the HTML, you're on the wrong way

Persistent templates (or re-using templates) with Hogan / Mustache?

Not entirely sure if "persistent templates" is what I am after, this is the first time I am using a Javascript templating engine. I am curious if there is a way of keeping the template data intact for the purposes of re-rendering a document once it has been rendered...
An example -- I define a simple template snippet:
<div id="price">Price: {{current_price}}</div>
I render it:
var template = Hogan.compile($("#price").html())
$("#price").html(template.render(price_data))
Let's say I want to update the price information every X seconds (fire a request, grab JSON and push it back to #price), re-rendering the template fails as there is no {{current_price}} any more. I could just do something along the lines of $('#price').text('Price: ' + price_data) after a succesful request but I feel this somehow makes the idea behind using templates useless.
So the question is, what is a way to re-use templates on a document? Cache the template data into a variable and re-use it when rendering or is there a more clever way?
Thanks.
You shouldn't be using a thing as its own template, you'll run into all sorts of problems (especially once you start adding mustache tags to attributes, or conditionally showing html tags, or anything like that).
Make your template its own element on the page. And do yourself a favor and use a <script type="text/x-mustache"> tag for it.

More modular; include html elements in .js file?

I have a page which has many buttons that perform an action with javascript. No problem here, just a html page and multiple .js file, one .js file for every action. Just to separate stuff a bit and make everything a bit more manageable. I will write a script to combine them all at deployment.
A lot of my javascript actions need html elements to function. For example to change a color the color.js needs a div with form elements so that user can change the color and press the button to save changes. The color.js binds functions to the onClick of the buttons and such. No problem here.
A problem comes up when there are many javascript actions using many html elements. All these html elements need to put into one html page. This makes managing things complicated. For example when i change the color field id i need to go to color.js and do the change. In addition i have to find the html element in my huge html page and also do the change.
Is it possible to put the html elements needed by my javascript actions into the .js file and somehow include it in my html page?
Of course i can escape all html elements and put it in a javascript variable and document.write it. But escaping is not a nice thing to do and changing html afterwards is quite painful.
I'm also not looking for a ajax function, cause with many different actions this will cause network problems.
Cheers for any thoughts on the subject!
You can use templates to generate dynamic HTML via JavaScript. Most of Templates Engines have the same implementation:
A template structure (DOM) or HTML string
A method to render the template
The data that fills the template (Array, Object)
Settings to configure the template engine
I have used some template engines, one of them is Hogan.js which has a friendly sugared-syntax, very easy to use, but lower performance. It implements the Mustache syntax.
Other template engine is in Underscore.js library, which provides no sugared-syntax but a native javascript syntax, giving more power, and best performance.
The last one that I have used is kendo-ui templates.Kendo UI templates have a heavy emphasis on performance over feature glut. It will trade convenient "syntax sugar" for improved performance.
If you want to try more engines, here you can get a lot of them:
Template-Engine-Chooser
By other hand, if you want just to load static HTML (e.g. an static view), you can use jQuery.load(url) to load data from the server and place the returned HTML into the matched element. .load() is a shorthand method of jQuery.ajax()
Recomendation: whichever option you choose, I recommend using cache, either to save the view or to save the compiled template, thus the performance is improved by avoiding request of the same resource.

How to put THREEjs code into separate JS file with meteor

Okay so I have a meteor app and I am trying to make templates that have THREEjs animations in them so I can load a specific animation by loading a specific template. I wanted to de-clutter my HTML file by taking out the script tags and moving them to a separate JavaScript file but that wasn't working. I ended up just putting the JavaScript into my HTML and it worked. I was going to just keep doing that and live with the cluttered code but now I have run into a problem.
For some odd reason even if a for loop is inside the script tags, the computer will see the < sign and expect a html tag. At first I thought I had forgotten a closing tag or something but I checked and I haven't. If I delete the for loop (only create one particle) everything works perfectly again. I could fix this by just using escape character for the < sign (<) but I think I should find a solution to the overarching problem so I don't run into similar problems in the future.
I want to put the least amount of JavaScript in my HTML file as possible. (Meteor doesn't like it and neiter do I.)
If I try to just copy and paste my JavaScript into a separate file, it won't find the min.three.js file (it tells me THREE isn't defined)
I would prefer not to use another framework like require.js mainly because I'm not sure how but I will as a last resort if that's the only way to do it
All the examples for THREEjs put the code directly in the HTML file but how can I put it into a separate javascript file and make sure the javascript file finds min.three.js?
This is an overview of what the template looks like. I used jQuery to find actualAnimation2 and appended the container to that. You can also find all the code here
<template name = "animation2">
<div id = "animation2" class = "centered">
<div class = "line"></div>
<h1>Animation 2</h1>
<div class = "line"></div>
{{> animationButtons}}
<!--Put in a threejs animation here -->
<div id = "actualAnimation2" class = "animation">
<script src="client/three.min.js"></script>
<script>
//THREEjs stuff here
//This is what I want to move
</script>
</div>
</div>
</template>
tl;dr: How can I make THREEjs play nice with Meteor?
Any suggestions are welcome and let me know if I can clarify anything. Thank you for your help!
Quoting http://docs.meteor.com/ :
Some JavaScript libraries only work when placed in the
client/compatibility subdirectory. Files in this directory are
executed without being wrapped in a new variable scope. This means
that each top-level var defines a global variable. In addition, these
files are executed before other client-side JavaScript files.
This is exactly what needs to be done with three.min.js because the beggining of the file looks like :
// threejs.org/license
'use strict';var THREE={REVISION:"68"};
So you need to put three.min.js inside cient/compatibility/.
But you are probably better off using a package, choose carefully the one who is more likely to upgrade to revision 69 quickly in a few weeks or so.
If I try to just copy and paste my JavaScript into a separate file, it won't find the min.three.js file (it tells me THREE isn't defined)
It sounds like you're running into an issue where your JS files are loaded before min.three.js. You might be able to fix that by taking advantage of Meteor's JS load order - files in directories called lib are loaded first, so if you put your min.three.js file inside /client/lib it will load before source files outside that directory.
Another option would be to use a package - for example, meteor add aralun:three.

Categories

Resources