How to use HTML templates with javascript Web component - javascript

HTML templates are nice and fast. The problem is HTML import has been deprecated. This means it makes the most sense to put web components each in their own .js file instead of .html files. However if one uses a .js file, then as far as I can tell, one can't use a precompiled template to attach at the shadow DOM, one has to use some fixed string that I imagine is parsed every time.
With .html web component one could do:
shadowRoot.appendChild(myTemplate.content.cloneNode(true));
However in a .js file you have to do something like:
something.innerHTML = `
<style>
...
/* lots of style */
</style>
<b>I'm in shadow dom!</b>
<!-- lots of html -->
<slot></slot>
`;
Is this not a bad idea because the text must be revaulated every time, whereas a template is evaulated once?
So what is the recommened way of embedding one's style and html in a .js file for a web component?
I am using vanilla JS, no frameworks.

+1 for Vanilla code, your code will last another 26 JavaScript years.
There is no recommended way, it all depends on your use case.
You can put <template id="MY-COMPONENT">in your main HTML file,
and do document.getElementById("MY-COMPONENT").content.cloneNode(true) in your component code (knowing when your DOM is ready)
You can do document.createElement("template"), and take it from there
You can use the <load-file> Web Component to load external HTML files.
(no template advantage here)
So it all comes down to understanding what your application needs.
Note that the pattern you see in many blogs:
let template = document.createElement("template");
template.innerHTML = ` CSS & HTML CONTENT `;
...
this.shadowRoot.appendChild(template..content.cloneNode(true));
In many use cases, where a parsed template is hardly re- used, is an expensive way of writing:
this.shadowRoot.innerHTML = ` CSS & HTML CONTENT `;
Be aware the append template approach has (potential) (edge-case) disadvantages as well;
its content is parsed async, while .innerHTML= is synchronous.
An while you are at it, be sure to learn the differences:
https://developer.mozilla.org/en-US/docs/Web/API/Element/append (no IE support)
https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild

Related

Are there better ways of inserting HTML into the DOM than insertAdjacentHTML() and a huge HTML string?

I need to insert a large amount of HTML into the DOM.
My current way of doing this is copy-pasting the HTML I need to insert into a JS file, formatting it such that it's bug-free within the string, and then using insertAdjacentHTML() to insert.
Having a huge, multi-line string of copy-pasted HTML in the JS just feels dirty every time I happen to scroll through it.
The only constraint is that I really want to avoid using libraries if I can.
Better in this case is pretty much a secure implementation that's an improvement in readability.
Even if you don't wish to use a HTML templating tool, you can still include the HTML in a similar manner.
The handlebars.js site includes some examples, such as including the HTML in the .html file itself, selecting the element, and parsing its contents:
You can deliver a template to the browser by including it in a tag.
<script id="entry-template" type="text/x-handlebars-template">
<div class="entry">
<h1>{{title}}</h1>
<div class="body">
{{body}}
</div>
</div>
</script>
Compile a template in JavaScript by using Handlebars.compile
var source = $("#entry-template").html();
var template = Handlebars.compile(source);
In your case, you could replace the type property in the html to anything obvious, and retrieve the contents by calling document.querySelector("htmlString").innerHTML.
Other options include loading the HTML as a separate file via AJAX, or if you're daring, writing your own transpiler to stick the HTML in the Javascript for you (which is basically what React does with JSX)
One last thing to consider: you will want to ensure that the injected HTML doesn't include any potential XSS vulnerabilities or that any DOM events won't get screwed up as a result of adding the HTML (though using insertAdjacentHTML should prevent events from breaking).
Also, consider looking over this HTML injection code I wrote awhile back to ensure you're avoiding (some but not all) possible pitfalls:
https://github.com/jsweeneydev/HTMLTemplate/blob/master/htmltemplate.js
Good luck!

Using text.js with require.js how can I create one template html file but grab each script template individually?

I'm using require.js and text.js to load a template file that has a bunch of <script> templates in it:
e.g. /scripts/templates/comments.html
<script type="text/template" id="js-comment-reply-tmpl">
// html in here
</script>
<script type="text/template" id="js-comment-edit-tmpl">
// html in here
</script>
And because using underscore's template system (or any similar js micro-templating system), this file itself gets loaded as a string. Is there a smart way to just grab each template from within that file? e.g. $(template).html() wrap it in jQuery and then do a find() or something on it? I'd essentially have to place it into the DOM first though, so that would probably be slow as hell and I might as well just not even load it with text.js and just pluck it out of the DOM initially.
My other thought is to split them each into their own files, but then that would slow down on request time (although I'd probably just end up using r.js with node to minify this all in the end anyway so it wouldn't matter).
e.g. /scripts/templates/comment_reply.html
<script type="text/template" id="js-comment-reply-tmpl">
// html in here
</script>
e.g. /scripts/templates/comment_edit.html
// html in here
What's the best/most efficient way to do this?
I would advise moving each template into it's own file and loading them all separately. Some of the advantages of doing so are:
Easier maintainance - Searching for a template in a project is easier if they all have their own names and it also reduces hassle with source control conflicts if developers aren't all editing the same file.
Portability - If you end up using a templating system that can be used on the server (like Mustache) then the templates are already easy to share between front and back-end.
The main disadvantage that you already highlighted are the extra requests, but you should definitely not be going to production without building your scripts with r.js so this shouldn't be a problem

Comparmentalize HTML via Javascript or jQuery

I'm trying to come up with a purely front-end solution to a common practice.
In the past, I've stored the HTML for reused website elements (like the <nav>, <header>, <footer>, etc.) in a functions.php file, and used php functions to call these things in on multiple pages. This of course makes it easier to make changes site-wide, making my life easier.
However, I don't really want to use PHP (or in the past, ASP) to do this anymore. Does any one know of a clean way to do this in Javascript, jQuery or even Node? To be clear I want to call a function in the HTML (like writeNav(); ) that pulls the HTML for the nav. I'd rather not include <script> tags everywhere, if possible.
One very common solution for "building up a library of chunks of HTML that can be reused elsewhere" is "templating". There are numerous templating libraries to choose from (Underscore even has its own, small, template function), but I'd recommend looking at Handlebars.js first, as it's very robust but also very simple.
Handlebars templates will allow you to store your HTML however you want:
in strings in your .js files,
in <script type='text/handlebars'> tags on your html pages, or
in separate html files that get compiled in to a single JS file
It will also allow you to swap out small pieces of the HTML, so that you could (for instance) have a header that gets used all over, but replaces the title, like so:
<h3>{{title}}</h3>
The Handlebars site (http://handlebarsjs.com/) has an excellent run through; I highly recommend it.
There are also text editors like BBEdit with include support if it's just about organizing how you write your HTML.
I think what you're talking about are html includes.
This is not really a production worthy solution, but gets me through the prototyping phase.
Using jQuery, include this in your $(document).ready() callback:
$(".include").each(function() {
var inc = $(this);
$.ajax({
url : inc.attr("title"),
dataType : 'html',
success : function(data) {
inc.replaceWith(data);
console.log("adding " + inc.attr("title"));
});
Then in the body wherever you want to include an html file, do this:
<div class="include" title="path/to/html/file.html"></div>
All elements (divs, spans, etc) with the "include" attribute will be replaced by the content of the file path in the title attribute.
Note: the entire tag will be replaced in this process.
This is trivial with jQuery, e.g.
function writeP(str){
document.write($('<p />').text(str).get(0));
}
You could then do something like:
<div class="foo">
<script type="text/javascript">writeP('hello');</script>
</div>
...which would result in:
<div class="foo">
<p>hello</p>
</div>
That example is silly, but I believe the mechanism is in the spirit of what it is you're trying to accomplish.
Cheers

Maintaining proper CSS \ Javascript and using a Template Engine - a contradiction?

A template engine (Velocity, FreeMaker, etc.) lets you, among other things, split up your HTML into re-usable chunks. E.g. you have a <div> showing an ad that appears on lots of places in your site - you can compose a file containing that <div> and its contents once (with Velocity: a 'myAd.vm' file), and load it up into whatever page necessary (with Velocity: apply #parse('myAd.vm').
I like to think of these .vm files as functions, they get "invoked" (parsed) and spit out textual content. They can have "parameters" - in Velocity you can #set( $myParam = 'foo' ) just before parsing the 'myAd.vm' file, and use that variable inside that file.
My question is: How does the proper way of defining CSS and Javascript in their own files fit in with that?
The 'myAd.vm' needs CSS styling, you can define that CSS in that file itself with a <style> tag - which will result in an HTML document with a style tag in its <body> - not in its <head>, and certainly not in a separate file.
Or, you could define the CSS that 'myAd.vm' needs in a separate 'myAd.css' file, and demand that whatever HTML document that parses 'myAd.vm' will have a <LINK REL="StyleSheet" HREF="myAd.css" TYPE="text/css"> in its head tag. This is a problem since it makes things more complex and cumbersome, and - you may want to actually parse the 'myAd.vm' file depending on a conditional (in Velocity, for example, you could have #if(someCondition) #parse('myAd.vm') #end) - meaning you don't actually know in advance whether the head tag should link to that external CSS file.
Any thoughts?
Thanks.
Most frameworks that ive used give you the ability to make some kind of function call that kind of acts as an include for a css or js file, these are then output in the head to external files. In many casses i actually run all these through a minifier so in the end there is only one css and one js file.
This way you can add to the asset stack from within view partials and put stuff directly in the head.
Apache Wicket (a component-based framework) allows you to add what it calls "Header contributions" ("renderHead" method now in Wicket 1.5) as a part of its page composition through inheritance system. This means that, although a page only defines a chunk of the total HTML to be rendered, it can still add something to the <head> of the whole document and therefore include <link> tags for your javascript and CSS files in their correct places.
As for non-component-based frameworks, the Thymeleaf template engine (which can be used with Spring MVC), as a result of its "natural templating" ability, allows you to compose pages by including fragments from other pages (both in <head> and in <body>, which to some extent solves your issue), as opposed to the "inheritance-oriented" approach natural to composition frameworks like Sitemesh or Tiles.
Regards,
Daniel.

Is it possible to use Django to include CSS (here's the tricky part) from a JS file?

As far as I know, there's no way to use {% include %} within a dynamic JS file to include styles. But I don't want to have to make another call to the server to download styles.
Perhaps it would be possible by taking a stylesheet and injecting it into the head element of the document...has anyone does this before?
In your JS file:
var style = document.createElement('link');
style.setAttribute('rel', 'stylesheet');
style.setAttribute('type', 'text/css');
style.setAttribute('href', 'style.css');
document.getElementsByTagName('head')[0].appendChild(style);
Hope that helps.
With jquery...
$('head').append($('<link rel="stylesheet" type="text/css" href="style.css">'))
I can envision cases where you'd want to dynamically generate JS or CSS, but generally you're better off creating static files for each and making your code general enough to fulfill all your needs.
This goes beyond a simple matter of code reuse - if you're dynamically generating any of this, it will need to be re-downloaded each time it's used. You're wasting CPU time rendering the templates, and wasting bandwidth sending the same (or potentially the same) data over the wire over and over.
But if you have a good use case for meta-coding, there's no reason why you can't either:
a) put the JS or CSS in the header (or body, in the case of JS) of your rendered template
b) create a view for the JS or CSS, and use Django's template engine to render them.
The {% include %} tag will work fine for (a), and for (b) you'd just use normal HTML to reference the URL of your view.

Categories