1

I've made a function that takes a select multiple input and generate checkboxes based on the options of this select. Then, I want that each checkbox execute some action when clicked. The function is:

const makeCheckboxDropdown = (input) => {
    if (input.type !== "select-multiple") {
        console.error("The input must be an `select multiple` type!")
        return
    }

    input.options[0].selected = false

    const header = document.getElementById('header')
    const outer = document.createElement('div')
    const options = input.options
    for (i = 0; i < options.length; i++) {
        let div = document.createElement('div')
        div.classList.add('checkbox')

        let label = document.createElement('label')

        let cb = document.createElement('input')
        cb.type = "checkbox"
        cb.value = options[i].value
        cb.addEventListener("change", (e) => console.log("clicked"))

        label.appendChild(cb)
        label.innerHTML += options[i].value

        div.appendChild(label)
        outer.appendChild(div)
    }
    header.parentNode.insertBefore(outer, header.nextSibiling)
}

Everything works: the checkbox are generated as intended. Except that, when I click the checkboxes, nothing happens. It should log "clicked" on console, but nothing happens. Strangely, if I add this same EventListener through browser debug console, it works.

What is happening here?

Edit: an repl.it example.

Mateus Felipe
  • 1,071
  • 2
  • 19
  • 43

1 Answers1

2

This is an issue with you using innerHTML on the label after you have already appended the checkbox. One alternative that works is a slight modification as below. What I've done is append another element (a span, you might also use document.createTextNode) with the value. This action is safe since I am not using innerHTML on the element that has the checkbox inside of it.

An explanation of what innerHTML is doing can be seen in this question here.

I believe you could also fix this by getting the checkbox again after you have modified the HTML with innerHTML (for example using children on your label DOM element) and applying the listener at that point, but I wouldn't recommend that method.

function ready(fn) {
  if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading"){
    fn();
  } else {
    document.addEventListener('DOMContentLoaded', fn);
  }
}

function theFunc(e) {
  console.log('clicked');
}

ready(() => {
  const makeCheckboxDropdown = (input) => {
    if (input.type !== "select-multiple") {
        console.error("The input must be an `select multiple` type!");
        return;
    }

    input.options[0].selected = false;
    
    const header = document.getElementById('header');
    const outer = document.createElement('div');
    const options = input.options;
    for (i = 0; i < options.length; i++) {
      let div = document.createElement('div');
      div.classList.add('checkbox');

      let label = document.createElement('label');

      let cb = document.createElement('input');
      cb.type = "checkbox";
      cb.value = options[i].value;
      cb.addEventListener('change', theFunc);

      label.appendChild(cb);
      
      let value = document.createElement('span');
      value.textContent = options[i].value;
      label.appendChild(value);

      div.appendChild(label);
      outer.appendChild(div);
    }
    header.parentNode.insertBefore(outer, header.nextSibiling);
  };

  const a = document.getElementById('a');
  makeCheckboxDropdown(a);
})
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>repl.it</title>
    <link href="index.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <script src="index.js"></script>
    <div class="content">
        <div class="clearfix"></div>
        <div class="box box-primary">
            <div class="box-body">
                <h4 id="header">Dias da Semana</h4>
                <select id="a" multiple>
                    <option>1:00 am</option>
                    <option>2:00 am</option>
                    <option>3:00 am</option>
                    <option>4:00 am</option>
                    <option>5:00 am</option>
                    <option>6:00 am</option>
                    <option>7:00 am</option>
                    <option>8:00 am</option>
                    <option>9:00 am</option>
                    <option>10:00 am</option>
                    <option>11:00 am</option>
                    <option>12:00 pm</option>
                    <option>13:00 pm</option>
                    <option>14:00 pm</option>
                    <option>15:00 pm</option>
                    <option>16:00 pm</option>
                    <option>17:00 pm</option>
                    <option>18:00 pm</option>
                    <option>19:00 pm</option>
                    <option>20:00 pm</option>
                    <option>21:00 pm</option>
                    <option>22:00 pm</option>
                    <option>23:00 pm</option>
                    <option>00:00 am</option>
                </select>
            </div>
        </div>
    </div>
  </body>
</html>
crapier
  • 373
  • 1
  • 6
  • Thank you! I solved it by changing `label.innerHTML += [...]` for `label.appendChild(document.createTextNode([...]))` as the link in your answer suggests. – Mateus Felipe Dec 14 '17 at 10:41