user6268172
user6268172

Reputation:

Clean way to write innerHTML with simple Javascript

I have a bunch of list items that need to have tags, classes, and all that. I'm getting the JSON data from the server and building the li items up using appendChild on a ul.

Here's an example function from for building the "lemma" items.

function createLemmaItem(lemma) {
  // bunch of "lemma" attributes -- create the base li
  let total = lemma.forms.length;
  let node = document.createElement("li");
  node.classList.add("lemma-item");
  node.dataset.lemmaId = lemma.id;
  node.dataset.group = lemma.group;
  node.setAttribute("onclick", "activateLemma(this)");

  // set the innerHTML to a long HTML string
  node.innerHTML = `
  <div class="lemma-col-1">
    <label>
      <input type="checkbox" onclick="lemmaToggleAll(this)" checked>
      ${lemma.name}
    </label>
  </div>
  <div class="lemma-col-2">
  ${lemma.latin}
  </div>
  <div class="lemma-col-3">
  ${lemma.homid}
  </div>
  <div class="lemma-col-4">
  <span class="counter">(<span class="total">${total}</span>/<span class="max">${total}</span>)</span>
  </div>`;
  return node;
}

Another example for a "form" item:

function createFormItem(lemma, form) {
  let node = document.createElement("li");
  node.classList.add("form-item");
  node.classList.add("hidden");
  node.dataset.lemmaId = lemma.id;
  node.dataset.group = lemma.group;
  node.innerHTML = `
  <label>
    <input type="checkbox" name="${lemma.name}@${lemma.latin}@${lemma.homid}@${lemma.group}@${form}" onchange="changeCount(this)"checked>
    ${form}
  </label>`
  return node;
}

Then I create the items by using this function to generate each one and then appending it to a ul with appendChild.

This seems pretty messy to me, and gets even messier when I have longer HTML strings. I'm concerned about the readability, maintainability, and fragility of my code when I do it this way.

How do you usually handle long HTML strings like this that are in the middle of JS files (without using a framework)? Is there a canonical way? Put the strings in a separate file or something?

Upvotes: 0

Views: 101

Answers (2)

user6268172
user6268172

Reputation:

Much cleaner and simpler with <template>.

Creating forms:

function createFormItem(lemma, form) {
  let template = document.querySelector("#form-item");
  let node = template.content.cloneNode(true);

  let name = document.createTextNode(form);
  node.querySelector("label").append(name);

  let li = node.querySelector("li");
  li.dataset.lemmaId = lemma.id;
  li.dataset.group = lemma.group;
  return node;
}

Creating lemmas:

function createLemmaItem(lemma) {
  let template = document.querySelector("#lemma-item");
  let node = template.content.cloneNode(true);

  let name = document.createTextNode(lemma.name);
  node.querySelector("label").append(name);

  let li = document.querySelector("li");
  li.dataset.lemmaId = lemma.id;
  li.dataset.group = lemma.group;

  let col2 = node.querySelector("div.lemma-col-2");
  let latin = document.createTextNode(lemma.latin);
  col2.append(latin);

  let col3 = node.querySelector("div.lemma-col-3");
  let homid = document.createTextNode(lemma.homid);
  col3.append(homid);

  let count = lemma.forms.length;
  let max = node.querySelector(".max");
  let total = node.querySelector(".total");
  max.innerHTML = count;
  total.innerHTML = count;

  return node;
}

And these can now be put in a separate file to make things even easier.

Upvotes: 0

CertainPerformance
CertainPerformance

Reputation: 371198

How do you usually handle long HTML strings like this that are in the middle of JS files (without using a framework)? Is there a canonical way? Put the strings in a separate file or something?

Putting distinctly different parts of a script into separate files makes maintainability a lot easier. It's much nicer to be able to, for example, navigate to lemmaHTML.js and change what you need to in it, than to have to control-F through a 500-line or 2500+ line monolithic .js file that contains your entire script.

One way to achieve this is to use ES6 modules. You can make a module that exports a function that creates the HTML string. For example, you could have a createLemmaItem.js file:

export function createLemmaItem(lemma) {
  // ...
}
// nothing else in this file

And do the same thing for createFormItem.js.

To import them, start from a module script, and simply do

import { createLemmaItem } from './createLemmaItem.js';

and use the createLemmaItem identifier as needed.

Another similar approach is to use a module bundler like Webpack to write everything in ES6 modules like above, but to bundle up all your separate files into a single one for production. This enables obsolete browsers like IE to use your code, and will significantly improve connection issues with large applications. (If you have hundreds of modules, making a separate request for each module might well take too long to load for clients.)

Though, if you're at this point where your script is large and you're worried about maintainability, I'd highly recommend looking into a framework like React - I know you said you didn't want to, but something like that that really is the tried-and-true professional approach nowadays, in comparison to vanilla JS DOM manipulation.

Upvotes: 0

Related Questions