29

I'm building a small app with a few modal dialog windows. The windows require a tiny bit of HTML. I've hard coded the window HTML in the javascript library but am not thrilled with this solution. Is there a more elegant way to do this? It seems that JavaScript doesn't have multi line strings/heredoc syntax.

var html = "<div id='email_window'><h2>Email Share</h2><div>";
html = html + "<form action='javascript:emailDone();' method='post'>";
html = html + "<div><label for='to'>To</label><input id='to' type='text'></div>";
html = html + "<div><label for='from'>From</label><input id='from' type='text' value='" + email + "'></div>";
html = html + "<div><label for='subject'>Subject</label><input id='subject' type='text' disabled='disabled' value='" + subject + "'></div>";
html = html + "<div><label for='body'>Body</label><input id='body' type='text' disabled='disabled' value='" + body + "'></div>";
html = html + "<div><input type='submit' value='Send'><input type='button' value='Cancel' onClick='javascript:$.fancybox.close();'></div>";
html = html + "</form></div>";

$("#data").html(html);

Added to clarify the original message-

Any solution can't use Ajax/XHR to pull in the template file because the javascript library will be on a different domain that the html file it's included in It's a little like ShareThis. The library will be included on a number of different sites and attached to the onClick event of any anchor tag inside divs with attribute sharetool="true".

For example:

http://www.bar.com - index.html
<html>
...
<script type="text/javascript" src="http://www.foo.com/sharetool.js"></script>
...
<body>
<div sharetool="true">
</div>
...
</html>
Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390
Andrew Hopper
  • 956
  • 2
  • 9
  • 20

7 Answers7

21

You can include the HTML as regular markup at the end of the page, inside an invisible div. Then you're able to reference it with jQuery.

You then need to programmatically set your variable fields (email, subject, body)

<div id='container' style='display: none;'>
  <div id='your-dialog-box-contents'>
    ...
    ...
  </div>
</div>

<script type='text/javascript'>
  $("#from").val(from);
  $("#subject").val(subject);
  $("#body").val(body);
  $("#data").html($("#your-dialog-box-contents"));
</script>
Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390
  • +1 this is very clever and I have used it with success in the past. – jackJoe May 31 '11 at 19:12
  • 3
    Thanks for the idea. It's not quite what I need for this project. The JS library I'm writing is meant to be used across lots of different websites so it's not practical to add a hidden div to all the pages that include the library. – Andrew Hopper May 31 '11 at 20:36
16

Templates. Pick your poison

Either inline them as script blocks or load them using ajax as external resources.

I personally use EJS as external template files and just get EJS to load them and inject them into a container with json data bound to the template.

new EJS({ 
    url: "url/to/view"
}).update('html_container_name', {
    "foobar": "Suprise"
});

And then view files use generic view logic.

// url/to/view
<p> <%=foobar %></p>
chb
  • 1,727
  • 7
  • 25
  • 47
Raynos
  • 166,823
  • 56
  • 351
  • 396
  • I'd like to say I'm pretty happy with EJS at my workplace. Having a separate file makes for a better MVC set-up. – Darien May 31 '11 at 19:56
  • @Darien and file re-use is neat if your using SSJS. – Raynos May 31 '11 at 20:39
  • This looks really promising. I failed to mention in the original post that the JavaScript file and html file it's included in are on different domains. Looking at the EJS source it looks like a XMLHttpRequest is used to fetch the template. This won't work across domains will it? – Andrew Hopper May 31 '11 at 21:29
  • @andrew nope. But you can fire off a get request to the templates and pass them into EJS manually. Means you need to write a wrapper around EJS – Raynos May 31 '11 at 22:00
  • Got it. Maybe I'll contribute the remote template wrapper to the project. – Andrew Hopper Jun 01 '11 at 13:07
  • [This article](http://engineering.linkedin.com/frontend/client-side-templating-throwdown-mustache-handlebars-dustjs-and-more), written by a developer for LinkedIn, is fairly recent and comprehensive in its comparison of client-side javascript templating technologies. – chb Jul 11 '12 at 04:54
8

For multiline strings (no frameworks, just javascript) there are several solutions. See my answer to this SO Question. You could combine that with some simple templating:

String.prototype.template = String.prototype.template ||
        function (){
            var  args = Array.prototype.slice.call(arguments)
                ,str = this
                ,i=0
                ;
            function replacer(a){
                var aa = parseInt(a.substr(1),10)-1;
                return args[aa];
            }
            return  str.replace(/(\$\d+)/gm,replacer)
};
//basic usage:
'some #1'.template('string'); //=> some string
//your 'html' could look like:
var html =
  [ '<form action="javascript:emailDone();" method="post">',
    ' <div><label for="to">To</label>',
    '       <input id="to" type="text"></div>',
    ' <div><label for="from">From</label>',
    '       <input id="from" type="text" ',
    '         value="$0"></div>',
    ' <div><label for="subject">Subject</label>',
    '      <input id="subject" type="text" disabled="disabled" ',
    '        value="$1"></div>',
    ' <div><label for="body">Body</label>',
    '      <input id="body" type="text" disabled="disabled" ',
    '        value="$2"></div>',
    ' <div><input type="submit" value="Send"><input type="button" ',
    '        value="Cancel" ',
    '        onClick="javascript:$.fancybox.close();"></div>',
    '</form>'
  ] .join('').template(email, subject, body);
Community
  • 1
  • 1
KooiInc
  • 119,216
  • 31
  • 141
  • 177
5

There is 2 solutions tto your problem: - An alternative to the heredoc Syntax in javascript is to escape the newline char with \ :

var tpl = "hello\
stackoverflow\
World !";

The char is escaped so ignored, and it wont take place in the resulting string.

You can also create a plain html file with your template, and in your js script you create a hidden iframe and load the crossdomain html template. You can now access the document object of the iframe and retreive body.innerHTML. In theory! I Didn't tested this solution yet....

Sirko
  • 72,589
  • 19
  • 149
  • 183
Dev in Gfx
  • 51
  • 1
  • 1
5

Personally I like building DOM trees like this:

$('#data').html(
    $('<div/>', {
        id: 'email_window',
        html: $('<h2/>', {
            html: 'Email Share'
        })
    }).after(
        $('<form/>', {
            action: 'javascript:emailDone();',
            method: 'post',
            html: $('<div/>', {
                html: $('<label/>', {
                    for: 'to',
                    html: 'To'
                }).after($('<input/>', {
                    id: 'to',
                    type: 'text'
                }))
            }).after(
                ... etc
            )
        })
    )
);
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Self closing html labels with content inside them :( I'm sure self closed tags do not hold content. – Raynos May 31 '11 at 19:16
  • 2
    Indeed better from a jQuery perspective, yet very poor from a readability perspective. (meta to data ratio stinks). – Brad Christie May 31 '11 at 19:17
  • 1
    @Raynos: The `/` is ignored per the jQuery regex testing for tags in the selector, anyways. – Brad Christie May 31 '11 at 19:17
  • 1
    @Raynos, no, there's a html property applied to them. jQuery will use the value of this property to modify the self-closing tag into a not self-closing tag :-) – Darin Dimitrov May 31 '11 at 19:17
  • @BradChristie I know that jQuery ignores it and handles it well. @DarinDimitrov just not very semantic. Feels wrong to use `
    ` but then put content in it.
    – Raynos May 31 '11 at 19:19
  • @Raynos, I don't see anything wrong in it. jQuery builds and inserts correct elements into the DOM tree. – Darin Dimitrov May 31 '11 at 19:20
  • I prefer to longer `
    ` is all. It indicates that I want this div to have content. Where as if I use `` I dont want it to have content
    – Raynos May 31 '11 at 19:22
  • @Raynos: (Wanted to include ref.) [see this](https://github.com/jquery/jquery/blob/master/src/core.js#L33). `
    `=`
    `=`
    `, but I believe the correct answer is "whichever is most readable".
    – Brad Christie May 31 '11 at 19:24
  • This is an interesting approach. In our situation a front end designer will provide HTML. I want to be able to copy this directly into the JS library without having to translate to JavaScript code. – Andrew Hopper May 31 '11 at 20:38
1

You're right, JS doesn't have heredocs or multi-line strings. That said, the usual approach to this is to have the HTML in...the HTML, and show or hide it as appropriate. You're already using jQuery, so you're most of the way there:

<div style="display:none;">
    <form method='post' class="email">
         <input id='from' type='text'> <!-- other form fields omitted for brevity -->
    </form>
    <form method='post' class="facebook"></form> <!-- again, omitted for brevity -->
</div>

Then, you can populate the form and toss it in the right spot:

$('#data').html($('form.email').find('input#from').val(email).end().html());
Dan Davies Brackett
  • 9,811
  • 2
  • 32
  • 54
  • Thanks for the idea. It's not quite what I need for this project. The JS library I'm writing is meant to be used across lots of different websites so it's not practical to add a hidden div to all the pages that include the library. – Andrew Hopper May 31 '11 at 20:38
0

Cook.js

div([
  button({click:[firstEvent, secondEvent]},
         'You can bind (attach) events on the fly.'),
  p('Here are some popular search engines'),
  ul([
    li([
      a('Google', {href:'http://www.google.com'})
    ]),
    li([
      a('Bing', {href:'http://www.bing.com'})
    ]),
    li([
      a('Yahoo', {href:'http://www.yahoo.com'})
    ])
  ])
]);

how it works

Objects = Attribute & Events
    -> {href:'facebook.com', src:'static/cat.gif', ng-bind:'blah'}
String  = Text or Html
    -> 'hello world'
Array   = Elements
    -> [a('hello world', {href:'facebook.com'}), img({src:'static/cat.gif'})] 

more on cook.js!

Raj Nathani
  • 2,805
  • 1
  • 20
  • 19