jQuery templates on the server side - javascript

Has anyone tried to use jQuery templates (or any other JavaScript based templating) on the server side with something like env.js?
I'm considering attempting it to see what benefits could be gained by being able to render identical templates on either the client or server side of a web application, but I was hoping someone might already have some experience, or know of an existing project doing this. I'd be particularly interested to know about any performance issues I might encounter compared to some more traditional templating engine.
To recap : Has anyone ever used jquery templates on the server site? If so, were there any performance issues, or other problems I might run into?

env.js is unnecessary.
<plug shameless="true">
I am in the process of specing and re-implementing JQuery templates to allow them to be used independently of the DOM. See https://github.com/mikesamuel/jquery-jquery-tmpl-proposal for code, and demos. The spec is available at http://wiki.jqueryui.com/w/page/37898666/Template and it says:
Text-centric rather than DOM dependent. Status: Done. See section 12 implementations. foo${bar} translates to something very similar to function (data, options) { return "foo" + bar; } modulo some dethunking of bar
...
This will allow to use this template engine in server side javascript environment, such as node.js, or java/rhino
I would love feedback and can help you get started.
</plug>

A friend of mine working on a distributed Genetic Programing project used a js sevrer side template system to manage all the web workers spawned across all users browsers. His code is here: github. I don't know how helpful it will be, but I know it was quite simple to implement and did some amazing things. From how easy he found it I would recommend a js template system it.

It's fairly trivial to write server side code to process the jQuery templates.
Here is some very basic vb.net code I have created that will return the result of a jquery template string to an array of any objects. Currently it only does the replacement of data values
Public Shared Function RenderTemplate(template As String, list As Array) As String
Dim myRegexOptions As RegexOptions = RegexOptions.Multiline
Dim myRegex As New Regex(strRegex, myRegexOptions)
Dim splits = myRegex.Split(template)
Dim matches = myRegex.Matches(template)
Dim i As Integer = 0
Dim swap As Boolean = False
Dim str As New StringBuilder
For Each item In list
swap = False
For i = 0 To splits.Length - 1
If swap Then
str.Append(CallByName(item, splits(i), CallType.Get, Nothing))
Else
str.Append(splits(i))
End If
swap = Not swap
Next
Next
Return str.ToString
End Function
So if I sent in the following...
Dim strTargetString As String = "<p><a href='${Link}'>${Name}</a></p>"
Dim data As New Generic.List(Of TestClass)
data.Add(New TestClass With {.Link = "http://stackoverflow.com", .Name = "First Object"})
data.Add(New TestClass With {.Link = "http://stackexchange.com", .Name = "Second Object"})
Return Render(strTargetString, data.ToArray)
It would output it as a string
<p><a href='http://stackoverflow.com'>First Object</a></p>
<p><a href='http://stackexchange.com'>Second Object</a></p>
This will work alot faster than spawning up a fake browser object on the server, and running the whole jQuery library just to replace a few tags.

Related

How to prevent script injection attacks

Intro
This topic has been the bane of many questions and answers on StackOverflow -and in many other tech-forums; however, most of them are specific to exact conditions and even worse: "over-all" security in script-injection prevention via dev-tools-console, or dev-tools-elements or even address-bar is said to be "impossible" to protect. This question is to address these issues and serve as current and historical reference as technology improves -or new/better methods are discovered to address browser security issues -specifically related to script-injection attacks.
Concerns
There are many ways to either extract -or manipulate information "on the fly"; specifically, it's very easy to intercept information gathered from input -to be transmitted to the server - regardless of SSL/TLS.
intercept example
Have a look here
Regardless of how "crude" it is, one can easily use the principle to fabricate a template to just copy+paste into an eval() in the browser console to do all kinds of nasty things such as:
console.log() intercepted information in transit via XHR
manipulate POST-data, changing user-references such as UUIDs
feed the target-server alternative GET (& post) request information to either relay (or gain) info by inspecting the JS-code, cookies and headers
This kind of attack "seems" trivial to the untrained eye, but when highly dynamic interfaces are in concern, then this quickly becomes a nightmare -waiting to be exploited.
We all know "you can't trust the front-end" and the server should be responsible for security; however - what about the privacy/security of our beloved visitors? Many people create "some quick app" in JavaScript and either do not know (or care) about the back-end security.
Securing the front-end as well as the back-end would prove formidable against an average attacker, and also lighten the server-load (in many cases).
Efforts
Both Google and Facebook have implemented some ways of mitigating these issues, and they work; so it is NOT "impossible", however, they are very specific to their respective platforms and to implement requires the use of entire frameworks plus a lot of work -only to cover the basics.
Regardless of how "ugly" some of these protection mechanisms may appear; the goal is to help (mitigate/prevent) security issues to some degree, making it difficult for an attacker. As everybody knows by now: "you cannot keep a hacker out, you can only discourage their efforts".
Tools & Requirements
The goal is to have a simple set of tools (functions):
these MUST be in plain (vanilla) javascript
together they should NOT exceed a few lines of code (at most 200)
they have to be immutable, preventing "re-capture" by an attacker
these MUST NOT clash with any (popular) JS frameworks, such as React, Angular, etc
does NOT have to be "pretty", but readable at least, "one-liners" welcome
cross-browser compatible, at least to a good percentile
Runtime Reflection / Introspection
This is a way to address some of these concerns, and I don't claim it's "the best" way (at all), it's an attempt.
If one could intercept some "exploitable" functions and methods and see if "the call" (per call) was made from the server that spawned it, or not, then this could prove useful as then we can see if the call came "from thin air" (dev-tools).
If this approach is to be taken, then first we need a function that grabs the call-stack and discard that which is not FUBU (for us by us). If the result of this function is empty, hazaa! - we did not make the call and we can proceed accordingly.
a word or two
In order to make this as short & simple as possible, the following code examples follow DRYKIS principles, which are:
don't repeat yourself, keep it simple
"less code" welcomes the adept
"too much code & comments" scare away everybody
if you can read code - go ahead and make it pretty
With that said, pardon my "short-hand", explanation will follow
first we need some constants and our stack-getter
const MAIN = window;
const VOID = (function(){}()); // paranoid
const HOST = `https://${location.host}`; // if not `https` then ... ?
const stak = function(x,a, e,s,r,h,o)
{
a=(a||''); e=(new Error('.')); s=e.stack.split('\n'); s.shift(); r=[]; h=HOSTPURL; o=['_fake_']; s.forEach((i)=>
{
if(i.indexOf(h)<0){return}; let p,c,f,l,q; q=1; p=i.trim().split(h); c=p[0].split('#').join('').split('at ').join('').trim();
c=c.split(' ')[0];if(!c){c='anon'}; o.forEach((y)=>{if(((c.indexOf(y)==0)||(c.indexOf('.'+y)>0))&&(a.indexOf(y)<0)){q=0}}); if(!q){return};
p=p[1].split(' '); f=p[0]; if(f.indexOf(':')>0){p=f.split(':'); f=p[0]}else{p=p.pop().split(':')}; if(f=='/'){return};
l=p[1]; r[r.length]=([c,f,l]).join(' ');
});
if(!isNaN(x*1)){return r[x]}; return r;
};
After cringing, bare in mind this was written "on the fly" as "proof of concept", yet tested and it works. Edit as you whish.
stak() - short explanation
the only 2 relevant arguments are the 1st 2, the rest is because .. laziness (short answer)
both arguments are optional
if the 1st arg x is a number then e.g. stack(0) returns the 1st item in the log, or undefined
if the 2nd arg a is either a string -or an array then e.g. stack(undefined, "anonymous") allows "anonymous" even though it was "omitted" in o
the rest of the code just parses the stack quickly, this should work in both webkit & gecko -based browsers (chrome & firefox)
the result is an array of strings, each string is a log-entry separated by a single space as function file line
if the domain-name is not found in a log-entry (part of filename before parsing) then it won't be in the result
by default it ignores filename / (exactly) so if you test this code, putting in a separate .js file will yield better results than in index.html (typically) -or whichever web-root mechanism is used
don't worry about _fake_ for now, it's in the jack function below
now we need some tools
bore() - get/set/rip some value of an object by string reference
const bore = function(o,k,v)
{
if(((typeof k)!='string')||(k.trim().length<1)){return}; // invalid
if(v===VOID){return (new Function("a",`return a.${k}`))(o)}; // get
if(v===null){(new Function("a",`delete a.${k}`))(o); return true}; // rip
(new Function("a","z",`a.${k}=z`))(o,v); return true; // set
};
bake() - shorthand to harden existing object properties (or define new ones)
const bake = function(o,k,v)
{
if(!o||!o.hasOwnProperty){return}; if(v==VOID){v=o[k]};
let c={enumerable:false,configurable:false,writable:false,value:v};
let r=true; try{Object.defineProperty(o,k,c);}catch(e){r=false};
return r;
};
bake & bore - rundown
These are failry self-explanatory, so, some quick examples should suffice
using bore to get a property: console.log(bore(window,"XMLHttpRequest.prototype.open"))
using bore to set a property: bore(window,"XMLHttpRequest.prototype.open",function(){return "foo"})
using bore to rip (destroy carelessly): bore(window,"XMLHttpRequest.prototype.open",null)
using bake to harden an existing property: bake(XMLHttpRequest.prototype,'open')
using bake to define a new (hard) property: bake(XMLHttpRequest.prototype,'bark',function(){return "woof!"})
intercepting functions and constructions
Now we can use all the above to our advantage as we devise a simple yet effective interceptor, by no means "perfect", but it should suffice; explanation follows:
const jack = function(k,v)
{
if(((typeof k)!='string')||!k.trim()){return}; // invalid reference
if(!!v&&((typeof v)!='function')){return}; // invalid callback func
if(!v){return this[k]}; // return existing definition, or undefined
if(k in this){this[k].list[(this[k].list.length)]=v; return}; //add
let h,n; h=k.split('.'); n=h.pop(); h=h.join('.'); // name & holder
this[k]={func:bore(MAIN,k),list:[v]}; // define new callback object
bore(MAIN,k,null); let f={[`_fake_${k}`]:function()
{
let r,j,a,z,q; j='_fake_'; r=stak(0,j); r=(r||'').split(' ')[0];
if(!r.startsWith(j)&&(r.indexOf(`.${j}`)<0)){fail(`:(`);return};
r=jack((r.split(j).pop())); a=([].slice.call(arguments));
for(let p in r.list)
{
if(!r.list.hasOwnProperty(p)||q){continue}; let i,x;
i=r.list[p].toString(); x=(new Function("y",`return {[y]:${i}}[y];`))(j);
q=x.apply(r,a); if(q==VOID){return}; if(!Array.isArray(q)){q=[q]};
z=r.func.apply(this,q);
};
return z;
}}[`_fake_${k}`];
bake(f,'name',`_fake_${k}`); bake((h?bore(MAIN,h):MAIN),n,f);
try{bore(MAIN,k).prototype=Object.create(this[k].func.prototype)}
catch(e){};
}.bind({});
jack() - explanation
it takes 2 arguments, the first as string (used to bore), the second is used as interceptor (function)
the first few comments explain a bit .. the "add" line simply adds another interceptor to the same reference
jack deposes an existing function, stows it away, then use "interceptor-functions" to replay arguments
the interceptors can either return undefined or a value, if no value is returned from any, the original function is not called
the first value returned by an interceptor is used as argument(s) to call the original and return is result to the caller/invoker
that fail(":(") is intentional; an error will be thrown if you don't have that function - only if the jack() failed.
Examples
Let's prevent eval from being used in the console -or address-bar
jack("eval",function(a){if(stak(0)){return a}; alert("having fun?")});
extensibility
If you want a DRY-er way to interface with jack, the following is tested and works well:
const hijack = function(l,f)
{
if(Array.isArray(l)){l.forEach((i)=>{jack(i,f)});return};
};
Now you can intercept in bulk, like this:
hijack(['eval','XMLHttpRequest.prototype.open'],function()
{if(stak(0)){return ([].slice.call(arguments))}; alert("gotcha!")});
A clever attacker may then use the Elements (dev-tool) to modify an attribute of some element, giving it some onclick event, then our interceptor won't catch that; however, we can use a mutation-observer and with that spy on "attribute changes". Upon attribute-change (or new-node) we can check if changes were made FUBU (or not) with our stak() check:
const watchDog=(new MutationObserver(function(l)
{
if(!stak(0)){alert("you again! :D");return};
}));
watchDog.observe(document.documentElement,{childList:true,subtree:true,attributes:true});
Conclusion
These were but a few ways of dealing with a bad problem; though I hope someone finds this useful, and please feel free to edit this answer, or post more (or alternative/better) ways of improving front-end security.

Connecting Javascript properties to Java object

The Problem
I am trying to feed a Java object into a script that would normally operate on the Document Object Model (DOM) of a web page. For the most part this functions as intended. However I have encountered a problem when dealing with attributes/properties of the DOM elements.
A particular property chain of interest is somediv.firstChild.href. What I can't figure out is how to get the firstChild property value dynamically. The simplest way I can think of at the moment is to use source.replaceAll("firstChild", "firstChild()"); to force the firstChild property to invoke the function firstChild() instead. However this will eventually open up a new can of worms.
The Question
How do I define an object that can be passed to a javascript function that can be operated on via the DOM?
Background
Learnings from C#
Before diving into Java I had learnt C#. In C# the concept of setters and getters is quite prevalent. If this interface method were available in Java my problem would be solved.
public string firstChild {
get { return this.getFirstChild(); }
set { this.setFirstChild(value); }
}
Current Implementation
The script is currently invoked by wrapping it in a function where I can pass in the window and document Java objects into the function's workspace.
document is a special top-level version of SpoofedDomElement (that extends it) but is functionally identical to the sample shown below. window is another object with minimal functions that handle event listeners.
Javascript (snippet) to operate on DOM
var somediv = document.createElement('div');
somediv.style.display = "block"
somediv.innerHTML="<a href='/mywork/server/test.html'>The Test Server Homepage</a>";
var linkvalue = somediv.firstChild.href;
This snippet is stored as the string theOriginalSource and used in the next section.
Java code to evaluate Javascript
String wrappedSource = "var scriptToInvoke = function(window, document){"
+ "\n" + theOriginalSource // from above
+ "\n};"
Object result = invocable.invokeFunction("scriptToInvoke", window, document);
This snippet wraps the javascript snippet so that I can pass in objects to use as window and document.
Java classes that spoof DOM elements
public class SpoofedDomElement {
public SpoofedDomElement firstChild;
public String id;
public String innerHtml;
public String href;
public SpoofedStyleProperties style = new SpoofedStyleProperties();
public String tagname;
...
}
public class SpoofedStyleProperties {
public String background = "transparent none repeat scroll 0% 0% auto padding-box border-box";
public String color = null;
public String display = "inline";
}
The above classes handle irrelevant parts of the code just fine (such as the assignment somediv.style.display = "block"). But it starts to fall apart when handling the values of firstChild or innerHtml when either value is changed.
Past Work
N.B. I include this section in all my questions to document what I have tried for future SO users who get here by Google. This might help someone reach a solution by aiding brainstorming.
Attempted Solutions
I have attempted to use a framework (HtmlUnit) to evaluate the Javascript. But I couldn't control which Javascript snippets were executed.
Potential Solutions
The following are questions that I am currently researching to find a solution. If I find anything I will report back.
Is there a way to emulate C# getter/setter behaviour in Java?
Can Javascript evaluate firstChild as a function?
Is there a way to create a wrapper within Javascript with getter/setters that can invoke my Java class's functions?
Is there an Apache Commons library for Nashorn (or similar) that isn't as heavy as the complete simulation frameworks (such as Selenium)?
It appears that you're trying to implement friendly access of script objects from Java code as well as trying to provide script-friendly API on top of java library/libraries.
For the first part [ script object access from Java ]
Apart from javax.script.Invocable interface, you can use JSObject. Nashorn exposes script objects as instances of jdk.nashorn.api.scripting.JSObject/.ScriptObjectMirror
https://docs.oracle.com/javase/8/docs/jdk/api/nashorn/jdk/nashorn/api/scripting/ScriptObjectMirror.html
For the second part [ friendlier access of Java objects from scripts ]
You can write script friendly wrappers in script itself using "JSAdapter".
Doc and Example:
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-JSAdapterconstructor
If you'd prefer to do in Java, you can implement your own jdk.nashorn.api.scripting.JSObject/.AbstractJSObject.
Doc and Example:
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-jsobject
Other nashorn specific script extensions may also be used to trap unknown property/method access in per object basis:
noSuchProperty hook in any script object:
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-noSuchProperty
noSuchMethod hook in any script object:
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-noSuchMethod
Object.bindProperties:
There script API extension can be used to bind properties of one object to another - the source object could be a Java object as well.
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-Object.bindProperties
With jdk9, there is more flexible inter-language linking possible with Dynalink API [ http://openjdk.java.net/jeps/276 ]
See also:
https://blogs.oracle.com/sundararajan/entry/dynamic_linker_api_for_the
https://blogs.oracle.com/sundararajan/entry/writing_pluggable_dynalink_linker_and
https://blogs.oracle.com/sundararajan/entry/nashorn_javascript_access_to_python
There are dynalink samples "samples/dynalink" directory of Nashorn OpenJDK repository:
http://hg.openjdk.java.net/jdk9/dev/nashorn/file/4a6ee1185fc8/samples/dynalink

Is there a good alternative to having JavaScript code as string literals in my ASP.net code?

Working on an ASP.net web application, I've been wondering if there is a good way to avoid writing JavaScript code in string literals in my ASP.net code. See here: https://web.archive.org/web/20211020150655/https://www.4guysfromrolla.com/articles/030202-1.aspx.
In the linked example, I see code that looks like:
Private Sub Calendar1_SelectionChanged(sender As Object, e As EventArgs)
Dim strjscript as string = "<script language=""javascript"">"
strjscript &= "window.opener." & _
Httpcontext.Current.Request.Querystring("formname") & ".value = '" & _
Calendar1.SelectedDate & "';window.close();"
strjscript = strjscript & "</script" & ">" 'Don't Ask, Tool Bug
Literal1.Text = strjscript 'Set the literal control's text to the JScript code
End Sub
I'm not used to using much JavaScript. A lot of the code that I've worked with has been mostly server-side coding with T-SQL. The above code gives me a headache just looking at it. Not only is it ugly, but it shows a pattern where a malicious user could try to inject malicious code.
Is there a better way to avoid manipulating JavaScript code as string literals? Think of the ways we have to avoid manipulating T-SQL code as string literals.
Ugh, dynamically building javascript and putting it inside a literal?
Generally the only time I embed javascript in code is when I am making a custom control and want it packaged neatly (no sepatate js file to worry about), and even then I use RegisterClientScriptBlock instead of a hack like this.
Why not just have a javascript function inside the page source (or an include file) that takes two parameters (form name and selected date) and then dynamically build the function call instead of the entire script?
A common way is to use the clientscriptmanager class:
http://msdn.microsoft.com/en-us/library/z9h4dk8y.aspx
You can call the registerstartupscript method, which will add the script to the end of your page, executes when the page finishes loading but before the page's OnLoad event is raised.
The RegisterClientScriptBlock method adds the script to the top of your page. This is where you might add commonly used fnctions.
Dim script As New StringBuilder()
script.AppendFormat("window.opener.{0}", Httpcontext.Current.Request.Querystring("formname"))
script.AppendFormat(".value = '{0}';window.close();", Calendar1.SelectedDate)
Dim cs As ClientScriptManager = Page.ClientScript
cs.RegisterClientScriptBlock(Me.GetType(), "ScriptKey", script.ToString(), true)
The last parameter tells the script manager to wrap the script in <script>...</script> tags so that you don't have to.
Also, if you are adding scripts from a user control, the "ScriptKey" makes sure that the same script does not get added more than once. If you need a separate script for each control, you can dynamically generate that parameter based on the control id.
The other common method for adding links to script files on your page is RegisterClientScriptInclude
Instead of writing out the complete function, embed the function on the page or in an external file and only dynamically write out the values. For example:
<script>
<asp:Literal ID="ScriptValues" runat="server" />
</script>
<script>
function foo(bar) { ... }
</script>
Then in your code behind or wherever (sorry, I don't do VB):
var values = new StringBuilder();
values.Append("var bar = " + bar + ";");
...
ScriptValues.Text = values.ToString();
for starters the StringBuilder is far better for this than using String (it's easier to read and more performance tuned)
Dim sbuild As StringBuilder = New StringBuilder
sbuild.Append("<script language=""javascript"">")
sbuild.Append("window.opener.")
sbuild.Append(Httpcontext.Current.Request.Querystring("formname"))
sbuild.Append(".value = ")
sbuild.Append(Calendar1.SelectedDate)
sbuild.Append("';window.close();")
sbuild.Append("</script>")
Literal1.Text = sbuild.ToString
But beyond that, I would suggest trying something like the TagBuilder Class. It says it's for MVC, but I don't see why you can't use it in a Web Forms scenario as well (you'd just have to import the MVC namespace) - (though I could be wrong on this part).
There are a few things to consider in dealing with your issue.
There are several methods, including Page.RegisterClientScript, that handle some of the dirty work, by properly wrapping your JavaScript code in the proper tags, as well as placing it within the proper place in the page (inline vs. beginning/end) that will deal with some of the formatting issues.
Your code sample above is VB.Net, which is little rough working with with large amounts of text due to the requirement of having to append the &_ to every line. C# does a better job at this. The good news is that with the release of .Net 4, you no longer have to worry about doing all the line concatenations.
If you are dealing with a large amount of text that you need to embed, you could consider keeping your JavaScript in a separate text file, and read the file into your literal, or script registration. You can even do some simple string replacements if you have to have some dynamic data. The StringBuilder class is also a help, with the use of the Append and AppenLine methods(), but again it depends on how much text you're dealing with and how often you'll be needing to work with the code block in question.
Move as much as possible into a .js file.
If anything, you should only need to render simple js function calls. I try to minimize the need for these, by adding a css class and then using jquery's class selector to attach the behavior.
From the example posted by rockinthesixstring, if you want to "clean" up the visual aspect of the code, you would also write it as:
Dim sbuild As StringBuilder = New StringBuilder
With sbuild
.Append("<script language=""javascript"">")
.Append("window.opener.")
.Append(Httpcontext.Current.Request.Querystring("formname"))
.Append(".value = ")
.Append(Calendar1.SelectedDate)
.Append("';window.close();")
.Append("</script>")
End With
Literal1.Text = sbuild.ToString
However I would look into the methods Page.ClientScript
Page.ClientScript.RegisterStartupScript
or if you are using a ScriptManager
ScriptManager.RegisterStartupScript
When working with JavaScript in ASP.NET, these are the paths you should follow:
Put it in a seperate JavaScript file. More maintanable.
However, if you (for whatever reason) can't put it in a JavaScript file, put it in a static class which exposes the script as constants with placeholders for value insertion (use the # symbol so you don't have to escape characters.
Like this:
public static class JavaScriptStuff
{
public const string SpecialScriptFormat = #"window.opener.{0}.value = '{1}';window.close();"
}
Then register it using ClientScriptManager - this way you also don't need to explicity open/close the script tag (stops human error).
http://msdn.microsoft.com/en-us/library/system.web.ui.scriptmanager.aspx
string myScript = string.Format(JavaScriptStuff.SpecialScriptFormat, HttpContext.Current.Request.QueryString("formname"), Calendar1.SelectedDate);
Page.ClientScript.RegisterStartupScript(this.GetType(), "myscript", myScript, true);
You can go even further, and not expose the scripts as public properties, instead expose "getter" methods which accept the params - which adds another layer of maintainability:
public static class JavaScriptStuff
{
private const string SpecialScriptFormat = #"window.opener.{0}.value = '{1}';window.close();"
public string GetSpecialScript(string queryString, string selectedDate)
{
return string.Format(SpecialScriptFormat, queryString, selectedDate);
}
}
}
Page.ClientScript.RegisterStartupScript(this.GetType(), "myscript", JavaScriptStuff.GetSpecialScript(HttpContext.Current.Request.QueryString("formname"), Calendar1.SelectedDate), true);
HTH

Is there a better way populate HTML widgets than building strings and setting innerHTML?

With XML XSLT, it is possible to build a nice template in HTML, convert it to XSLT and use xml data coming from the server to dynamically populate widgets on the client side.
JSON and JSONP are great for interacting with the server side and manipulating the data in JS. When it comes to rendering the JSON data, most examples I've seen use JS to concatenate an ugly string and set the innerHTML of some element to render it.
Is there an easy browser compatible way of creating html widgets and populating them with JSON that doesn't involve string banging or building loads of DOM elements?
As mentioned in other answers, what you're looking for is a javascript based templating language. There is a pretty good list going in this related question. Just to highlight a couple, mustache is clean, simple and ported to many many languages. I believe its being used by Twitter. Google Closure has template language that works in both JavaScript and Java. That's been battle tested by Google obviously.
Other major JS libraries each have templating as plugins or part of the library. I know jQuery has at least one plugin and is planning one on the roadmap for v1.5. Dojo has a clone of Django's templating language that looks pretty nice.
There are others, but I think that's going to be the cream of the crop.
I don't actually use any of these, because I use a home grown one, but I can tell you that they are very nice to work with and I highly recommend them over string concatenation or doing more work on the server.
You should see this blog post by John Resiq: JavaScript Micro-Templating
It has a simple micro-templating code you can re-use. It goes like this:
// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
(function(){
var cache = {};
this.tmpl = function tmpl(str, data){
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
var fn = !/\W/.test(str) ?
cache[str] = cache[str] ||
tmpl(document.getElementById(str).innerHTML) :
// Generate a reusable function that will serve as a template
// generator (and which will be cached).
new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};" +
// Introduce the data as local variables using with(){}
"with(obj){p.push('" +
// Convert the template into pure JavaScript
str
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');}return p.join('');");
// Provide some basic currying to the user
return data ? fn( data ) : fn;
};
})();
So you template would be in the markup:
<script type="text/html" id="item_tmpl">
<div id="<%=id%>" class="<%=(i % 2 == 1 ? " even" : "")%>">
<div class="grid_1 alpha right">
<img class="righted" src="<%=profile_image_url%>"/>
</div>
<div class="grid_6 omega contents">
<p><b><%=from_user%>:</b> <%=text%></p>
</div>
</div>
</script>
To use it:
var results = document.getElementById("results");
results.innerHTML = tmpl("item_tmpl", dataObject);
To be clear - the above example was taken from the blog post and is not my code. Follow the link for more info.
You could use jQuery and some sort of jQuery Plugin for Templates. For example http://ivorycity.com/blog/jquery-template-plugin/ (have a look at this page, there are some nice samples)

Is there a best practice for generating html with javascript

I'm calling a web service that returns an array of objects in JSON. I want to take those objects and populate a div with HTML. Let's say each object contains a url and a name.
If I wanted to generate the following HTML for each object:
<div><img src="the url" />the name</div>
Is there a best practice for this? I can see a few ways of doing it:
Concatenate strings
Create elements
Use a templating plugin
Generate the html on the server, then serve up via JSON.
Options #1 and #2 are going to be your most immediate straight forward options, however, for both options, you're going to feel the performance and maintenance impact by either building strings or creating DOM objects.
Templating isn't all that immature, and you're seeing it popup in most of the major Javascript frameworks.
Here's an example in JQuery Template Plugin that will save you the performance hit, and is really, really straightforward:
var t = $.template('<div><img src="${url}" />${name}</div>');
$(selector).append( t , {
url: jsonObj.url,
name: jsonObj.name
});
I say go the cool route (and better performing, more maintainable), and use templating.
If you absolutely have to concatenate strings, instead of the normal :
var s="";
for (var i=0; i < 200; ++i) {s += "testing"; }
use a temporary array:
var s=[];
for (var i=0; i < 200; ++i) { s.push("testing"); }
s = s.join("");
Using arrays is much faster, especially in IE. I did some testing with strings a while ago with IE7, Opera and FF. Opera took only 0.4s to perform the test, but IE7 hadn't finished after 20 MINUTES !!!! ( No, I am not kidding. ) With array IE was very fast.
Either of the first two options is both common and acceptable.
I'll give examples of each one in Prototype.
// assuming JSON looks like this:
// { 'src': 'foo/bar.jpg', 'name': 'Lorem ipsum' }
Approach #1:
var html = "<div><img src='#{src}' /> #{name}</div>".interpolate(json);
$('container').insert(html); // inserts at bottom
Approach #2:
var div = new Element('div');
div.insert( new Element('img', { src: json.src }) );
div.insert(" " + json.name);
$('container').insert(div); // inserts at bottom
Here's an example, using my Simple Templates plug-in for jQuery:
var tmpl = '<div class="#{classname}">#{content}</div>';
var vals = {
classname : 'my-class',
content : 'This is my content.'
};
var html = $.tmpl(tmpl, vals);
Perhaps a more modern approach is to use a templating language such as Mustache, which has implementations in many languages, including javascript. For example:
var view = {
url: "/hello",
name: function () {
return 'Jo' + 'hn';
}
};
var output = Mustache.render('<div><img src="{{url}}" />{{name}}</div>', view);
You even get an added benefit - you can reuse the same templates in other places, such as the server side.
If you need more complicated templates (if statements, loops, etc.), you can use Handlebars which has more features, and is compatible with Mustache.
You could add the template HTML to your page in a hidden div and then use cloneNode and your favorite library's querying facilities to populate it
/* CSS */
.template {display:none;}
<!--HTML-->
<div class="template">
<div class="container">
<h1></h1>
<img src="" alt="" />
</div>
</div>
/*Javascript (using Prototype)*/
var copy = $$(".template .container")[0].cloneNode(true);
myElement.appendChild(copy);
$(copy).select("h1").each(function(e) {/*do stuff to h1*/})
$(copy).select("img").each(function(e) {/*do stuff to img*/})
Disclosure: I am the maintainer of BOB.
There is a javascript library that makes this process a lot easier called BOB.
For your specific example:
<div><img src="the url" />the name</div>
This can be generated with BOB by the following code.
new BOB("div").insert("img",{"src":"the url"}).up().content("the name").toString()
//=> "<div><img src="the url" />the name</div>"
Or with the shorter syntax
new BOB("div").i("img",{"src":"the url"}).up().co("the name").s()
//=> "<div><img src="the url" />the name</div>"
This library is quite powerful and can be used to create very complex structures with data insertion (similar to d3), eg.:
data = [1,2,3,4,5,6,7]
new BOB("div").i("ul#count").do(data).i("li.number").co(BOB.d).up().up().a("a",{"href": "www.google.com"}).s()
//=> "<div><ul id="count"><li class="number">1</li><li class="number">2</li><li class="number">3</li><li class="number">4</li><li class="number">5</li><li class="number">6</li><li class="number">7</li></ul></div>"
BOB does currently not support injecting the data into the DOM. This is on the todolist. For now you can simply use the output together with normal JS, or jQuery, and put it wherever you want.
document.getElementById("parent").innerHTML = new BOB("div").insert("img",{"src":"the url"}).up().content("the name").s();
//Or jquery:
$("#parent").append(new BOB("div").insert("img",{"src":"the url"}).up().content("the name").s());
I made this library because I was not pleased with any of the alternatives like jquery and d3. The code very complicated and hard to read. Working with BOB is in my opinion, which is obviously biased, a lot more pleasant.
BOB is available on Bower, so you can get it by running bower install BOB.
Is there a best practice for this? I can see a few ways of doing it:
Concatenate strings
Create elements
Use a templating plugin
Generate the html on the server, then serve up via JSON.
1) This is an option. Build up the html with JavaScript on the client side and then inject it in the DOM as a whole.
Note that there is a paradigm behind this approach: the server outputs just data and (in case of interaction) receives data from the client asyncronoulsy with AJAX requests. The client side code operete as a stand-alone JavaScript web application.
The web application may operate, render the interface, even without the server being up (of course it won't display any data or offer any kind of interaction).
This paradigm is getting adopted often lately, and entire frameworks are build around this approach (see backbone.js for example).
2) For performance reasons, when possible, is better to build the html in a string and then inject it as a whole into the page.
3) This is another option, as well as adopting a Web Application framework. Other users have posted various templating engines available. I have the impression that you have the skills to evaluate them and decide whether to follow this path or not.
4) Another option. But serve it up as a plain text/html; why JSON? I don't like this approach because mixes PHP (your server language) with Html. But I adopt it often as a reasonable compromise between option 1 and 4.
My answer: you are already looking in the right direction.
I suggest to adopt an approach between 1 and 4 like I do. Otherwise adopt a web framework or templating engine.
Just my opinion based on my experience...

Categories

Resources