Best way to add modular html ? (example with Twitch API challenge)

What is the best practice to make the Javascript code to add some html content ?
For example with the Twitch API challenge I want to add a panel with different information for each channel.

  • I used a Javascript function to create and manipulate DOM content (using createElement, appenChild…) which took a lot of a bit messy code lines.
  • An other way would have been to write some html code within my javascript (something like var element = “< div >…< /div >”; but i hear it’s bad practice to use html code in javascript file and vice versa.
  • A third way would have been to write in the html an empty skull and then use JQuery to fill this skull for first element and for the next ones to do a clone of it and modify it.

Do you see other way to do that ? What would be the best way according to you and why ?

Generally, they way I would do it with jQuery is to create a function to create elements and call that. I’m not sure this is the best method, but it would go something like this:

function generateSingleTag(type, attrs, content) {
  'use strict';
  var elem = document.createElement(type);
  if (attrs) {
    for (var attr in attrs) {
      elem.setAttribute(attr, attrs[attr]);
    }
  }
  if (content) {
    elem.innerHTML = content;
  }
  return elem;
}

That could then be called like this:

var outerDiv = generateSingleTag('div', {'id':'outerDiv', 'class': 'container myContainer'});
var innerPara = generateSingleTag('p', {'id':'innerPara', 'class': 'text-center'}, 'Lorem Ipsum');
outerDiv.append(innerPara);

And this should produce:

<div id="outerDiv" class="container myContainer">
    <p id="innerPara" class="text-center">Lorem Ipsum</p>
</div>

The best way? A front-end framework like React, Angular 2, or Vue.

Since you are at the weather project, you are obviously not advanced enough for that yet, so I would use jQuery. Usually when I use jQuery, I create the html normally and then target it to change it instead of creating it in javascript. ex:

$('.myDivWithThisClass').val('I changed the value to: ' + (data.toString()));

You get the idea…

That is pretty confusing… I don’t think jQuery was really meant to create html, it is really much for updating the DOM.

It’s the only way I could think of doing it using just jQuery. I couldn’t think of any way of showing n sections of identical HTML where n is the number of records returned by a back-end API call.

For example, my portfolio project doesn’t have projects hard-coded in. It makes a REST call to a back-end which returns n projects. Each project is then shown on a panel. The only other way I could think of doing it was to write an excessive number of panels and hide the ones that aren’t needed. I couldn’t think of any way of writing a single template that could be used over and over again without using a HTML preprocessor or SPA framework.

Creating and modifying HTML with jQuery is extremely easy and is precisely the sort of thing the library was created for in the first place:

// ADD AN ELEMENT TO THE BODY
$( 'body' ).append('<div class="header" data-clickhandler="toggle"><h1>This is a header</h1><p>This is a paragraph</p></div>');

jQuery has very robust internal methods for creating DOM elements from your text strings - cleaning up unclosed tags, catching common mistakes (not that that means it’s OK to make them!).

You can also get fancier and use an object format (kind of like @JavaTheNutt was thinking). From the jQuery docs - create a div, dive it a class, some text, a click handler and append it to the body.

$( "<div/>", {
  "class": "test",
  text: "Click me!",
  click: function() {
    $( this ).toggleClass( "test" );
  }
})
  .appendTo( "body" );

Generally speaking, what I do, is keep a div in the page which is hidden, and load/insert template contents into that. Then I can select and clone elements from the templates for use in the page. As I move from section to section, if I need template resources which are not yet in the page, I can use an AJAX request to fetch them and append them to the template container so that they are available.

The best way? A front-end framework like React, Angular 2, or Vue.

For simply creating and inserting some HTML?

I would argue that a front-end framework is generally massive overkill for many projects. And things can be kept much simpler and more nimble without them.

But, I’ve yet to delve into them heavily - that’s simply what one of the front end framework architects had to say about them.

2 Likes

Great post, I guess I never used jQuery to its full potential! A front-end framework maybe not for small projects, and I agree with everything you said about overkill. My point was that the best way when working with html in javascript is a front-end framework, granted if you are only doing a little bit, jQuery is probably a better choice.

Angular would be an overkill, React too probably, Vue would be just fine.

I use this approach very regularly:

function buildItem( data, $ui ){
   // MODIFY UI CONTENTS ACCORDING TO DATA ...
   return $ui;
}

const $template_item = $( 'some html content you want to use many times' );
const $destination_for_templates = $( 'the container for your items' );
const array_of_items = [ ... ];

array_of_items.forEach( ( item ) => {
   $destination_for_templates.append( buildItem( item, $template.clone() ); 
});

// NOW INSERT $destination_for_templates somewhere in the page.
// I ALWAYS WORK IN DETACHED DOM FRAGMENTS TO KEEP DOM UPDATES TO A MINIMUM.

The HTML fragments can be simple or complex. Here’s a partial screen shot of a web application I write. Just JS and jQuery.

1 Like

Yes, of any of the frameworks, I have the most interest in Vue and that’s what I’ll be looking more closely into.

Vue is the simplest one, however it seems to be only popular in Asia, that’s a downside for the community.

Handlebars is a lightweight templating library that can make this easier for you. Then again, if all you were going to do is replace some variables in a template, you could write that yourself in a few lines of code.

Is it completely silly to mention Mustache.js?

Nope! Not at all.

Nor is it silly to mention es6 templating. There’s lots of ways, actually. :slight_smile:

1 Like

I just wrote a pen the other day that uses es6 template strings and pure functions to ‘mount’ generated markup to a given HTMLElement. Its a really clean way because all you do is map an array of objects (or any other structured data) into an array of html strings. It’s not as good as react, though (which only updates changed elements).

[...]
const ContactComp = contact =>
    `<div class="contact row">
        <div class="row">
            <div class="col-xs-12">
                ${ContactNameComp(contact.Name)}
            </div>
        </div>
        <div class="row">
            <div class="col-xs-6">${ContactEmailComp(contact.Email)}</div>
            <div class="col-xs-3">${ContactPhoneComp(contact.Phone)}</div>
            <div class="col-xs-3">${ContactCountryComp(contact.Country)}</div>
        </div>
    </div>`;

const sortContacts = contacts => contacts.sort((a, b) => String.localeCompare(a.Name, b.Name));

const ContactListComp = contacts => 
    `<h1>Contacts</h1>
    <div class="contact-list">
        ${sortContacts(contacts).map(ContactComp).join('\n')}
    </div>`;


document.addEventListener(`DOMContentLoaded`, () => {
    const entry = document.getElementById(`entry`);
    entry.innerHTML = ContactListComp(data);
});

I like to do it like this.

Don’t know about the best - but its probably the most readable and manageable way I have seen so far.

jQuery

$('.cards-panel').append([
    '<h1 class="section-title">Live Favorites</h1>',
    '<hr/>',
    '<div class="user-cards-container" id="userCards">',
    '</div>'
].join('\n'));

JS

var iframe = [ 
        '<iframe',
        '  src="' + url + '"',
        '  width="100%"',
        '  height="100%"',
        '  frameborder="0"',
        '  allowfullscreen>',
        '</iframe>'
    ].join('\n');
    myContainerDiv.insertAdjacentHTML('beforeend', iframe); //similar to innerHTML but appends rather than writes over