0

I had to dynamically populate an HTML select element via the DOM and Javascript.

I did a little research and I got things working with no problem, but I had some further questions.

I found 2 stackoveflow posts and another non-stack blog post concerning how to populate Select Elements:

All 3 articles are anywhere from 8 - 10 years old. They seem to favor a certain technique discussed below due to older browsers NOT being able to use 'innerHTML' DOM property.

All 3 of these articles instruct to populate Select Elements via the DOM using: document.createElement("option").

A number of people have tried to use the 'Element.innerHTML' DOM property to populate Select Elements with a STRING of options rather than using document.createElement("option").

Apparently, these people are then instructed to use document.createElement("option") rather than 'innerHTML' beacause 'innerHTML' has some known issues in older browsers such as Internet Explorer.

Normally, I would not look into this any further and just go with document.createElement("option") but I have 2 concerns:

  1. I am populating 2 Select Elements with 190 Options each (380 total) and each option represents an ISO Language Code.

    So through each iteration of Array.prototype.forEach() the Javscript application is creating an object for each and every ISO language code. That's 380 objects total in memory.

    Isn't document.createElement("option") inefficient when compared to just building a very large String of options and inserting via 'innerHTML?'

  2. I have tested the 'innerHTML' way in the latest Chrome and Firefox browsers and it works fine.

    I have no intention of supporting older browsers such as Internet Explorer so is 'innerHTML' a viable option in my case?

I would really like to use 'innerHTML' as the code is so much more readable (and saves memory?):

Array.prototype.map().toString gives me the full String of options.

Any thoughts on this matter would be greatly appreciated.

Nickolay
  • 31,095
  • 13
  • 107
  • 185
  • 1
    190 options is sort of a lot. Makes me wonder if there's going to be usability issues. – Tibrogargan Aug 21 '19 at 02:34
  • I have a notebook with pretty beefy specs and it runs very smoothly both ways: document.createElement("option") AND innerHTML Both work with no noticeable differences in performace client-side. – ponytails_soda Aug 21 '19 at 03:37
  • That was sort of my point. While there might be a difference in performance between the two, most people aren't going to notice it. Since you're not going to support platforms that require one anyway, it's a moot point. You're better of spending time on things that may actually get noticed, like having to scroll through 190 options, rather than premature optimization like this. – Tibrogargan Aug 21 '19 at 03:47
  • What would you suggest makes for a better interface design other than a select element? Even on the mymemory translation api demonstration page, they use a select element to choose a language. Also, this is the only element really to use to select a language so there is nothing "premature" about the optimization unless you can suggest a better interface alternative. – ponytails_soda Aug 21 '19 at 09:26
  • So you agree that creating an object for each language option is a detriment to performance although it might not be noticeable to the user? – ponytails_soda Aug 21 '19 at 09:33
  • 1
    If a change in performance has no effect on the productivity of the user, it's irrelevant if it's better or worse. Trying to fix something irrelevant is premature optimization. Your time is better spent on other things. – Tibrogargan Aug 22 '19 at 02:20
  • 1
    The correct answer here is to not use a select with 190 options. Make it an autocomplete or a similar field, otherwise it's a usability nightmare. – Etheryte Aug 24 '19 at 01:10

1 Answers1

0

As @Tibrogargan rightly notes, before trying to optimize you should test on the slowest device you want to support. On a laptop 10,000 options are processed in 15 ms, and if you care about very resource constrained devices, you arguably ought to serve this as part of the HTML page instead of running JS...

Comparing building up the DOM vs creating a string to set innerHTML, I think the DOM way is not any worse and possibly better than innerHTML in:

  • The amount objects created: document.createElement("option") (or new Option()) creates DOM nodes, which are also created when you set innerHTML! You might argue this also creates unnecessary JavaScript wrappers/representations of the DOM objects, but with innerHTML you're creating lots of temporary strings, and it's not clear which is worse.
  • Readability: using new Option(label, value) is arguably more clear than concatenating the HTML string ("<option value='" + value + "'>" + ...), though if you use template literals I'd say the two methods are on par with each other.
  • Benchmarking is notoriously hard to do correctly, but since people like to post jsperf links, here's one showing new Option() being faster in Firefox (and having the same performance in Chrome). I won't claim it's correct (have my doubts about the GC, in particular), but neither are the other benchmarks claiming that innerHTML is faster.

But with a real testcase I dare you find a device that shows any measurable difference:

const labels = [...Array(1000).keys()].map((i) => Math.sin(i+1).toString(36).substring(2)); // random looking strings
const div = document.getElementById("container");


function timeIt(fn) {
    let start = performance.now();

    fn();

    let elapsedMS = performance.now() - start
    div.innerHTML = "";
    return elapsedMS;
}

function createUsingDOM() {
    let select = document.createElement("select");
    for (let i = 0, len = labels.length; i < len; i++) {
        select.appendChild(new Option(labels[i], i));
    }
    div.appendChild(select);
}

function createUsingInnerHTML() {
    let options = [];
    for (let i = 0, len = labels.length; i < len; i++) {
       options.push("<option value='" + i + "'>" + labels[i] + "</option>");
    }
    div.innerHTML = "<select>" + options.join("") + "</select>";
}

//createUsingDOM()
// createUsingInnerHTML();
alert(`DOM: ${timeIt(createUsingDOM)}ms, innerHTML: ${timeIt(createUsingInnerHTML)}ms`)
<div id="container"></div>
Nickolay
  • 31,095
  • 13
  • 107
  • 185